Controller implements IFloodlightProviderService, 
            IStorageSourceListener { @Override public void addHAListener(IHAListener listener) { this.haListeners.add(listener); }  void setStorageSourceService(IStorageSourceService storageSource); void setCounterStore(ICounterStoreService counterStore); void setPktInProcessingService(IPktInProcessingTimeService pits); void setRestApiService(IRestApiService restApi); void setThreadPoolService(IThreadPoolService tp); @Override Role getRole(); @Override void setRole(Role role); @Override synchronized void addOFMessageListener(OFType type, 
                                                  IOFMessageListener listener); @Override synchronized void removeOFMessageListener(OFType type,
                                                     IOFMessageListener listener); void removeOFMessageListeners(OFType type); @Override Map<Long, IOFSwitch> getSwitches(); @Override void addOFSwitchListener(IOFSwitchListener listener); @Override void removeOFSwitchListener(IOFSwitchListener listener); @Override Map<OFType, List<IOFMessageListener>> getListeners(); @Override @LogMessageDocs({ @LogMessageDoc(message="Failed to inject OFMessage {message} onto " + "a null switch", explanation="Failed to process a message because the switch " + " is no longer connected."), @LogMessageDoc(level="ERROR", message="Error reinjecting OFMessage on switch {switch}", explanation="An I/O error occured while attempting to " + "process an OpenFlow message", recommendation=LogMessageDoc.CHECK_SWITCH) }) boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
                                   FloodlightContext bc); @Override @LogMessageDoc(message="Calling System.exit", explanation="The controller is terminating") synchronized void terminate(); @Override boolean injectOfMessage(IOFSwitch sw, OFMessage msg); @Override void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
                                      FloodlightContext bc); @Override BasicFactory getOFMessageFactory(); @Override String getControllerId(); @LogMessageDocs({ @LogMessageDoc(message="Listening for switch connections on {address}", explanation="The controller is ready and listening for new" + " switch connections"), @LogMessageDoc(message="Storage exception in controller " + "updates loop; terminating process", explanation=ERROR_DATABASE, recommendation=LogMessageDoc.CHECK_CONTROLLER), @LogMessageDoc(level="ERROR", message="Exception in controller updates loop", explanation="Failed to dispatch controller event", recommendation=LogMessageDoc.GENERIC_ACTION) }) void run(); void setConfigParams(Map<String, String> configParams); void init(Map<String, String> configParams); @LogMessageDoc(message="Waiting for storage source", explanation="The system database is not yet ready", recommendation="If this message persists, this indicates " + "that the system database has failed to start. " + LogMessageDoc.CHECK_CONTROLLER) void startupComponents(); @Override void addInfoProvider(String type, IInfoProvider provider); @Override void removeInfoProvider(String type, IInfoProvider provider); Map<String, Object> getControllerInfo(String type); @Override void addHAListener(IHAListener listener); @Override void removeHAListener(IHAListener listener); @Override Map<String, String> getControllerNodeIPs(); @Override void rowsModified(String tableName, Set<Object> rowKeys); @Override void rowsDeleted(String tableName, Set<Object> rowKeys); @Override long getSystemStartTime(); @Override void setAlwaysClearFlowsOnSwAdd(boolean value); boolean getAlwaysClearFlowsOnSwAdd();  }
@Test public void testControllerNodeIPChanges() throws Exception { class DummyHAListener implements IHAListener { public Map<String, String> curControllerNodeIPs; public Map<String, String> addedControllerNodeIPs; public Map<String, String> removedControllerNodeIPs; public int nCalled; public DummyHAListener() { this.nCalled = 0; } @Override public void roleChanged(Role oldRole, Role newRole) { } @Override public synchronized void controllerNodeIPsChanged( Map<String, String> curControllerNodeIPs, Map<String, String> addedControllerNodeIPs, Map<String, String> removedControllerNodeIPs) { this.curControllerNodeIPs = curControllerNodeIPs; this.addedControllerNodeIPs = addedControllerNodeIPs; this.removedControllerNodeIPs = removedControllerNodeIPs; this.nCalled++; notifyAll(); } public void do_assert(int nCalled, Map<String, String> curControllerNodeIPs, Map<String, String> addedControllerNodeIPs, Map<String, String> removedControllerNodeIPs) { assertEquals("nCalled is not as expected", nCalled, this.nCalled); assertEquals("curControllerNodeIPs is not as expected", curControllerNodeIPs, this.curControllerNodeIPs); assertEquals("addedControllerNodeIPs is not as expected", addedControllerNodeIPs, this.addedControllerNodeIPs); assertEquals("removedControllerNodeIPs is not as expected", removedControllerNodeIPs, this.removedControllerNodeIPs); } } long waitTimeout = 250; DummyHAListener listener = new DummyHAListener(); HashMap<String,String> expectedCurMap = new HashMap<String, String>(); HashMap<String,String> expectedAddedMap = new HashMap<String, String>(); HashMap<String,String> expectedRemovedMap = new HashMap<String, String>(); controller.addHAListener(listener); ControllerRunThread t = new ControllerRunThread(); t.start(); synchronized(listener) { controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1")); expectedCurMap.clear(); expectedAddedMap.clear(); expectedRemovedMap.clear(); expectedCurMap.put("c1", "1.1.1.1"); expectedAddedMap.put("c1", "1.1.1.1"); listener.wait(waitTimeout); listener.do_assert(1, expectedCurMap, expectedAddedMap, expectedRemovedMap); controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2")); listener.wait(waitTimeout); assertTrue("controllerNodeIPsChanged() should not have been called here", listener.nCalled == 1); controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2")); expectedCurMap.clear(); expectedAddedMap.clear(); expectedRemovedMap.clear(); expectedCurMap.put("c1", "1.1.1.1"); expectedCurMap.put("c2", "2.2.2.2"); expectedAddedMap.put("c2", "2.2.2.2"); listener.wait(waitTimeout); listener.do_assert(2, expectedCurMap, expectedAddedMap, expectedRemovedMap); controller.storageSource.updateRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3", getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.3")); expectedCurMap.clear(); expectedAddedMap.clear(); expectedRemovedMap.clear(); expectedCurMap.put("c1", "1.1.1.1"); expectedCurMap.put("c2", "2.2.2.3"); expectedAddedMap.put("c2", "2.2.2.3"); expectedRemovedMap.put("c2", "2.2.2.2"); listener.wait(waitTimeout); listener.do_assert(3, expectedCurMap, expectedAddedMap, expectedRemovedMap); controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3"); expectedCurMap.clear(); expectedAddedMap.clear(); expectedRemovedMap.clear(); expectedCurMap.put("c1", "1.1.1.1"); expectedRemovedMap.put("c2", "2.2.2.3"); listener.wait(waitTimeout); listener.do_assert(4, expectedCurMap, expectedAddedMap, expectedRemovedMap); } }