blob: 9060a51a94df883249b215cf2d12a049011b67ff [file] [log] [blame]
Jonathan Hartaa380972014-04-03 10:24:46 -07001package net.onrc.onos.core.intent.runtime;
Ray Milkey6688cd82014-03-11 16:40:46 -07002
3import net.floodlightcontroller.core.module.FloodlightModuleContext;
4import net.floodlightcontroller.core.module.FloodlightModuleException;
Jonathan Hart6df90172014-04-03 10:13:11 -07005import net.onrc.onos.core.datagrid.IDatagridService;
6import net.onrc.onos.core.datagrid.IEventChannel;
7import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hartaa380972014-04-03 10:24:46 -07008import net.onrc.onos.core.intent.Intent;
9import net.onrc.onos.core.intent.IntentMap;
10import net.onrc.onos.core.intent.IntentOperationList;
11import net.onrc.onos.core.intent.MockNetworkGraph;
12import net.onrc.onos.core.intent.ShortestPathIntent;
13import net.onrc.onos.core.intent.Intent.IntentState;
14import net.onrc.onos.core.intent.IntentOperation.Operator;
15import net.onrc.onos.core.intent.runtime.IntentStateList;
16import net.onrc.onos.core.intent.runtime.PathCalcRuntimeModule;
17import net.onrc.onos.core.intent.runtime.PersistIntent;
Jonathan Hart472062d2014-04-03 10:56:48 -070018import net.onrc.onos.core.topology.DeviceEvent;
19import net.onrc.onos.core.topology.INetworkGraphListener;
20import net.onrc.onos.core.topology.INetworkGraphService;
21import net.onrc.onos.core.topology.LinkEvent;
22import net.onrc.onos.core.topology.PortEvent;
23import net.onrc.onos.core.topology.SwitchEvent;
Ray Milkey6688cd82014-03-11 16:40:46 -070024import net.onrc.onos.registry.controller.IControllerRegistryService;
25
26import org.junit.After;
27import org.junit.Before;
28import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.powermock.api.easymock.PowerMock;
31import org.powermock.core.classloader.annotations.PrepareForTest;
32import org.powermock.modules.junit4.PowerMockRunner;
33
34import java.util.Collection;
Ray Milkeydc659c42014-03-28 16:30:42 -070035import java.util.LinkedList;
36import java.util.List;
Ray Milkey6688cd82014-03-11 16:40:46 -070037
Ray Milkeydc659c42014-03-28 16:30:42 -070038import org.hamcrest.Description;
39import org.hamcrest.Factory;
40import org.hamcrest.Matcher;
41import org.hamcrest.Matchers;
42import org.hamcrest.TypeSafeMatcher;
Jonathan Hart6df90172014-04-03 10:13:11 -070043
Ray Milkey6688cd82014-03-11 16:40:46 -070044import static org.easymock.EasyMock.*;
45import static org.hamcrest.MatcherAssert.assertThat;
46import static org.hamcrest.Matchers.*;
47
Ray Milkey6688cd82014-03-11 16:40:46 -070048/**
49 * @author Ray Milkey (ray@onlab.us)
Ray Milkeydc659c42014-03-28 16:30:42 -070050 *
51 * Unit tests for the Path Calculation Runtime module (PathCalcRuntimeModule).
52 * These test cases check the results of creating paths, deleting paths, and
53 * rerouting paths. The network graph, controller registry, and data grid are
54 * mocked out. The individual tests check the high level intents and the
55 * resulting operation lists to be sure they match the intended APIs.
56 *
Ray Milkey6688cd82014-03-11 16:40:46 -070057 */
58@RunWith(PowerMockRunner.class)
59@PrepareForTest(PathCalcRuntimeModule.class)
60public class PathCalcRuntimeModuleTest {
Ray Milkeydc659c42014-03-28 16:30:42 -070061 private static final Long LOCAL_PORT = 0xFFFEL;
62
Ray Milkey6688cd82014-03-11 16:40:46 -070063 private FloodlightModuleContext modContext;
64 private IDatagridService datagridService;
65 private INetworkGraphService networkGraphService;
66 private IControllerRegistryService controllerRegistryService;
67 private PersistIntent persistIntent;
Ray Milkeydc659c42014-03-28 16:30:42 -070068 private MockNetworkGraph graph;
Ray Milkey6688cd82014-03-11 16:40:46 -070069
70 @SuppressWarnings("unchecked")
71 @Before
72 public void setUp() throws Exception {
Ray Milkeydc659c42014-03-28 16:30:42 -070073 graph = new MockNetworkGraph();
Ray Milkey6688cd82014-03-11 16:40:46 -070074 graph.createSampleTopology1();
75
76 datagridService = createMock(IDatagridService.class);
77 networkGraphService = createMock(INetworkGraphService.class);
78 controllerRegistryService = createMock(IControllerRegistryService.class);
79 modContext = createMock(FloodlightModuleContext.class);
80 final IEventChannel eventChannel = createMock(IEventChannel.class);
81 persistIntent = PowerMock.createMock(PersistIntent.class);
82
83 PowerMock.expectNew(PersistIntent.class,
84 anyObject(IControllerRegistryService.class),
85 anyObject(INetworkGraphService.class)).andReturn(persistIntent);
86
87 expect(modContext.getServiceImpl(IDatagridService.class))
88 .andReturn(datagridService).once();
89 expect(modContext.getServiceImpl(INetworkGraphService.class))
90 .andReturn(networkGraphService).once();
91 expect(modContext.getServiceImpl(IControllerRegistryService.class))
92 .andReturn(controllerRegistryService).once();
93 expect(persistIntent.getKey()).andReturn(1L).anyTimes();
94 expect(persistIntent.persistIfLeader(eq(1L),
95 anyObject(IntentOperationList.class))).andReturn(true)
96 .anyTimes();
97
98 expect(networkGraphService.getNetworkGraph()).andReturn(graph)
99 .anyTimes();
100 networkGraphService.registerNetworkGraphListener(
101 anyObject(INetworkGraphListener.class));
102 expectLastCall();
103
104 expect(datagridService.createChannel("onos.pathintent",
105 Long.class, IntentOperationList.class))
106 .andReturn(eventChannel).once();
107
108 expect(datagridService.addListener(
109 eq("onos.pathintent_state"),
110 anyObject(IEventChannelListener.class),
111 eq(Long.class),
112 eq(IntentStateList.class)))
113 .andReturn(eventChannel).once();
114
115 replay(datagridService);
116 replay(networkGraphService);
117 replay(modContext);
118 replay(controllerRegistryService);
119 PowerMock.replay(persistIntent, PersistIntent.class);
120 }
121
Ray Milkeydc659c42014-03-28 16:30:42 -0700122
123 /**
124 * Hamcrest matcher to check that a collection of Intents contains an
125 * Intent with the specified Intent Id.
126 */
127 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
128 final private String id;
129
130 public EntryForIntentMatcher(String idValue) {
131 id = idValue;
132 }
133
134 @Override
135 public boolean matchesSafely(Collection<Intent> intents) {
136 assertThat(intents,
137 hasItem(Matchers.<Intent>hasProperty("id", equalTo(id))));
138 return true;
139 }
140
141 @Override
142 public void describeTo(Description description) {
143 description.appendText("an intent with id \" ").
144 appendText(id).
145 appendText("\"");
146 }
147 }
148
149
150 /**
151 * Factory method to create an Intent entry Matcher. Returns a matcher
152 * for the Intent with the given id.
153 * @param id id of the intent to match
154 * @return Matcher object
155 */
156 @Factory
157 public static Matcher<Collection<Intent>> hasIntentWithId(String id) {
158 return new EntryForIntentMatcher(id);
159 }
160
161
162 /**
163 * Matcher to determine if an IntentMap contains an entry with a given id,
164 * and that entry has a given state.
165 */
166 public static class IntentsHaveIntentWithStateMatcher extends TypeSafeMatcher<IntentMap> {
167 final private String id;
168 final private IntentState state;
169 private Intent intent;
170
171 public IntentsHaveIntentWithStateMatcher(String idValue,
172 IntentState stateValue) {
173 id = idValue;
174 state = stateValue;
175 }
176
177 @Override
178 public boolean matchesSafely(IntentMap intents) {
179 intent = intents.getIntent(id);
180
181 return intent != null && intent.getState() == state;
182 }
183
184 @Override
185 public void describeTo(Description description) {
186 if (intent == null) {
187 description.appendText("intent lookup for id \"");
188 description.appendText(id);
189 description.appendText("\"");
190 } else {
191 description.appendText("state ");
192 description.appendText(state.toString());
193 }
194 }
195
196 @Override
197 public void describeMismatchSafely(IntentMap intents,
198 Description mismatchDescription) {
199 if (intent != null) {
200 mismatchDescription.appendText("was ").
201 appendText(intent.getState().toString());
202 } else {
203 mismatchDescription.appendText("that intent was not found");
204 }
205 }
206 }
207
208
209 /**
210 * Factory method to create a Matcher for an IntentMap that looks for an
211 * Intent with a given id and state.
212 *
213 * @param id id of the Intent to match
214 * @param state if the Intent is found, its state must match this
215 * @return Matcher object
216 */
217 @Factory
218 public static Matcher<IntentMap> hasIntentWithIdAndState(String id,
219 IntentState state) {
220 return new IntentsHaveIntentWithStateMatcher(id, state);
221 }
222
223
Ray Milkey6688cd82014-03-11 16:40:46 -0700224 @After
225 public void tearDown() {
226 verify(datagridService);
227 verify(networkGraphService);
228 verify(modContext);
229 verify(controllerRegistryService);
230 PowerMock.verify(persistIntent, PersistIntent.class);
231 }
232
233 /**
234 * Test the result of executing a path calculation on an
235 * Intent Operation List which contains a path that references a
236 * non-existent switch.
Ray Milkeydc659c42014-03-28 16:30:42 -0700237 *
Ray Milkey6688cd82014-03-11 16:40:46 -0700238 * A 3 path list is created where one of the paths references a switch
239 * that is not in the topology. The test checks that the resulting
240 * Operation List has entries for the 2 correct paths, and that the
241 * high level intents contain a proper error entry for the bad path.
242 */
243 @Test
244 public void testInvalidSwitchName() throws FloodlightModuleException {
Ray Milkey6688cd82014-03-11 16:40:46 -0700245 final String BAD_SWITCH_INTENT_NAME = "No Such Switch Intent";
246
247 // create shortest path intents
248 final IntentOperationList opList = new IntentOperationList();
249 opList.add(Operator.ADD,
250 new ShortestPathIntent(BAD_SWITCH_INTENT_NAME, 111L, 12L,
251 LOCAL_PORT, 2L, 21L, LOCAL_PORT));
252 opList.add(Operator.ADD,
253 new ShortestPathIntent("2", 1L, 14L, LOCAL_PORT, 4L, 41L,
254 LOCAL_PORT));
255 opList.add(Operator.ADD,
256 new ShortestPathIntent("3", 2L, 23L, LOCAL_PORT, 3L, 32L,
257 LOCAL_PORT));
258
259 // compile high-level intent operations into low-level intent
260 // operations (calculate paths)
261 final PathCalcRuntimeModule runtime = new PathCalcRuntimeModule();
262 runtime.init(modContext);
263 runtime.startUp(modContext);
264 final IntentOperationList pathIntentOpList =
265 runtime.executeIntentOperations(opList);
266 assertThat(pathIntentOpList, notNullValue());
267
268 final IntentMap highLevelIntents = runtime.getHighLevelIntents();
269 assertThat(highLevelIntents, notNullValue());
270
271 final Collection<Intent> allIntents = highLevelIntents.getAllIntents();
272 assertThat(allIntents, notNullValue());
273
274 // One intent had an error and should not create a path list entry
275 assertThat(pathIntentOpList, hasSize(opList.size() - 1));
276
277 // Should be a high level intent for each operation
Ray Milkeydc659c42014-03-28 16:30:42 -0700278 assertThat(allIntents, hasSize(opList.size()));
Ray Milkey6688cd82014-03-11 16:40:46 -0700279
280 // Check that we got a high level intent for each operation
Ray Milkeydc659c42014-03-28 16:30:42 -0700281 assertThat(allIntents, hasIntentWithId("3"));
282 assertThat(allIntents, hasIntentWithId("2"));
283 assertThat(allIntents, hasIntentWithId(BAD_SWITCH_INTENT_NAME));
Ray Milkey6688cd82014-03-11 16:40:46 -0700284
285 // Check that the non existent switch was NACKed
Ray Milkeydc659c42014-03-28 16:30:42 -0700286 assertThat(highLevelIntents, hasIntentWithIdAndState(BAD_SWITCH_INTENT_NAME, IntentState.INST_NACK));
Ray Milkey6688cd82014-03-11 16:40:46 -0700287
288 // Check that switch 2 was correctly processed
Ray Milkeydc659c42014-03-28 16:30:42 -0700289 assertThat(highLevelIntents, hasIntentWithIdAndState("2", IntentState.INST_REQ));
Ray Milkey6688cd82014-03-11 16:40:46 -0700290
291 // Check that switch 3 was correctly processed
Ray Milkeydc659c42014-03-28 16:30:42 -0700292 assertThat(highLevelIntents, hasIntentWithIdAndState("3", IntentState.INST_REQ));
293
294 }
295
296
297 /**
298 * Test the result of executing a path calculation on an
299 * Intent Operation List and then removing one of the switches.
300 *
301 * A 3 path list is created and then one of the paths is removed.
302 * The test checks that the resulting Operation List is correct,
303 * and that the high level intents contain a proper "delete requested"
304 * entry for the deleted path.
305 */
306 @Test
307 public void testIntentRemoval() throws FloodlightModuleException {
308
309 // create shortest path intents
310 final IntentOperationList opList = new IntentOperationList();
311 opList.add(Operator.ADD,
312 new ShortestPathIntent("1", 1L, 12L, LOCAL_PORT, 2L, 21L,
313 LOCAL_PORT));
314 opList.add(Operator.ADD,
315 new ShortestPathIntent("2", 1L, 14L, LOCAL_PORT, 4L, 41L,
316 LOCAL_PORT));
317 opList.add(Operator.ADD,
318 new ShortestPathIntent("3", 2L, 23L, LOCAL_PORT, 3L, 32L,
319 LOCAL_PORT));
320
321 // compile high-level intent operations into low-level intent
322 // operations (calculate paths)
323 final PathCalcRuntimeModule runtime = new PathCalcRuntimeModule();
324 runtime.init(modContext);
325 runtime.startUp(modContext);
326 final IntentOperationList pathIntentOpList =
327 runtime.executeIntentOperations(opList);
328 assertThat(pathIntentOpList, notNullValue());
329
330 final IntentMap highLevelIntents = runtime.getHighLevelIntents();
331 assertThat(highLevelIntents, notNullValue());
332
333 final Collection<Intent> allIntents = highLevelIntents.getAllIntents();
334 assertThat(allIntents, notNullValue());
335
336 // Should be one operation per path
337 assertThat(pathIntentOpList, hasSize(opList.size()));
338
339 // Should be a high level intent for each operation
340 assertThat(allIntents, hasSize(opList.size()));
341
342 // Check that we got a high level intent for each operation
343 assertThat(allIntents, hasIntentWithId("3"));
344 assertThat(allIntents, hasIntentWithId("2"));
345 assertThat(allIntents, hasIntentWithId("1"));
346
347 // Check that switch 1 was correctly processed
348 assertThat(highLevelIntents,
349 hasIntentWithIdAndState("1", IntentState.INST_REQ));
350
351 // Check that switch 2 was correctly processed
352 assertThat(highLevelIntents,
353 hasIntentWithIdAndState("2", IntentState.INST_REQ));
354
355 // Check that switch 3 was correctly processed
356 assertThat(highLevelIntents,
357 hasIntentWithIdAndState("3", IntentState.INST_REQ));
358
359 // Now delete one path and check the results
360 final IntentOperationList opListForRemoval = new IntentOperationList();
361 opListForRemoval.add(Operator.REMOVE,
362 new ShortestPathIntent("1", 1L, 12L, LOCAL_PORT, 2L, 21L,
363 LOCAL_PORT));
364
365 final IntentOperationList pathIntentOpListAfterRemoval =
366 runtime.executeIntentOperations(opListForRemoval);
367 assertThat(pathIntentOpListAfterRemoval, notNullValue());
368 assertThat(pathIntentOpListAfterRemoval, hasSize(1));
369
370 // Check the high level intents.
371 final IntentMap highLevelIntentsAfterRemoval = runtime.getHighLevelIntents();
372 assertThat(highLevelIntentsAfterRemoval, notNullValue());
373
374 final Collection<Intent> allIntentsAfterRemoval = highLevelIntentsAfterRemoval.getAllIntents();
375 assertThat(allIntentsAfterRemoval, notNullValue());
376 assertThat(allIntentsAfterRemoval, hasSize(3));
377
378 // Check that we got a high level intent for each operation
379 assertThat(allIntentsAfterRemoval, hasIntentWithId("3"));
380 assertThat(allIntentsAfterRemoval, hasIntentWithId("2"));
381 assertThat(allIntentsAfterRemoval, hasIntentWithId("1"));
382
383 // Check the states of the high level intents
384 // Check that switch 1 was correctly processed
385 assertThat(highLevelIntents,
386 hasIntentWithIdAndState("1", IntentState.DEL_REQ));
387
388 // Check that switch 2 was correctly processed
389 assertThat(highLevelIntents,
390 hasIntentWithIdAndState("2", IntentState.INST_REQ));
391
392 // Check that switch 3 was correctly processed
393 assertThat(highLevelIntents,
394 hasIntentWithIdAndState("3", IntentState.INST_REQ));
395 }
396
397 /**
398 * Test the result of executing a path calculation on an
399 * Intent Operation List and then forcing a reroute.
400 *
401 * A 3 path list is created and then one of the links is removed.
402 * The test checks that the resulting Operation List is correct,
403 * and that the high level intents contain a proper "reroute requested"
404 * entry for the deleted link.
405 */
406 @Test
407 public void testIntentReroute() throws FloodlightModuleException {
408
409 // create shortest path intents
410 final IntentOperationList opList = new IntentOperationList();
411 final ShortestPathIntent pathIntent1 =
412 new ShortestPathIntent("1", 1L, 12L, LOCAL_PORT, 2L, 21L,
413 LOCAL_PORT);
414
415 opList.add(Operator.ADD, pathIntent1);
416 opList.add(Operator.ADD,
417 new ShortestPathIntent("2", 1L, 14L, LOCAL_PORT, 4L, 41L,
418 LOCAL_PORT));
419 opList.add(Operator.ADD,
420 new ShortestPathIntent("3", 2L, 23L, LOCAL_PORT, 3L, 32L,
421 LOCAL_PORT));
422
423 // compile high-level intent operations into low-level intent
424 // operations (calculate paths)
425 final PathCalcRuntimeModule runtime = new PathCalcRuntimeModule();
426 runtime.init(modContext);
427 runtime.startUp(modContext);
428 final IntentOperationList pathIntentOpList =
429 runtime.executeIntentOperations(opList);
430 assertThat(pathIntentOpList, notNullValue());
431
432 final IntentMap highLevelIntents = runtime.getHighLevelIntents();
433 assertThat(highLevelIntents, notNullValue());
434
435 final Collection<Intent> allIntents = highLevelIntents.getAllIntents();
436 assertThat(allIntents, notNullValue());
437
438 // Should be one operation per path
439 assertThat(pathIntentOpList, hasSize(opList.size()));
440
441 // Should be a high level intent for each operation
442 assertThat(allIntents, hasSize(opList.size()));
443
444 // Check that we got a high level intent for each operation
445 assertThat(allIntents, hasIntentWithId("3"));
446 assertThat(allIntents, hasIntentWithId("2"));
447 assertThat(allIntents, hasIntentWithId("1"));
448
449 // Check that switch 1 was correctly processed
450 assertThat(highLevelIntents,
451 hasIntentWithIdAndState("1", IntentState.INST_REQ));
452
453 // Check that switch 2 was correctly processed
454 assertThat(highLevelIntents,
455 hasIntentWithIdAndState("2", IntentState.INST_REQ));
456
457 // Check that switch 3 was correctly processed
458 assertThat(highLevelIntents,
459 hasIntentWithIdAndState("3", IntentState.INST_REQ));
460
461 // Now add a different path to one of the switches path and check
462 // the results
463 IntentStateList states = new IntentStateList();
464 states.put("1", IntentState.INST_ACK);
465 states.put("2", IntentState.INST_ACK);
466 states.put("3", IntentState.INST_ACK);
467 runtime.getHighLevelIntents().changeStates(states);
468 states.clear();
469 states.put("1___0", IntentState.INST_ACK);
470 states.put("2___0", IntentState.INST_ACK);
471 states.put("3___0", IntentState.INST_ACK);
472 runtime.getPathIntents().changeStates(states);
473
474 final List<SwitchEvent> emptySwitchEvents = new LinkedList<>();
475 final List<PortEvent> emptyPortEvents = new LinkedList<>();
476 final List<DeviceEvent> emptyDeviceEvents = new LinkedList<>();
477 final List<LinkEvent> addedLinkEvents = new LinkedList<>();
478 final List<LinkEvent> removedLinkEvents = new LinkedList<>();
479
480 graph.removeLink(1L, 12L, 2L, 21L); // This link is used by the intent "1"
481 graph.removeLink(2L, 21L, 1L, 12L);
482 LinkEvent linkEvent1 = new LinkEvent(1L, 12L, 2L, 21L);
483 LinkEvent linkEvent2 = new LinkEvent(2L, 21L, 1L, 12L);
484 removedLinkEvents.add(linkEvent1);
485 removedLinkEvents.add(linkEvent2);
486 runtime.networkGraphEvents(
487 emptySwitchEvents,
488 emptySwitchEvents,
489 emptyPortEvents,
490 emptyPortEvents,
491 addedLinkEvents,
492 removedLinkEvents,
493 emptyDeviceEvents,
494 emptyDeviceEvents);
495 final IntentOperationList opListForReroute = new IntentOperationList();
496 opListForReroute.add(Operator.ADD, pathIntent1);
497
498 final IntentOperationList pathIntentOpListAfterReroute =
499 runtime.executeIntentOperations(opListForReroute);
500 assertThat(pathIntentOpListAfterReroute, notNullValue());
501 assertThat(pathIntentOpListAfterReroute, hasSize(2));
502
503 // Check the high level intents.
504 final IntentMap highLevelIntentsAfterReroute = runtime.getHighLevelIntents();
505 assertThat(highLevelIntentsAfterReroute, notNullValue());
506
507 final Collection<Intent> allIntentsAfterReroute = highLevelIntentsAfterReroute.getAllIntents();
508 assertThat(allIntentsAfterReroute, notNullValue());
509 assertThat(allIntentsAfterReroute, hasSize(3));
510
511 // Check that we got a high level intent for each operation
512 assertThat(allIntentsAfterReroute, hasIntentWithId("3"));
513 assertThat(allIntentsAfterReroute, hasIntentWithId("2"));
514 assertThat(allIntentsAfterReroute, hasIntentWithId("1"));
515
516 // Check the states of the high level intents
517 // Check that switch 1 was correctly processed
518 assertThat(highLevelIntents,
519 hasIntentWithIdAndState("1", IntentState.REROUTE_REQ));
520
521 // Check that switch 2 was correctly processed
522 assertThat(highLevelIntents,
523 hasIntentWithIdAndState("2", IntentState.INST_ACK));
524
525 // Check that switch 3 was correctly processed
526 assertThat(highLevelIntents,
527 hasIntentWithIdAndState("3", IntentState.INST_ACK));
528
Ray Milkey6688cd82014-03-11 16:40:46 -0700529
530 }
531
532
533}