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