Umesh Krishnaswamy | 345ee99 | 2012-12-13 20:29:48 -0800 | [diff] [blame] | 1 | package net.floodlightcontroller.staticflowentry; |
| 2 | |
Umesh Krishnaswamy | 345ee99 | 2012-12-13 20:29:48 -0800 | [diff] [blame] | 3 | |
Jonathan Hart | 2fa2806 | 2013-11-25 20:16:28 -0800 | [diff] [blame] | 4 | /* |
Umesh Krishnaswamy | 345ee99 | 2012-12-13 20:29:48 -0800 | [diff] [blame] | 5 | public class StaticFlowTests extends FloodlightTestCase { |
| 6 | |
| 7 | static String TestSwitch1DPID = "00:00:00:00:00:00:00:01"; |
| 8 | static int TotalTestRules = 3; |
| 9 | |
Jonathan Hart | 2fa2806 | 2013-11-25 20:16:28 -0800 | [diff] [blame] | 10 | // |
| 11 | // Create TestRuleXXX and the corresponding FlowModXXX |
| 12 | // for X = 1..3 |
| 13 | // |
Umesh Krishnaswamy | 345ee99 | 2012-12-13 20:29:48 -0800 | [diff] [blame] | 14 | static Map<String,Object> TestRule1; |
| 15 | static OFFlowMod FlowMod1; |
| 16 | static { |
| 17 | FlowMod1 = new OFFlowMod(); |
| 18 | TestRule1 = new HashMap<String,Object>(); |
| 19 | TestRule1.put(COLUMN_NAME, "TestRule1"); |
| 20 | TestRule1.put(COLUMN_SWITCH, TestSwitch1DPID); |
| 21 | // setup match |
| 22 | OFMatch match = new OFMatch(); |
| 23 | TestRule1.put(COLUMN_DL_DST, "00:20:30:40:50:60"); |
| 24 | match.fromString("dl_dst=00:20:30:40:50:60"); |
| 25 | // setup actions |
| 26 | List<OFAction> actions = new LinkedList<OFAction>(); |
| 27 | TestRule1.put(COLUMN_ACTIONS, "output=1"); |
| 28 | actions.add(new OFActionOutput((short)1, (short) Short.MAX_VALUE)); |
| 29 | // done |
| 30 | FlowMod1.setMatch(match); |
| 31 | FlowMod1.setActions(actions); |
| 32 | FlowMod1.setBufferId(-1); |
| 33 | FlowMod1.setOutPort(OFPort.OFPP_NONE.getValue()); |
| 34 | FlowMod1.setPriority(Short.MAX_VALUE); |
| 35 | FlowMod1.setLengthU(OFFlowMod.MINIMUM_LENGTH + 8); // 8 bytes of actions |
| 36 | } |
| 37 | |
| 38 | static Map<String,Object> TestRule2; |
| 39 | static OFFlowMod FlowMod2; |
| 40 | |
| 41 | static { |
| 42 | FlowMod2 = new OFFlowMod(); |
| 43 | TestRule2 = new HashMap<String,Object>(); |
| 44 | TestRule2.put(COLUMN_NAME, "TestRule2"); |
| 45 | TestRule2.put(COLUMN_SWITCH, TestSwitch1DPID); |
| 46 | // setup match |
| 47 | OFMatch match = new OFMatch(); |
| 48 | TestRule2.put(COLUMN_NW_DST, "192.168.1.0/24"); |
| 49 | match.fromString("nw_dst=192.168.1.0/24"); |
| 50 | // setup actions |
| 51 | List<OFAction> actions = new LinkedList<OFAction>(); |
| 52 | TestRule2.put(COLUMN_ACTIONS, "output=1"); |
| 53 | actions.add(new OFActionOutput((short)1, (short) Short.MAX_VALUE)); |
| 54 | // done |
| 55 | FlowMod2.setMatch(match); |
| 56 | FlowMod2.setActions(actions); |
| 57 | FlowMod2.setBufferId(-1); |
| 58 | FlowMod2.setOutPort(OFPort.OFPP_NONE.getValue()); |
| 59 | FlowMod2.setPriority(Short.MAX_VALUE); |
| 60 | FlowMod2.setLengthU(OFFlowMod.MINIMUM_LENGTH + 8); // 8 bytes of actions |
| 61 | |
| 62 | } |
| 63 | |
| 64 | |
| 65 | static Map<String,Object> TestRule3; |
| 66 | static OFFlowMod FlowMod3; |
| 67 | static { |
| 68 | FlowMod3 = new OFFlowMod(); |
| 69 | TestRule3 = new HashMap<String,Object>(); |
| 70 | TestRule3.put(COLUMN_NAME, "TestRule3"); |
| 71 | TestRule3.put(COLUMN_SWITCH, TestSwitch1DPID); |
| 72 | // setup match |
| 73 | OFMatch match = new OFMatch(); |
| 74 | TestRule3.put(COLUMN_DL_DST, "00:20:30:40:50:60"); |
| 75 | TestRule3.put(COLUMN_DL_VLAN, 4096); |
| 76 | match.fromString("dl_dst=00:20:30:40:50:60,dl_vlan=4096"); |
| 77 | // setup actions |
| 78 | TestRule3.put(COLUMN_ACTIONS, "output=controller"); |
| 79 | List<OFAction> actions = new LinkedList<OFAction>(); |
| 80 | actions.add(new OFActionOutput(OFPort.OFPP_CONTROLLER.getValue(), (short) Short.MAX_VALUE)); |
| 81 | // done |
| 82 | FlowMod3.setMatch(match); |
| 83 | FlowMod3.setActions(actions); |
| 84 | FlowMod3.setBufferId(-1); |
| 85 | FlowMod3.setOutPort(OFPort.OFPP_NONE.getValue()); |
| 86 | FlowMod3.setPriority(Short.MAX_VALUE); |
| 87 | FlowMod3.setLengthU(OFFlowMod.MINIMUM_LENGTH + 8); // 8 bytes of actions |
| 88 | |
| 89 | } |
| 90 | |
| 91 | private void verifyFlowMod(OFFlowMod testFlowMod, |
| 92 | OFFlowMod goodFlowMod) { |
| 93 | verifyMatch(testFlowMod, goodFlowMod); |
| 94 | verifyActions(testFlowMod, goodFlowMod); |
| 95 | // dont' bother testing the cookie; just copy it over |
| 96 | goodFlowMod.setCookie(testFlowMod.getCookie()); |
| 97 | // .. so we can continue to use .equals() |
| 98 | assertEquals(goodFlowMod, testFlowMod); |
| 99 | } |
| 100 | |
| 101 | |
| 102 | private void verifyMatch(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { |
| 103 | assertEquals(goodFlowMod.getMatch(), testFlowMod.getMatch()); |
| 104 | } |
| 105 | |
| 106 | |
| 107 | private void verifyActions(OFFlowMod testFlowMod, OFFlowMod goodFlowMod) { |
| 108 | List<OFAction> goodActions = goodFlowMod.getActions(); |
| 109 | List<OFAction> testActions = testFlowMod.getActions(); |
| 110 | assertNotNull(goodActions); |
| 111 | assertNotNull(testActions); |
| 112 | assertEquals(goodActions.size(), testActions.size()); |
| 113 | // assumes actions are marshalled in same order; should be safe |
| 114 | for(int i = 0; i < goodActions.size(); i++) { |
| 115 | assertEquals(goodActions.get(i), testActions.get(i)); |
| 116 | } |
| 117 | |
| 118 | } |
| 119 | |
| 120 | |
| 121 | @Override |
| 122 | public void setUp() throws Exception { |
| 123 | super.setUp(); |
| 124 | } |
| 125 | |
| 126 | @Test |
| 127 | public void testStaticFlowPush() throws IOException { |
| 128 | StaticFlowEntryPusher staticFlowEntryPusher = new StaticFlowEntryPusher(); |
| 129 | IStorageSourceService storage = createStorageWithFlowEntries(); |
| 130 | long dpid = HexString.toLong(TestSwitch1DPID); |
| 131 | |
| 132 | // Create a Switch and attach a switch |
| 133 | IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class); |
| 134 | Capture<OFMessage> writeCapture = new Capture<OFMessage>(CaptureType.ALL); |
| 135 | Capture<FloodlightContext> contextCapture = new Capture<FloodlightContext>(CaptureType.ALL); |
| 136 | Capture<List<OFMessage>> writeCaptureList = new Capture<List<OFMessage>>(CaptureType.ALL); |
| 137 | |
| 138 | //OFMessageSafeOutStream mockOutStream = createNiceMock(OFMessageSafeOutStream.class); |
| 139 | mockSwitch.write(capture(writeCapture), capture(contextCapture)); |
| 140 | expectLastCall().anyTimes(); |
| 141 | mockSwitch.write(capture(writeCaptureList), capture(contextCapture)); |
| 142 | expectLastCall().anyTimes(); |
| 143 | mockSwitch.flush(); |
| 144 | expectLastCall().anyTimes(); |
| 145 | |
| 146 | staticFlowEntryPusher.setStorageSource(storage); |
| 147 | |
| 148 | FloodlightModuleContext fmc = new FloodlightModuleContext(); |
| 149 | |
| 150 | MockFloodlightProvider mockFloodlightProvider = getMockFloodlightProvider(); |
| 151 | Map<Long, IOFSwitch> switchMap = new HashMap<Long, IOFSwitch>(); |
| 152 | switchMap.put(dpid, mockSwitch); |
| 153 | // NO ! expect(mockFloodlightProvider.getSwitches()).andReturn(switchMap).anyTimes(); |
| 154 | mockFloodlightProvider.setSwitches(switchMap); |
| 155 | staticFlowEntryPusher.setFloodlightProvider(mockFloodlightProvider); |
| 156 | RestApiServer restApi = new RestApiServer(); |
| 157 | try { |
| 158 | restApi.init(fmc); |
| 159 | } catch (FloodlightModuleException e) { |
| 160 | e.printStackTrace(); |
| 161 | } |
| 162 | staticFlowEntryPusher.restApi = restApi; |
| 163 | staticFlowEntryPusher.startUp(null); // again, to hack unittest |
| 164 | |
| 165 | // verify that flowpusher read all three entries from storage |
| 166 | assertEquals(TotalTestRules, staticFlowEntryPusher.countEntries()); |
| 167 | |
| 168 | // if someone calls mockSwitch.getOutputStream(), return mockOutStream instead |
| 169 | //expect(mockSwitch.getOutputStream()).andReturn(mockOutStream).anyTimes(); |
| 170 | |
| 171 | // if someone calls getId(), return this dpid instead |
| 172 | expect(mockSwitch.getId()).andReturn(dpid).anyTimes(); |
| 173 | expect(mockSwitch.getStringId()).andReturn(TestSwitch1DPID).anyTimes(); |
| 174 | replay(mockSwitch); |
| 175 | |
| 176 | // hook the static pusher up to the fake switch |
| 177 | staticFlowEntryPusher.addedSwitch(mockSwitch); |
| 178 | |
| 179 | verify(mockSwitch); |
| 180 | |
| 181 | // Verify that the switch has gotten some flow_mods |
| 182 | assertEquals(true, writeCapture.hasCaptured()); |
| 183 | assertEquals(TotalTestRules, writeCapture.getValues().size()); |
| 184 | |
| 185 | // Order assumes how things are stored in hash bucket; |
| 186 | // should be fixed because OFMessage.hashCode() is deterministic |
| 187 | OFFlowMod firstFlowMod = (OFFlowMod) writeCapture.getValues().get(2); |
| 188 | verifyFlowMod(firstFlowMod, FlowMod1); |
| 189 | OFFlowMod secondFlowMod = (OFFlowMod) writeCapture.getValues().get(1); |
| 190 | verifyFlowMod(secondFlowMod, FlowMod2); |
| 191 | OFFlowMod thirdFlowMod = (OFFlowMod) writeCapture.getValues().get(0); |
| 192 | verifyFlowMod(thirdFlowMod, FlowMod3); |
| 193 | |
| 194 | writeCapture.reset(); |
| 195 | contextCapture.reset(); |
| 196 | |
| 197 | |
| 198 | // delete two rules and verify they've been removed |
| 199 | // this should invoke staticFlowPusher.rowsDeleted() |
| 200 | storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule1"); |
| 201 | storage.deleteRow(StaticFlowEntryPusher.TABLE_NAME, "TestRule2"); |
| 202 | |
| 203 | assertEquals(1, staticFlowEntryPusher.countEntries()); |
| 204 | assertEquals(2, writeCapture.getValues().size()); |
| 205 | |
| 206 | OFFlowMod firstDelete = (OFFlowMod) writeCapture.getValues().get(0); |
| 207 | FlowMod1.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); |
| 208 | verifyFlowMod(firstDelete, FlowMod1); |
| 209 | |
| 210 | OFFlowMod secondDelete = (OFFlowMod) writeCapture.getValues().get(1); |
| 211 | FlowMod2.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); |
| 212 | verifyFlowMod(secondDelete, FlowMod2); |
| 213 | |
| 214 | // add rules back to make sure that staticFlowPusher.rowsInserted() works |
| 215 | writeCapture.reset(); |
| 216 | FlowMod2.setCommand(OFFlowMod.OFPFC_ADD); |
| 217 | storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2); |
| 218 | assertEquals(2, staticFlowEntryPusher.countEntries()); |
| 219 | assertEquals(1, writeCaptureList.getValues().size()); |
| 220 | List<OFMessage> outList = |
| 221 | (List<OFMessage>) writeCaptureList.getValues().get(0); |
| 222 | assertEquals(1, outList.size()); |
| 223 | OFFlowMod firstAdd = (OFFlowMod) outList.get(0); |
| 224 | verifyFlowMod(firstAdd, FlowMod2); |
| 225 | writeCapture.reset(); |
| 226 | contextCapture.reset(); |
| 227 | writeCaptureList.reset(); |
| 228 | |
| 229 | // now try an update, calling staticFlowPusher.rowUpdated() |
| 230 | TestRule3.put(COLUMN_DL_VLAN, 333); |
| 231 | storage.updateRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); |
| 232 | assertEquals(2, staticFlowEntryPusher.countEntries()); |
| 233 | assertEquals(1, writeCaptureList.getValues().size()); |
| 234 | |
| 235 | outList = (List<OFMessage>) writeCaptureList.getValues().get(0); |
| 236 | assertEquals(2, outList.size()); |
| 237 | OFFlowMod removeFlowMod = (OFFlowMod) outList.get(0); |
| 238 | FlowMod3.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); |
| 239 | verifyFlowMod(removeFlowMod, FlowMod3); |
| 240 | FlowMod3.setCommand(OFFlowMod.OFPFC_ADD); |
| 241 | FlowMod3.getMatch().fromString("dl_dst=00:20:30:40:50:60,dl_vlan=333"); |
| 242 | OFFlowMod updateFlowMod = (OFFlowMod) outList.get(1); |
| 243 | verifyFlowMod(updateFlowMod, FlowMod3); |
| 244 | |
| 245 | } |
| 246 | |
| 247 | |
| 248 | IStorageSourceService createStorageWithFlowEntries() { |
| 249 | return populateStorageWithFlowEntries(new MemoryStorageSource()); |
| 250 | } |
| 251 | |
| 252 | IStorageSourceService populateStorageWithFlowEntries(IStorageSourceService storage) { |
| 253 | Set<String> indexedColumns = new HashSet<String>(); |
| 254 | indexedColumns.add(COLUMN_NAME); |
| 255 | storage.createTable(StaticFlowEntryPusher.TABLE_NAME, indexedColumns); |
| 256 | storage.setTablePrimaryKeyName(StaticFlowEntryPusher.TABLE_NAME, COLUMN_NAME); |
| 257 | |
| 258 | storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule1); |
| 259 | storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule2); |
| 260 | storage.insertRow(StaticFlowEntryPusher.TABLE_NAME, TestRule3); |
| 261 | |
| 262 | return storage; |
| 263 | } |
| 264 | |
| 265 | @Test |
| 266 | public void testHARoleChanged() throws IOException { |
| 267 | StaticFlowEntryPusher staticFlowEntryPusher = new StaticFlowEntryPusher(); |
| 268 | IStorageSourceService storage = createStorageWithFlowEntries(); |
| 269 | MockFloodlightProvider mfp = getMockFloodlightProvider(); |
| 270 | staticFlowEntryPusher.setFloodlightProvider(mfp); |
| 271 | staticFlowEntryPusher.setStorageSource(storage); |
| 272 | RestApiServer restApi = new RestApiServer(); |
| 273 | try { |
| 274 | FloodlightModuleContext fmc = new FloodlightModuleContext(); |
| 275 | restApi.init(fmc); |
| 276 | } catch (FloodlightModuleException e) { |
| 277 | e.printStackTrace(); |
| 278 | } |
| 279 | staticFlowEntryPusher.restApi = restApi; |
| 280 | staticFlowEntryPusher.startUp(null); // again, to hack unittest |
| 281 | |
| 282 | assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID)); |
| 283 | assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); |
| 284 | assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); |
| 285 | assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); |
| 286 | |
| 287 | // Send a notification that we've changed to slave |
| 288 | mfp.dispatchRoleChanged(null, Role.SLAVE); |
| 289 | // Make sure we've removed all our entries |
| 290 | assert(staticFlowEntryPusher.entry2dpid.isEmpty()); |
| 291 | assert(staticFlowEntryPusher.entriesFromStorage.isEmpty()); |
| 292 | |
| 293 | // Send a notification that we've changed to master |
| 294 | mfp.dispatchRoleChanged(Role.SLAVE, Role.MASTER); |
| 295 | // Make sure we've learned the entries |
| 296 | assert(staticFlowEntryPusher.entry2dpid.containsValue(TestSwitch1DPID)); |
| 297 | assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod1)); |
| 298 | assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod2)); |
| 299 | assert(staticFlowEntryPusher.entriesFromStorage.containsValue(FlowMod3)); |
| 300 | } |
| 301 | } |
Jonathan Hart | 2fa2806 | 2013-11-25 20:16:28 -0800 | [diff] [blame] | 302 | */ |