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