Import Floodlight v0.90
diff --git a/src/ext/floodlight/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java b/src/ext/floodlight/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
new file mode 100644
index 0000000..186fd69
--- /dev/null
+++ b/src/ext/floodlight/src/test/java/net/floodlightcontroller/staticflowentry/StaticFlowTests.java
@@ -0,0 +1,334 @@
+package net.floodlightcontroller.staticflowentry;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+import org.easymock.Capture;
+import org.easymock.CaptureType;
+import org.junit.Test;
+import org.openflow.protocol.OFFlowMod;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFPort;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.util.HexString;
+
+
+import net.floodlightcontroller.core.FloodlightContext;
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
+import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.module.FloodlightModuleContext;
+import net.floodlightcontroller.core.module.FloodlightModuleException;
+import net.floodlightcontroller.core.test.MockFloodlightProvider;
+import net.floodlightcontroller.test.FloodlightTestCase;
+import net.floodlightcontroller.restserver.RestApiServer;
+import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher;
+import net.floodlightcontroller.storage.IStorageSourceService;
+import net.floodlightcontroller.storage.memory.MemoryStorageSource;
+import static net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher.*;
+import static org.easymock.EasyMock.*;
+
+public class StaticFlowTests extends FloodlightTestCase {
+
+ static String TestSwitch1DPID = "00:00:00:00:00:00:00:01";
+ static int TotalTestRules = 3;
+
+ /***
+ * Create TestRuleXXX and the corresponding FlowModXXX
+ * for X = 1..3
+ */
+ static Map<String,Object> TestRule1;
+ static OFFlowMod FlowMod1;
+ static {
+ FlowMod1 = new OFFlowMod();
+ TestRule1 = new HashMap<String,Object>();
+ TestRule1.put(COLUMN_NAME, "TestRule1");
+ TestRule1.put(COLUMN_SWITCH, TestSwitch1DPID);
+ // setup match
+ OFMatch match = new OFMatch();
+ TestRule1.put(COLUMN_DL_DST, "00:20:30:40:50:60");
+ match.fromString("dl_dst=00:20:30:40:50:60");
+ // setup actions
+ List<OFAction> actions = new LinkedList<OFAction>();
+ TestRule1.put(COLUMN_ACTIONS, "output=1");
+ actions.add(new OFActionOutput((short)1, (short) Short.MAX_VALUE));
+ // done
+ FlowMod1.setMatch(match);
+ FlowMod1.setActions(actions);
+ FlowMod1.setBufferId(-1);
+ FlowMod1.setOutPort(OFPort.OFPP_NONE.getValue());
+ FlowMod1.setPriority(Short.MAX_VALUE);
+ FlowMod1.setLengthU(OFFlowMod.MINIMUM_LENGTH + 8); // 8 bytes of actions
+ }
+
+ static Map<String,Object> TestRule2;
+ static OFFlowMod FlowMod2;
+
+ static {
+ FlowMod2 = new OFFlowMod();
+ TestRule2 = new HashMap<String,Object>();
+ TestRule2.put(COLUMN_NAME, "TestRule2");
+ TestRule2.put(COLUMN_SWITCH, TestSwitch1DPID);
+ // setup match
+ OFMatch match = new OFMatch();
+ TestRule2.put(COLUMN_NW_DST, "192.168.1.0/24");
+ match.fromString("nw_dst=192.168.1.0/24");
+ // setup actions
+ List<OFAction> actions = new LinkedList<OFAction>();
+ TestRule2.put(COLUMN_ACTIONS, "output=1");
+ actions.add(new OFActionOutput((short)1, (short) Short.MAX_VALUE));
+ // done
+ FlowMod2.setMatch(match);
+ FlowMod2.setActions(actions);
+ FlowMod2.setBufferId(-1);
+ FlowMod2.setOutPort(OFPort.OFPP_NONE.getValue());
+ FlowMod2.setPriority(Short.MAX_VALUE);
+ FlowMod2.setLengthU(OFFlowMod.MINIMUM_LENGTH + 8); // 8 bytes of actions
+
+ }
+
+
+ static Map<String,Object> TestRule3;
+ static OFFlowMod FlowMod3;
+ static {
+ FlowMod3 = new OFFlowMod();
+ TestRule3 = new HashMap<String,Object>();
+ TestRule3.put(COLUMN_NAME, "TestRule3");
+ TestRule3.put(COLUMN_SWITCH, TestSwitch1DPID);
+ // setup match
+ OFMatch match = new OFMatch();
+ TestRule3.put(COLUMN_DL_DST, "00:20:30:40:50:60");
+ TestRule3.put(COLUMN_DL_VLAN, 4096);
+ match.fromString("dl_dst=00:20:30:40:50:60,dl_vlan=4096");
+ // setup actions
+ TestRule3.put(COLUMN_ACTIONS, "output=controller");
+ List<OFAction> actions = new LinkedList<OFAction>();
+ actions.add(new OFActionOutput(OFPort.OFPP_CONTROLLER.getValue(), (short) Short.MAX_VALUE));
+ // done
+ FlowMod3.setMatch(match);
+ FlowMod3.setActions(actions);
+ FlowMod3.setBufferId(-1);
+ FlowMod3.setOutPort(OFPort.OFPP_NONE.getValue());
+ FlowMod3.setPriority(Short.MAX_VALUE);
+ FlowMod3.setLengthU(OFFlowMod.MINIMUM_LENGTH + 8); // 8 bytes of actions
+
+ }
+
+ private void verifyFlowMod(OFFlowMod testFlowMod,
+ OFFlowMod goodFlowMod) {
+ verifyMatch(testFlowMod, goodFlowMod);
+ verifyActions(testFlowMod, goodFlowMod);
+ // dont' bother testing the cookie; just copy it over
+ goodFlowMod.setCookie(testFlowMod.getCookie());
+ // .. so we can continue to use .equals()
+ assertEquals(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();
+ }
+
+ @Test
+ public void testStaticFlowPush() throws IOException {
+ StaticFlowEntryPusher staticFlowEntryPusher = new StaticFlowEntryPusher();
+ IStorageSourceService storage = createStorageWithFlowEntries();
+ long dpid = HexString.toLong(TestSwitch1DPID);
+
+ // Create a Switch and attach a switch
+ IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class);
+ Capture<OFMessage> writeCapture = new Capture<OFMessage>(CaptureType.ALL);
+ Capture<FloodlightContext> contextCapture = new Capture<FloodlightContext>(CaptureType.ALL);
+ Capture<List<OFMessage>> writeCaptureList = new Capture<List<OFMessage>>(CaptureType.ALL);
+
+ //OFMessageSafeOutStream mockOutStream = createNiceMock(OFMessageSafeOutStream.class);
+ mockSwitch.write(capture(writeCapture), capture(contextCapture));
+ expectLastCall().anyTimes();
+ mockSwitch.write(capture(writeCaptureList), capture(contextCapture));
+ expectLastCall().anyTimes();
+ mockSwitch.flush();
+ expectLastCall().anyTimes();
+
+ staticFlowEntryPusher.setStorageSource(storage);
+
+ FloodlightModuleContext fmc = new FloodlightModuleContext();
+
+ MockFloodlightProvider mockFloodlightProvider = getMockFloodlightProvider();
+ Map<Long, IOFSwitch> switchMap = new HashMap<Long, IOFSwitch>();
+ switchMap.put(dpid, mockSwitch);
+ // NO ! expect(mockFloodlightProvider.getSwitches()).andReturn(switchMap).anyTimes();
+ mockFloodlightProvider.setSwitches(switchMap);
+ staticFlowEntryPusher.setFloodlightProvider(mockFloodlightProvider);
+ RestApiServer restApi = new RestApiServer();
+ try {
+ restApi.init(fmc);
+ } catch (FloodlightModuleException e) {
+ e.printStackTrace();
+ }
+ staticFlowEntryPusher.restApi = restApi;
+ staticFlowEntryPusher.startUp(null); // again, to hack unittest
+
+ // 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
+ expect(mockSwitch.getId()).andReturn(dpid).anyTimes();
+ expect(mockSwitch.getStringId()).andReturn(TestSwitch1DPID).anyTimes();
+ replay(mockSwitch);
+
+ // hook the static pusher up to the fake switch
+ staticFlowEntryPusher.addedSwitch(mockSwitch);
+
+ 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();
+ contextCapture.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.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
+ verifyFlowMod(firstDelete, FlowMod1);
+
+ OFFlowMod secondDelete = (OFFlowMod) writeCapture.getValues().get(1);
+ FlowMod2.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
+ verifyFlowMod(secondDelete, FlowMod2);
+
+ // add rules back to make sure that staticFlowPusher.rowsInserted() works
+ writeCapture.reset();
+ FlowMod2.setCommand(OFFlowMod.OFPFC_ADD);
+ storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2);
+ assertEquals(2, staticFlowEntryPusher.countEntries());
+ assertEquals(1, writeCaptureList.getValues().size());
+ List<OFMessage> outList =
+ (List<OFMessage>) writeCaptureList.getValues().get(0);
+ assertEquals(1, outList.size());
+ OFFlowMod firstAdd = (OFFlowMod) outList.get(0);
+ verifyFlowMod(firstAdd, FlowMod2);
+ writeCapture.reset();
+ contextCapture.reset();
+ writeCaptureList.reset();
+
+ // now try an 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 = (List<OFMessage>) writeCaptureList.getValues().get(0);
+ assertEquals(2, outList.size());
+ OFFlowMod removeFlowMod = (OFFlowMod) outList.get(0);
+ FlowMod3.setCommand(OFFlowMod.OFPFC_DELETE_STRICT);
+ verifyFlowMod(removeFlowMod, FlowMod3);
+ FlowMod3.setCommand(OFFlowMod.OFPFC_ADD);
+ FlowMod3.getMatch().fromString("dl_dst=00:20:30:40:50:60,dl_vlan=333");
+ OFFlowMod updateFlowMod = (OFFlowMod) outList.get(1);
+ verifyFlowMod(updateFlowMod, FlowMod3);
+
+ }
+
+
+ IStorageSourceService createStorageWithFlowEntries() {
+ return populateStorageWithFlowEntries(new MemoryStorageSource());
+ }
+
+ IStorageSourceService populateStorageWithFlowEntries(IStorageSourceService storage) {
+ 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 {
+ StaticFlowEntryPusher staticFlowEntryPusher = new StaticFlowEntryPusher();
+ IStorageSourceService storage = createStorageWithFlowEntries();
+ MockFloodlightProvider mfp = getMockFloodlightProvider();
+ staticFlowEntryPusher.setFloodlightProvider(mfp);
+ staticFlowEntryPusher.setStorageSource(storage);
+ RestApiServer restApi = new RestApiServer();
+ try {
+ FloodlightModuleContext fmc = new FloodlightModuleContext();
+ restApi.init(fmc);
+ } catch (FloodlightModuleException e) {
+ e.printStackTrace();
+ }
+ staticFlowEntryPusher.restApi = restApi;
+ staticFlowEntryPusher.startUp(null); // again, to hack unittest
+
+ assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID));
+ assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1));
+ assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2));
+ assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3));
+
+ // Send a notification that we've changed to slave
+ mfp.dispatchRoleChanged(null, Role.SLAVE);
+ // Make sure we've removed all our entries
+ assert(staticFlowEntryPusher.entry2dpid.isEmpty());
+ assert(staticFlowEntryPusher.entriesFromStorage.isEmpty());
+
+ // Send a notification that we've changed to master
+ mfp.dispatchRoleChanged(Role.SLAVE, Role.MASTER);
+ // Make sure we've learned the entries
+ assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID));
+ assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1));
+ assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2));
+ assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3));
+ }
+}