From 2300e677d300aa7d384abaa674173ad91a36a4b5 Mon Sep 17 00:00:00 2001 From: Ryan Izard <rizard@g.clemson.edu> Date: Fri, 20 Feb 2015 09:49:31 -0500 Subject: [PATCH] Added a patch to support HP ProCurve switches and others that do not support the OFPT_BARRIER_REQUEST message. Updated SFP unit test to include the SEND_FLOW_REM flag in the test flows (forgot from last commit). --- .../internal/OFSwitchHandshakeHandler.java | 46 +- .../resources/floodlightdefault.properties | 1 + src/main/resources/logback-test.xml | 2 +- .../staticflowentry/StaticFlowTests.java | 617 +++++++++--------- 4 files changed, 355 insertions(+), 311 deletions(-) diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java index e70b43c36..d0e0f6911 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java @@ -43,6 +43,8 @@ import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; import org.projectfloodlight.openflow.protocol.OFGetConfigReply; import org.projectfloodlight.openflow.protocol.OFGetConfigRequest; +import org.projectfloodlight.openflow.protocol.OFGroupDelete; +import org.projectfloodlight.openflow.protocol.OFGroupType; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply; @@ -64,6 +66,7 @@ import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.OFGroup; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TableId; import org.projectfloodlight.openflow.types.U64; @@ -440,11 +443,33 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { OFFlowDelete deleteFlows = this.factory.buildFlowDelete() .build(); this.sw.write(deleteFlows); - } else { /* All other OFVersions support multiple tables. */ + } else { /* All other OFVersions support multiple tables and groups. */ OFFlowDelete deleteFlows = this.factory.buildFlowDelete() .setTableId(TableId.ALL) .build(); this.sw.write(deleteFlows); + + /* + * Clear all groups. + * We have to do this for all types manually as of Loxi 0.9.0. + */ + OFGroupDelete delgroup = this.sw.getOFFactory().buildGroupDelete() + .setGroup(OFGroup.ALL) + .setGroupType(OFGroupType.ALL) + .build(); + this.sw.write(delgroup); + delgroup.createBuilder() + .setGroupType(OFGroupType.FF) + .build(); + this.sw.write(delgroup); + delgroup.createBuilder() + .setGroupType(OFGroupType.INDIRECT) + .build(); + this.sw.write(delgroup); + delgroup.createBuilder() + .setGroupType(OFGroupType.SELECT) + .build(); + this.sw.write(delgroup); } /* @@ -845,15 +870,28 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { @Override void processOFError(OFErrorMsg m) { - logErrorDisconnect(m); - } + /* + * HP ProCurve switches do not support + * the ofpt_barrier_request message. + * + * Look for an error from a bad ofpt_barrier_request, + * log a warning, but proceed. + */ + if (m.getErrType() == OFErrorType.BAD_REQUEST && + ((OFBadRequestErrorMsg) m).getCode() == OFBadRequestCode.BAD_TYPE && + ((OFBadRequestErrorMsg) m).getData().getParsedMessage().get() instanceof OFBarrierRequest) { + log.warn("Switch does not support Barrier Request messages. Could be an HP ProCurve."); + } else { + logErrorDisconnect(m); + } + } @Override void enterState() { sendHandshakeSetConfig(); } } - + /** * We are waiting for a OFDescriptionStat message from the switch. * Once we receive any stat message we try to parse it. If it's not diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index 9dc689f7c..47b5bc91a 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -19,4 +19,5 @@ org.sdnplatform.sync.internal.SyncManager.authScheme=CHALLENGE_RESPONSE org.sdnplatform.sync.internal.SyncManager.keyStorePath=/etc/floodlight/auth_credentials.jceks org.sdnplatform.sync.internal.SyncManager.dbPath=/var/lib/floodlight/ org.sdnplatform.sync.internal.SyncManager.port=6642 +net.floodlightcontroller.core.internal.FloodlightProvider.openflowPort=6653 net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml index 1986dbd15..72350a0bc 100644 --- a/src/main/resources/logback-test.xml +++ b/src/main/resources/logback-test.xml @@ -18,7 +18,7 @@ <logger name="net.floodlightcontroller.packet" level="INFO"></logger> <logger name="net.floodlightcontroller.forwarding" level="INFO"></logger> <logger name="net.floodlightcontroller.routing" level="INFO"></logger> - <logger name="net.floodlightcontroller.core" level="INFO"></logger> + <logger name="net.floodlightcontroller.core.internal" level="INFO"></logger> <logger level="DEBUG" name="net.floodlightcontroller.firewall"></logger> <logger level="INFO" name="net.floodlightcontroller.staticflowentry"></logger> </configuration> diff --git a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java b/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java index 562edb123..51b2319f1 100644 --- a/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java +++ b/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java @@ -17,6 +17,7 @@ package net.floodlightcontroller.staticflowentry; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -30,6 +31,7 @@ import org.junit.Test; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.OFFlowModFlags; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.protocol.OFMessage; @@ -61,310 +63,313 @@ import static org.junit.Assert.*; public class StaticFlowTests extends FloodlightTestCase { - static String TestSwitch1DPID = "00:00:00:00:00:00:00:01"; - static int TotalTestRules = 3; - - static OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); - - /*** - * Create TestRuleXXX and the corresponding FlowModXXX - * for X = 1..3 - */ - static Map<String,Object> TestRule1; - static OFFlowMod FlowMod1; - static { - FlowMod1 = factory.buildFlowModify().build(); - TestRule1 = new HashMap<String,Object>(); - TestRule1.put(COLUMN_NAME, "TestRule1"); - TestRule1.put(COLUMN_SWITCH, TestSwitch1DPID); - // setup match - Match match; - TestRule1.put(COLUMN_DL_DST, "00:20:30:40:50:60"); - match = MatchUtils.fromString("eth_dst=00:20:30:40:50:60", factory.getVersion()); - // setup actions - List<OFAction> actions = new LinkedList<OFAction>(); - TestRule1.put(COLUMN_ACTIONS, "output=1"); - actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE)); - // done - FlowMod1 = FlowMod1.createBuilder().setMatch(match) - .setActions(actions) - .setBufferId(OFBufferId.NO_BUFFER) - .setOutPort(OFPort.ANY) - .setPriority(Integer.MAX_VALUE) - .setXid(4) - .build(); - } - - static Map<String,Object> TestRule2; - static OFFlowMod FlowMod2; - - static { - FlowMod2 = factory.buildFlowModify().build(); - TestRule2 = new HashMap<String,Object>(); - TestRule2.put(COLUMN_NAME, "TestRule2"); - TestRule2.put(COLUMN_SWITCH, TestSwitch1DPID); - // setup match - Match match; - TestRule2.put(COLUMN_DL_TYPE, "0x800"); - TestRule2.put(COLUMN_NW_DST, "192.168.1.0/24"); - match = MatchUtils.fromString("eth_type=0x800,ipv4_dst=192.168.1.0/24", factory.getVersion()); - // setup actions - List<OFAction> actions = new LinkedList<OFAction>(); - TestRule2.put(COLUMN_ACTIONS, "output=1"); - actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE)); - // done - FlowMod2 = FlowMod2.createBuilder().setMatch(match) - .setActions(actions) - .setBufferId(OFBufferId.NO_BUFFER) - .setOutPort(OFPort.ANY) - .setPriority(Integer.MAX_VALUE) - .setXid(5) - .build(); - } - - - static Map<String,Object> TestRule3; - static OFFlowMod FlowMod3; - private StaticFlowEntryPusher staticFlowEntryPusher; - private IOFSwitchService switchService; - private IOFSwitch mockSwitch; - private MockDebugCounterService debugCounterService; - private Capture<OFMessage> writeCapture; - private Capture<List<OFMessage>> writeCaptureList; - private long dpid; - private MemoryStorageSource storage; - static { - FlowMod3 = factory.buildFlowModify().build(); - TestRule3 = new HashMap<String,Object>(); - TestRule3.put(COLUMN_NAME, "TestRule3"); - TestRule3.put(COLUMN_SWITCH, TestSwitch1DPID); - // setup match - Match match; - TestRule3.put(COLUMN_DL_DST, "00:20:30:40:50:60"); - TestRule3.put(COLUMN_DL_VLAN, 96); - match = MatchUtils.fromString("eth_dst=00:20:30:40:50:60,eth_vlan_vid=96", factory.getVersion()); - // setup actions - TestRule3.put(COLUMN_ACTIONS, "output=controller"); - List<OFAction> actions = new LinkedList<OFAction>(); - actions.add(factory.actions().output(OFPort.CONTROLLER, Integer.MAX_VALUE)); - // done - FlowMod3 = FlowMod3.createBuilder().setMatch(match) - .setActions(actions) - .setBufferId(OFBufferId.NO_BUFFER) - .setOutPort(OFPort.ANY) - .setPriority(Integer.MAX_VALUE) - .setXid(6) - .build(); - } - - private void verifyFlowMod(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { - verifyMatch(testFlowMod, goodFlowMod); - verifyActions(testFlowMod, goodFlowMod); - // dont' bother testing the cookie; just copy it over - goodFlowMod = goodFlowMod.createBuilder().setCookie(testFlowMod.getCookie()).build(); - // .. so we can continue to use .equals() - assertTrue(OFMessageUtils.equalsIgnoreXid(goodFlowMod, testFlowMod)); - } - - - private void verifyMatch(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { - assertEquals(goodFlowMod.getMatch(), testFlowMod.getMatch()); - } - - - private void verifyActions(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { - List<OFAction> goodActions = goodFlowMod.getActions(); - List<OFAction> testActions = testFlowMod.getActions(); - assertNotNull(goodActions); - assertNotNull(testActions); - assertEquals(goodActions.size(), testActions.size()); - // assumes actions are marshalled in same order; should be safe - for(int i = 0; i < goodActions.size(); i++) { - assertEquals(goodActions.get(i), testActions.get(i)); - } - } - - - @Override - public void setUp() throws Exception { - super.setUp(); - debugCounterService = new MockDebugCounterService(); - staticFlowEntryPusher = new StaticFlowEntryPusher(); - switchService = getMockSwitchService(); - storage = new MemoryStorageSource(); - dpid = HexString.toLong(TestSwitch1DPID); - - mockSwitch = createNiceMock(IOFSwitch.class); - writeCapture = new Capture<OFMessage>(CaptureType.ALL); - writeCaptureList = new Capture<List<OFMessage>>(CaptureType.ALL); - - mockSwitch.write(capture(writeCapture)); - expectLastCall().anyTimes(); - mockSwitch.write(capture(writeCaptureList)); - expectLastCall().anyTimes(); - mockSwitch.flush(); - expectLastCall().anyTimes(); - expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes(); - replay(mockSwitch); - - FloodlightModuleContext fmc = new FloodlightModuleContext(); - fmc.addService(IStorageSourceService.class, storage); - fmc.addService(IOFSwitchService.class, getMockSwitchService()); - fmc.addService(IDebugCounterService.class, debugCounterService); - - MockFloodlightProvider mockFloodlightProvider = getMockFloodlightProvider(); - Map<DatapathId, IOFSwitch> switchMap = new HashMap<DatapathId, IOFSwitch>(); - switchMap.put(DatapathId.of(dpid), mockSwitch); - getMockSwitchService().setSwitches(switchMap); - fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider); - RestApiServer restApi = new RestApiServer(); - fmc.addService(IRestApiService.class, restApi); - fmc.addService(IOFSwitchService.class, switchService); - - restApi.init(fmc); - debugCounterService.init(fmc); - storage.init(fmc); - staticFlowEntryPusher.init(fmc); - debugCounterService.init(fmc); - storage.startUp(fmc); - - createStorageWithFlowEntries(); - - staticFlowEntryPusher.startUp(fmc); // again, to hack unittest - } - - @Test - public void testStaticFlowPush() throws Exception { - - // verify that flowpusher read all three entries from storage - assertEquals(TotalTestRules, staticFlowEntryPusher.countEntries()); - - // if someone calls mockSwitch.getOutputStream(), return mockOutStream instead - //expect(mockSwitch.getOutputStream()).andReturn(mockOutStream).anyTimes(); - - // if someone calls getId(), return this dpid instead - resetToNice(mockSwitch); - mockSwitch.write(capture(writeCapture)); - expectLastCall().anyTimes(); - mockSwitch.write(capture(writeCaptureList)); - expectLastCall().anyTimes(); - mockSwitch.flush(); - expectLastCall().anyTimes(); - expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes(); - expect(mockSwitch.getId()).andReturn(DatapathId.of(dpid)).anyTimes(); - replay(mockSwitch); - - // hook the static pusher up to the fake switch - staticFlowEntryPusher.switchAdded(DatapathId.of(dpid)); - - verify(mockSwitch); - - // Verify that the switch has gotten some flow_mods - assertEquals(true, writeCapture.hasCaptured()); - assertEquals(TotalTestRules, writeCapture.getValues().size()); - - // Order assumes how things are stored in hash bucket; - // should be fixed because OFMessage.hashCode() is deterministic - OFFlowMod firstFlowMod = (OFFlowMod) writeCapture.getValues().get(2); - verifyFlowMod(firstFlowMod, FlowMod1); - OFFlowMod secondFlowMod = (OFFlowMod) writeCapture.getValues().get(1); - verifyFlowMod(secondFlowMod, FlowMod2); - OFFlowMod thirdFlowMod = (OFFlowMod) writeCapture.getValues().get(0); - verifyFlowMod(thirdFlowMod, FlowMod3); - - writeCapture.reset(); - - // delete two rules and verify they've been removed - // this should invoke staticFlowPusher.rowsDeleted() - storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule1"); - storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule2"); - - assertEquals(1, staticFlowEntryPusher.countEntries()); - assertEquals(2, writeCapture.getValues().size()); - - OFFlowMod firstDelete = (OFFlowMod) writeCapture.getValues().get(0); - FlowMod1 = FlowModUtils.toFlowDeleteStrict(FlowMod1); - verifyFlowMod(firstDelete, FlowMod1); - - OFFlowMod secondDelete = (OFFlowMod) writeCapture.getValues().get(1); - FlowMod2 = FlowModUtils.toFlowDeleteStrict(FlowMod2); - verifyFlowMod(secondDelete, FlowMod2); - - // add rules back to make sure that staticFlowPusher.rowsInserted() works - writeCapture.reset(); - FlowMod2 = FlowModUtils.toFlowAdd(FlowMod2); - FlowMod2 = FlowMod2.createBuilder().setXid(12).build(); - storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2); - assertEquals(2, staticFlowEntryPusher.countEntries()); - assertEquals(1, writeCaptureList.getValues().size()); - List<OFMessage> outList = - writeCaptureList.getValues().get(0); - assertEquals(1, outList.size()); - OFFlowMod firstAdd = (OFFlowMod) outList.get(0); - verifyFlowMod(firstAdd, FlowMod2); - writeCapture.reset(); - writeCaptureList.reset(); - - // now try an overwriting update, calling staticFlowPusher.rowUpdated() - TestRule3.put(COLUMN_DL_VLAN, 333); - storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); - assertEquals(2, staticFlowEntryPusher.countEntries()); - assertEquals(1, writeCaptureList.getValues().size()); - - outList = writeCaptureList.getValues().get(0); - assertEquals(2, outList.size()); - OFFlowMod removeFlowMod = (OFFlowMod) outList.get(0); - FlowMod3 = FlowModUtils.toFlowDeleteStrict(FlowMod3); - verifyFlowMod(removeFlowMod, FlowMod3); - FlowMod3 = FlowModUtils.toFlowAdd(FlowMod3); - FlowMod3 = FlowMod3.createBuilder().setMatch(MatchUtils.fromString("eth_dst=00:20:30:40:50:60,eth_vlan_vid=333", factory.getVersion())).setXid(14).build(); - OFFlowMod updateFlowMod = (OFFlowMod) outList.get(1); - verifyFlowMod(updateFlowMod, FlowMod3); - writeCaptureList.reset(); - - // now try an action modifying update, calling staticFlowPusher.rowUpdated() - TestRule3.put(COLUMN_ACTIONS, "output=controller,pop_vlan"); // added pop-vlan - storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); - assertEquals(2, staticFlowEntryPusher.countEntries()); - assertEquals(1, writeCaptureList.getValues().size()); - - outList = writeCaptureList.getValues().get(0); - assertEquals(1, outList.size()); - OFFlowMod modifyFlowMod = (OFFlowMod) outList.get(0); - FlowMod3 = FlowModUtils.toFlowModifyStrict(FlowMod3); - List<OFAction> modifiedActions = FlowMod3.getActions(); - modifiedActions.add(factory.actions().popVlan()); // add the new action to what we should expect - FlowMod3 = FlowMod3.createBuilder().setActions(modifiedActions).setXid(19).build(); - verifyFlowMod(modifyFlowMod, FlowMod3); - } - - - IStorageSourceService createStorageWithFlowEntries() { - return populateStorageWithFlowEntries(); - } - - IStorageSourceService populateStorageWithFlowEntries() { - Set<String> indexedColumns = new HashSet<String>(); - indexedColumns.add(COLUMN_NAME); - storage.createTable(StaticFlowEntryPusher.TABLE_NAME, indexedColumns); - storage.setTablePrimaryKeyName(StaticFlowEntryPusher.TABLE_NAME, COLUMN_NAME); - - storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule1); - storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2); - storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); - - return storage; - } - - @Test - public void testHARoleChanged() throws IOException { - - assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID)); - assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); - assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); - assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); - - /* FIXME: what's the right behavior here ?? + static String TestSwitch1DPID = "00:00:00:00:00:00:00:01"; + static int TotalTestRules = 3; + + static OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); + + /*** + * Create TestRuleXXX and the corresponding FlowModXXX + * for X = 1..3 + */ + static Map<String,Object> TestRule1; + static OFFlowMod FlowMod1; + static { + FlowMod1 = factory.buildFlowModify().build(); + TestRule1 = new HashMap<String,Object>(); + TestRule1.put(COLUMN_NAME, "TestRule1"); + TestRule1.put(COLUMN_SWITCH, TestSwitch1DPID); + // setup match + Match match; + TestRule1.put(COLUMN_DL_DST, "00:20:30:40:50:60"); + match = MatchUtils.fromString("eth_dst=00:20:30:40:50:60", factory.getVersion()); + // setup actions + List<OFAction> actions = new LinkedList<OFAction>(); + TestRule1.put(COLUMN_ACTIONS, "output=1"); + actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE)); + // done + FlowMod1 = FlowMod1.createBuilder().setMatch(match) + .setActions(actions) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setBufferId(OFBufferId.NO_BUFFER) + .setOutPort(OFPort.ANY) + .setPriority(Integer.MAX_VALUE) + .setXid(4) + .build(); + } + + static Map<String,Object> TestRule2; + static OFFlowMod FlowMod2; + + static { + FlowMod2 = factory.buildFlowModify().build(); + TestRule2 = new HashMap<String,Object>(); + TestRule2.put(COLUMN_NAME, "TestRule2"); + TestRule2.put(COLUMN_SWITCH, TestSwitch1DPID); + // setup match + Match match; + TestRule2.put(COLUMN_DL_TYPE, "0x800"); + TestRule2.put(COLUMN_NW_DST, "192.168.1.0/24"); + match = MatchUtils.fromString("eth_type=0x800,ipv4_dst=192.168.1.0/24", factory.getVersion()); + // setup actions + List<OFAction> actions = new LinkedList<OFAction>(); + TestRule2.put(COLUMN_ACTIONS, "output=1"); + actions.add(factory.actions().output(OFPort.of(1), Integer.MAX_VALUE)); + // done + FlowMod2 = FlowMod2.createBuilder().setMatch(match) + .setActions(actions) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setBufferId(OFBufferId.NO_BUFFER) + .setOutPort(OFPort.ANY) + .setPriority(Integer.MAX_VALUE) + .setXid(5) + .build(); + } + + + static Map<String,Object> TestRule3; + static OFFlowMod FlowMod3; + private StaticFlowEntryPusher staticFlowEntryPusher; + private IOFSwitchService switchService; + private IOFSwitch mockSwitch; + private MockDebugCounterService debugCounterService; + private Capture<OFMessage> writeCapture; + private Capture<List<OFMessage>> writeCaptureList; + private long dpid; + private MemoryStorageSource storage; + static { + FlowMod3 = factory.buildFlowModify().build(); + TestRule3 = new HashMap<String,Object>(); + TestRule3.put(COLUMN_NAME, "TestRule3"); + TestRule3.put(COLUMN_SWITCH, TestSwitch1DPID); + // setup match + Match match; + TestRule3.put(COLUMN_DL_DST, "00:20:30:40:50:60"); + TestRule3.put(COLUMN_DL_VLAN, 96); + match = MatchUtils.fromString("eth_dst=00:20:30:40:50:60,eth_vlan_vid=96", factory.getVersion()); + // setup actions + TestRule3.put(COLUMN_ACTIONS, "output=controller"); + List<OFAction> actions = new LinkedList<OFAction>(); + actions.add(factory.actions().output(OFPort.CONTROLLER, Integer.MAX_VALUE)); + // done + FlowMod3 = FlowMod3.createBuilder().setMatch(match) + .setActions(actions) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setBufferId(OFBufferId.NO_BUFFER) + .setOutPort(OFPort.ANY) + .setPriority(Integer.MAX_VALUE) + .setXid(6) + .build(); + } + + private void verifyFlowMod(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { + verifyMatch(testFlowMod, goodFlowMod); + verifyActions(testFlowMod, goodFlowMod); + // dont' bother testing the cookie; just copy it over + goodFlowMod = goodFlowMod.createBuilder().setCookie(testFlowMod.getCookie()).build(); + // .. so we can continue to use .equals() + assertTrue(OFMessageUtils.equalsIgnoreXid(goodFlowMod, testFlowMod)); + } + + + private void verifyMatch(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { + assertEquals(goodFlowMod.getMatch(), testFlowMod.getMatch()); + } + + + private void verifyActions(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { + List<OFAction> goodActions = goodFlowMod.getActions(); + List<OFAction> testActions = testFlowMod.getActions(); + assertNotNull(goodActions); + assertNotNull(testActions); + assertEquals(goodActions.size(), testActions.size()); + // assumes actions are marshalled in same order; should be safe + for(int i = 0; i < goodActions.size(); i++) { + assertEquals(goodActions.get(i), testActions.get(i)); + } + } + + + @Override + public void setUp() throws Exception { + super.setUp(); + debugCounterService = new MockDebugCounterService(); + staticFlowEntryPusher = new StaticFlowEntryPusher(); + switchService = getMockSwitchService(); + storage = new MemoryStorageSource(); + dpid = HexString.toLong(TestSwitch1DPID); + + mockSwitch = createNiceMock(IOFSwitch.class); + writeCapture = new Capture<OFMessage>(CaptureType.ALL); + writeCaptureList = new Capture<List<OFMessage>>(CaptureType.ALL); + + mockSwitch.write(capture(writeCapture)); + expectLastCall().anyTimes(); + mockSwitch.write(capture(writeCaptureList)); + expectLastCall().anyTimes(); + mockSwitch.flush(); + expectLastCall().anyTimes(); + expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes(); + replay(mockSwitch); + + FloodlightModuleContext fmc = new FloodlightModuleContext(); + fmc.addService(IStorageSourceService.class, storage); + fmc.addService(IOFSwitchService.class, getMockSwitchService()); + fmc.addService(IDebugCounterService.class, debugCounterService); + + MockFloodlightProvider mockFloodlightProvider = getMockFloodlightProvider(); + Map<DatapathId, IOFSwitch> switchMap = new HashMap<DatapathId, IOFSwitch>(); + switchMap.put(DatapathId.of(dpid), mockSwitch); + getMockSwitchService().setSwitches(switchMap); + fmc.addService(IFloodlightProviderService.class, mockFloodlightProvider); + RestApiServer restApi = new RestApiServer(); + fmc.addService(IRestApiService.class, restApi); + fmc.addService(IOFSwitchService.class, switchService); + + restApi.init(fmc); + debugCounterService.init(fmc); + storage.init(fmc); + staticFlowEntryPusher.init(fmc); + debugCounterService.init(fmc); + storage.startUp(fmc); + + createStorageWithFlowEntries(); + + staticFlowEntryPusher.startUp(fmc); // again, to hack unittest + } + + @Test + public void testStaticFlowPush() throws Exception { + + // verify that flowpusher read all three entries from storage + assertEquals(TotalTestRules, staticFlowEntryPusher.countEntries()); + + // if someone calls mockSwitch.getOutputStream(), return mockOutStream instead + //expect(mockSwitch.getOutputStream()).andReturn(mockOutStream).anyTimes(); + + // if someone calls getId(), return this dpid instead + resetToNice(mockSwitch); + mockSwitch.write(capture(writeCapture)); + expectLastCall().anyTimes(); + mockSwitch.write(capture(writeCaptureList)); + expectLastCall().anyTimes(); + mockSwitch.flush(); + expectLastCall().anyTimes(); + expect(mockSwitch.getOFFactory()).andReturn(factory).anyTimes(); + expect(mockSwitch.getId()).andReturn(DatapathId.of(dpid)).anyTimes(); + replay(mockSwitch); + + // hook the static pusher up to the fake switch + staticFlowEntryPusher.switchAdded(DatapathId.of(dpid)); + + verify(mockSwitch); + + // Verify that the switch has gotten some flow_mods + assertEquals(true, writeCapture.hasCaptured()); + assertEquals(TotalTestRules, writeCapture.getValues().size()); + + // Order assumes how things are stored in hash bucket; + // should be fixed because OFMessage.hashCode() is deterministic + OFFlowMod firstFlowMod = (OFFlowMod) writeCapture.getValues().get(2); + verifyFlowMod(firstFlowMod, FlowMod1); + OFFlowMod secondFlowMod = (OFFlowMod) writeCapture.getValues().get(1); + verifyFlowMod(secondFlowMod, FlowMod2); + OFFlowMod thirdFlowMod = (OFFlowMod) writeCapture.getValues().get(0); + verifyFlowMod(thirdFlowMod, FlowMod3); + + writeCapture.reset(); + + // delete two rules and verify they've been removed + // this should invoke staticFlowPusher.rowsDeleted() + storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule1"); + storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule2"); + + assertEquals(1, staticFlowEntryPusher.countEntries()); + assertEquals(2, writeCapture.getValues().size()); + + OFFlowMod firstDelete = (OFFlowMod) writeCapture.getValues().get(0); + FlowMod1 = FlowModUtils.toFlowDeleteStrict(FlowMod1); + verifyFlowMod(firstDelete, FlowMod1); + + OFFlowMod secondDelete = (OFFlowMod) writeCapture.getValues().get(1); + FlowMod2 = FlowModUtils.toFlowDeleteStrict(FlowMod2); + verifyFlowMod(secondDelete, FlowMod2); + + // add rules back to make sure that staticFlowPusher.rowsInserted() works + writeCapture.reset(); + FlowMod2 = FlowModUtils.toFlowAdd(FlowMod2); + FlowMod2 = FlowMod2.createBuilder().setXid(12).build(); + storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2); + assertEquals(2, staticFlowEntryPusher.countEntries()); + assertEquals(1, writeCaptureList.getValues().size()); + List<OFMessage> outList = + writeCaptureList.getValues().get(0); + assertEquals(1, outList.size()); + OFFlowMod firstAdd = (OFFlowMod) outList.get(0); + verifyFlowMod(firstAdd, FlowMod2); + writeCapture.reset(); + writeCaptureList.reset(); + + // now try an overwriting update, calling staticFlowPusher.rowUpdated() + TestRule3.put(COLUMN_DL_VLAN, 333); + storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); + assertEquals(2, staticFlowEntryPusher.countEntries()); + assertEquals(1, writeCaptureList.getValues().size()); + + outList = writeCaptureList.getValues().get(0); + assertEquals(2, outList.size()); + OFFlowMod removeFlowMod = (OFFlowMod) outList.get(0); + FlowMod3 = FlowModUtils.toFlowDeleteStrict(FlowMod3); + verifyFlowMod(removeFlowMod, FlowMod3); + FlowMod3 = FlowModUtils.toFlowAdd(FlowMod3); + FlowMod3 = FlowMod3.createBuilder().setMatch(MatchUtils.fromString("eth_dst=00:20:30:40:50:60,eth_vlan_vid=333", factory.getVersion())).setXid(14).build(); + OFFlowMod updateFlowMod = (OFFlowMod) outList.get(1); + verifyFlowMod(updateFlowMod, FlowMod3); + writeCaptureList.reset(); + + // now try an action modifying update, calling staticFlowPusher.rowUpdated() + TestRule3.put(COLUMN_ACTIONS, "output=controller,pop_vlan"); // added pop-vlan + storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); + assertEquals(2, staticFlowEntryPusher.countEntries()); + assertEquals(1, writeCaptureList.getValues().size()); + + outList = writeCaptureList.getValues().get(0); + assertEquals(1, outList.size()); + OFFlowMod modifyFlowMod = (OFFlowMod) outList.get(0); + FlowMod3 = FlowModUtils.toFlowModifyStrict(FlowMod3); + List<OFAction> modifiedActions = FlowMod3.getActions(); + modifiedActions.add(factory.actions().popVlan()); // add the new action to what we should expect + FlowMod3 = FlowMod3.createBuilder().setActions(modifiedActions).setXid(19).build(); + verifyFlowMod(modifyFlowMod, FlowMod3); + } + + + IStorageSourceService createStorageWithFlowEntries() { + return populateStorageWithFlowEntries(); + } + + IStorageSourceService populateStorageWithFlowEntries() { + Set<String> indexedColumns = new HashSet<String>(); + indexedColumns.add(COLUMN_NAME); + storage.createTable(StaticFlowEntryPusher.TABLE_NAME, indexedColumns); + storage.setTablePrimaryKeyName(StaticFlowEntryPusher.TABLE_NAME, COLUMN_NAME); + + storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule1); + storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2); + storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); + + return storage; + } + + @Test + public void testHARoleChanged() throws IOException { + + assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); + assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); + + /* FIXME: what's the right behavior here ?? // Send a notification that we've changed to slave mfp.dispatchRoleChanged(Role.SLAVE); // Make sure we've removed all our entries @@ -378,6 +383,6 @@ public class StaticFlowTests extends FloodlightTestCase { assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); - */ - } + */ + } } -- GitLab