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