diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java index 5616b2793787cf2893852e2345db2c0d790a8969..1bc258b564e0bc49084e29bfac8608d1baefbe29 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchListener.java @@ -37,6 +37,12 @@ public interface IOFSwitchListener { */ public void removedSwitch(IOFSwitch sw); + /** + * Fired when ports on a switch change (any change to the collection + * of OFPhysicalPorts and/or to a particular port) + */ + public void switchPortChanged(Long switchId); + /** * The name assigned to this listener * @return diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 628ee15b969f84be23b58e463afd9e55c1906f25..9eacefcc422f8847b81b9e88ad743c45d2acdcc8 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -238,27 +238,39 @@ public class Controller implements IFloodlightProviderService, */ public void dispatch(); } + public enum SwitchUpdateType { + ADDED, + REMOVED, + PORTCHANGED + } /** * Update message indicating a switch was added or removed */ protected class SwitchUpdate implements IUpdate { public IOFSwitch sw; - public boolean added; - public SwitchUpdate(IOFSwitch sw, boolean added) { + public SwitchUpdateType switchUpdateType; + public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) { this.sw = sw; - this.added = added; + this.switchUpdateType = switchUpdateType; } public void dispatch() { if (log.isTraceEnabled()) { log.trace("Dispatching switch update {} {}", - sw, added); + sw, switchUpdateType); } if (switchListeners != null) { for (IOFSwitchListener listener : switchListeners) { - if (added) - listener.addedSwitch(sw); - else - listener.removedSwitch(sw); + switch(switchUpdateType) { + case ADDED: + listener.addedSwitch(sw); + break; + case REMOVED: + listener.removedSwitch(sw); + break; + case PORTCHANGED: + listener.switchPortChanged(sw.getId()); + break; + } } } } @@ -1058,6 +1070,12 @@ public class Controller implements IFloodlightProviderService, removePortInfo(sw, portNumber); log.debug("Port #{} deleted for {}", portNumber, sw); } + SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED); + try { + this.updates.put(update); + } catch (InterruptedException e) { + log.error("Failure adding update to queue", e); + } } /** @@ -1286,7 +1304,7 @@ public class Controller implements IFloodlightProviderService, } updateActiveSwitchInfo(sw); - SwitchUpdate update = new SwitchUpdate(sw, true); + SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED); try { this.updates.put(update); } catch (InterruptedException e) { @@ -1325,7 +1343,7 @@ public class Controller implements IFloodlightProviderService, // of the switch state that's written to storage. updateInactiveSwitchInfo(sw); - SwitchUpdate update = new SwitchUpdate(sw, false); + SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED); try { this.updates.put(update); } catch (InterruptedException e) { @@ -1686,10 +1704,12 @@ public class Controller implements IFloodlightProviderService, // ignore } } - - //for (Short portNum : oldports.keySet()) { - // sw.deletePort(portNum); - //} + SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED); + try { + this.updates.put(update); + } catch (InterruptedException e) { + log.error("Failure adding update to queue", e); + } } protected void removePortInfo(IOFSwitch sw, short portNumber) { diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 47b830a7a8b6e6716b6eed607deceb49f8f42ea9..17ca25979d5f419cb79dcbf08bba5a4ef0e979eb 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -1107,6 +1107,16 @@ IFloodlightModule, IInfoProvider, IHAListener { lock.writeLock().unlock(); } } + + /** + * We don't react the port changed notifications here. we listen for + * OFPortStatus messages directly. Might consider using this notifier + * instead + */ + @Override + public void switchPortChanged(Long switchId) { + // no-op + } /** * Delete links incident on a given switch port. diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index 524757f181bf79333b91029c3d404cc0b17c6f23..e11d2d65b70036c1af0d7536c7b94627c7479f7d 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -320,6 +320,11 @@ public class StaticFlowEntryPusher // do NOT delete from our internal state; we're tracking the rules, // not the switches } + + @Override + public void switchPortChanged(Long switchId) { + // no-op + } /** * This handles both rowInsert() and rowUpdate() diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index 6e897cafd2b5f072157b10a1f44be1a549a02108..da47f272bf2236862c84e6a2b51df11c98b2001f 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -41,6 +41,9 @@ import net.floodlightcontroller.core.IListener.Command; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IOFSwitchListener; import net.floodlightcontroller.core.OFMessageFilterManager; +import net.floodlightcontroller.core.internal.Controller.IUpdate; +import net.floodlightcontroller.core.internal.Controller.SwitchUpdate; +import net.floodlightcontroller.core.internal.Controller.SwitchUpdateType; import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.test.MockFloodlightProvider; @@ -72,9 +75,11 @@ import org.openflow.protocol.OFPacketIn; import org.openflow.protocol.OFPacketOut; import org.openflow.protocol.OFPhysicalPort; import org.openflow.protocol.OFPort; +import org.openflow.protocol.OFPortStatus; import org.openflow.protocol.OFStatisticsReply; import org.openflow.protocol.OFType; import org.openflow.protocol.OFPacketIn.OFPacketInReason; +import org.openflow.protocol.OFPortStatus.OFPortReason; import org.openflow.protocol.OFVendor; import org.openflow.protocol.action.OFAction; import org.openflow.protocol.action.OFActionOutput; @@ -518,9 +523,11 @@ public class ControllerTest extends FloodlightTestCase { class DummySwitchListener implements IOFSwitchListener { public int nAdded; public int nRemoved; + public int nPortChanged; public DummySwitchListener() { nAdded = 0; nRemoved = 0; + nPortChanged = 0; } public synchronized void addedSwitch(IOFSwitch sw) { nAdded++; @@ -533,6 +540,11 @@ public class ControllerTest extends FloodlightTestCase { public String getName() { return "dummy"; } + @Override + public void switchPortChanged(Long switchId) { + nPortChanged++; + notifyAll(); + } } DummySwitchListener switchListener = new DummySwitchListener(); IOFSwitch sw = createMock(IOFSwitch.class); @@ -541,14 +553,21 @@ public class ControllerTest extends FloodlightTestCase { controller.addOFSwitchListener(switchListener); synchronized(switchListener) { - controller.updates.put(controller.new SwitchUpdate(sw, true)); + controller.updates.put(controller.new SwitchUpdate(sw, + Controller.SwitchUpdateType.ADDED)); switchListener.wait(500); assertTrue("IOFSwitchListener.addedSwitch() was not called", switchListener.nAdded == 1); - controller.updates.put(controller.new SwitchUpdate(sw, false)); + controller.updates.put(controller.new SwitchUpdate(sw, + Controller.SwitchUpdateType.REMOVED)); switchListener.wait(500); assertTrue("IOFSwitchListener.removedSwitch() was not called", switchListener.nRemoved == 1); + controller.updates.put(controller.new SwitchUpdate(sw, + Controller.SwitchUpdateType.PORTCHANGED)); + switchListener.wait(500); + assertTrue("IOFSwitchListener.switchPortChanged() was not called", + switchListener.nPortChanged == 1); } } @@ -1176,4 +1195,55 @@ public class ControllerTest extends FloodlightTestCase { assertTrue(exceptionThrown); verify(sw); } + + public void verifyPortChangedUpdateInQueue(IOFSwitch sw) throws Exception { + assertEquals(1, controller.updates.size()); + IUpdate update = controller.updates.take(); + assertEquals(true, update instanceof SwitchUpdate); + SwitchUpdate swUpdate = (SwitchUpdate)update; + assertEquals(sw, swUpdate.sw); + assertEquals(SwitchUpdateType.PORTCHANGED, swUpdate.switchUpdateType); + } + + /* + * Test handlePortStatus() + * TODO: test correct updateStorage behavior! + */ + @Test + public void testHandlePortStatus() throws Exception { + IOFSwitch sw = createMock(IOFSwitch.class); + OFPhysicalPort port = new OFPhysicalPort(); + port.setName("myPortName1"); + port.setPortNumber((short)42); + + OFPortStatus ofps = new OFPortStatus(); + ofps.setDesc(port); + + ofps.setReason((byte)OFPortReason.OFPPR_ADD.ordinal()); + sw.setPort(port); + expectLastCall().once(); + replay(sw); + controller.handlePortStatusMessage(sw, ofps, false); + verify(sw); + verifyPortChangedUpdateInQueue(sw); + reset(sw); + + ofps.setReason((byte)OFPortReason.OFPPR_MODIFY.ordinal()); + sw.setPort(port); + expectLastCall().once(); + replay(sw); + controller.handlePortStatusMessage(sw, ofps, false); + verify(sw); + verifyPortChangedUpdateInQueue(sw); + reset(sw); + + ofps.setReason((byte)OFPortReason.OFPPR_DELETE.ordinal()); + sw.deletePort(port.getPortNumber()); + expectLastCall().once(); + replay(sw); + controller.handlePortStatusMessage(sw, ofps, false); + verify(sw); + verifyPortChangedUpdateInQueue(sw); + reset(sw); + } }