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));
+    }
+}