blob: 16cb357d9129f0c146fa35193d64091f4a65cf86 [file] [log] [blame]
Brian O'Connorabafb502014-12-02 22:26:20 -08001package org.onosproject.net.intent.impl;
Brian O'Connor427a1762014-11-19 18:40:32 -08002
Ray Milkeye9a3e222014-12-03 16:46:06 -08003import java.util.Collection;
4import java.util.List;
5import java.util.Map;
6import java.util.Set;
7import java.util.concurrent.CountDownLatch;
8import java.util.concurrent.TimeUnit;
9import java.util.concurrent.atomic.AtomicLong;
alshabiba9819bf2014-11-30 18:15:52 -080010
Brian O'Connor427a1762014-11-19 18:40:32 -080011import org.hamcrest.Description;
Brian O'Connor427a1762014-11-19 18:40:32 -080012import org.hamcrest.TypeSafeMatcher;
13import org.junit.After;
14import org.junit.Before;
15import org.junit.Test;
Brian O'Connorabafb502014-12-02 22:26:20 -080016import org.onosproject.TestApplicationId;
17import org.onosproject.core.ApplicationId;
18import org.onosproject.core.impl.TestCoreManager;
19import org.onosproject.event.impl.TestEventDispatcher;
20import org.onosproject.net.NetworkResource;
21import org.onosproject.net.flow.FlowRule;
22import org.onosproject.net.flow.FlowRuleBatchEntry;
23import org.onosproject.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
24import org.onosproject.net.flow.FlowRuleBatchOperation;
25import org.onosproject.net.intent.Intent;
26import org.onosproject.net.intent.IntentCompiler;
27import org.onosproject.net.intent.IntentEvent;
28import org.onosproject.net.intent.IntentEvent.Type;
29import org.onosproject.net.intent.IntentExtensionService;
30import org.onosproject.net.intent.IntentId;
31import org.onosproject.net.intent.IntentInstaller;
32import org.onosproject.net.intent.IntentListener;
33import org.onosproject.net.intent.IntentService;
34import org.onosproject.net.intent.IntentState;
35import org.onosproject.net.intent.IntentTestsMocks;
36import org.onosproject.net.resource.LinkResourceAllocations;
37import org.onosproject.store.trivial.impl.SimpleIntentBatchQueue;
38import org.onosproject.store.trivial.impl.SimpleIntentStore;
Brian O'Connor427a1762014-11-19 18:40:32 -080039
Ray Milkeye9a3e222014-12-03 16:46:06 -080040import com.google.common.collect.HashMultimap;
41import com.google.common.collect.Lists;
42import com.google.common.collect.Maps;
43import com.google.common.collect.Multimap;
44import com.google.common.collect.Sets;
Brian O'Connor427a1762014-11-19 18:40:32 -080045
Ray Milkeye9a3e222014-12-03 16:46:06 -080046import static org.hamcrest.MatcherAssert.assertThat;
47import static org.hamcrest.Matchers.hasSize;
Brian O'Connor427a1762014-11-19 18:40:32 -080048import static org.junit.Assert.assertEquals;
Ray Milkeye9a3e222014-12-03 16:46:06 -080049import static org.junit.Assert.assertNotNull;
Brian O'Connor427a1762014-11-19 18:40:32 -080050import static org.junit.Assert.assertTrue;
Brian O'Connor427a1762014-11-19 18:40:32 -080051import static org.onlab.util.Tools.delay;
Ray Milkeye9a3e222014-12-03 16:46:06 -080052import static org.onosproject.net.intent.IntentState.FAILED;
53import static org.onosproject.net.intent.IntentState.INSTALLED;
54import static org.onosproject.net.intent.IntentState.WITHDRAWN;
Brian O'Connor427a1762014-11-19 18:40:32 -080055
56/**
57 * Test intent manager and transitions.
58 *
59 * TODO implement the following tests:
60 * - {submit, withdraw, update, replace} intent
61 * - {submit, update, recomiling} intent with failed compilation
62 * - failed reservation
63 * - push timeout recovery
64 * - failed items recovery
65 *
66 * in general, verify intents store, flow store, and work queue
67 */
68public class IntentManagerTest {
69
70 private static final ApplicationId APPID = new TestApplicationId("manager-test");
71
72 private IntentManager manager;
73 private MockFlowRuleService flowRuleService;
74
75 protected IntentService service;
76 protected IntentExtensionService extensionService;
77 protected TestListener listener = new TestListener();
78 protected TestIntentCompiler compiler = new TestIntentCompiler();
79 protected TestIntentInstaller installer = new TestIntentInstaller();
80
Ray Milkeye9a3e222014-12-03 16:46:06 -080081 private static class TestListener implements IntentListener {
82 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
83 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
84
85 @Override
86 public void event(IntentEvent event) {
87 events.put(event.type(), event);
88 if (latchMap.containsKey(event.type())) {
89 latchMap.get(event.type()).countDown();
90 }
91 }
92
93 public int getCounts(IntentEvent.Type type) {
94 return events.get(type).size();
95 }
96
97 public void setLatch(int count, IntentEvent.Type type) {
98 latchMap.put(type, new CountDownLatch(count));
99 }
100
101 public void await(IntentEvent.Type type) {
102 try {
103 assertTrue("Timed out waiting for: " + type,
104 latchMap.get(type).await(5, TimeUnit.SECONDS));
105 } catch (InterruptedException e) {
106 e.printStackTrace();
107 }
108 }
109 }
110
111 private static class TestIntentTracker implements ObjectiveTrackerService {
112 private TopologyChangeDelegate delegate;
113 @Override
114 public void setDelegate(TopologyChangeDelegate delegate) {
115 this.delegate = delegate;
116 }
117
118 @Override
119 public void unsetDelegate(TopologyChangeDelegate delegate) {
120 if (delegate.equals(this.delegate)) {
121 this.delegate = null;
122 }
123 }
124
125 @Override
126 public void addTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
127 //TODO
128 }
129
130 @Override
131 public void removeTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
132 //TODO
133 }
134 }
135
136 private static class MockIntent extends Intent {
137 private static AtomicLong counter = new AtomicLong(0);
138
139 private final Long number;
140 // Nothing new here
141 public MockIntent(Long number) {
142 super(APPID, null);
143 this.number = number;
144 }
145
146 public Long number() {
147 return number;
148 }
149
150 public static Long nextId() {
151 return counter.getAndIncrement();
152 }
153 }
154
155 private static class MockInstallableIntent extends MockIntent {
156 public MockInstallableIntent(Long number) {
157 super(number);
158 }
159
160 @Override
161 public boolean isInstallable() {
162 return true;
163 }
164 }
165
166 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
167 @Override
168 public List<Intent> compile(MockIntent intent, List<Intent> installable,
169 Set<LinkResourceAllocations> resources) {
170 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
171 }
172 }
173
174 private static class TestIntentCompilerError implements IntentCompiler<MockIntent> {
175 @Override
176 public List<Intent> compile(MockIntent intent, List<Intent> installable,
177 Set<LinkResourceAllocations> resources) {
178 throw new IntentCompilationException("Compilation always fails");
179 }
180 }
181
182 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
183 @Override
184 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
185 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
186 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
187 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
188 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
189 }
190
191 @Override
192 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
193 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
194 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
195 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
196 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
197 }
198
199 @Override
200 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
201 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
202 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
203 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
204 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
205 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
206 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
207 }
208 }
209
210 private static class TestIntentErrorInstaller implements IntentInstaller<MockInstallableIntent> {
211 @Override
212 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
213 throw new IntentInstallationException("install() always fails");
214 }
215
216 @Override
217 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
218 throw new IntentRemovalException("uninstall() always fails");
219 }
220
221 @Override
222 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
223 throw new IntentInstallationException("replace() always fails");
224 }
225 }
226
227 /**
228 * Hamcrest matcher to check that a conllection of Intents contains an
229 * Intent with the specified Intent Id.
230 */
231 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
232 private final IntentId id;
233
234 public EntryForIntentMatcher(IntentId idValue) {
235 id = idValue;
236 }
237
238 @Override
239 public boolean matchesSafely(Collection<Intent> intents) {
240 for (Intent intent : intents) {
241 if (intent.id().equals(id)) {
242 return true;
243 }
244 }
245 return false;
246 }
247
248 @Override
249 public void describeTo(Description description) {
250 description.appendText("an intent with id \" ").
251 appendText(id.toString()).
252 appendText("\"");
253 }
254 }
255
256 private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
257 return new EntryForIntentMatcher(id);
258 }
259
Brian O'Connor427a1762014-11-19 18:40:32 -0800260 @Before
261 public void setUp() {
262 manager = new IntentManager();
263 flowRuleService = new MockFlowRuleService();
264 manager.store = new SimpleIntentStore();
265 manager.batchService = new SimpleIntentBatchQueue();
266 manager.eventDispatcher = new TestEventDispatcher();
267 manager.trackerService = new TestIntentTracker();
268 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -0800269 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -0800270 service = manager;
271 extensionService = manager;
272
273 manager.activate();
274 service.addListener(listener);
275 extensionService.registerCompiler(MockIntent.class, compiler);
276 extensionService.registerInstaller(MockInstallableIntent.class, installer);
277
278 assertTrue("store should be empty",
279 Sets.newHashSet(service.getIntents()).isEmpty());
280 assertEquals(0L, flowRuleService.getFlowRuleCount());
281 }
282
283 @After
284 public void tearDown() {
285 // verify that all intents are parked and the batch operation is unblocked
286 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
287 for (Intent i : service.getIntents()) {
288 IntentState state = service.getIntentState(i.id());
289 assertTrue("Intent " + i.id() + " is in invalid state " + state,
290 parked.contains(state));
291 }
292 //the batch has not yet been removed when we receive the last event
293 // FIXME: this doesn't guarantee to avoid the race
294 for (int tries = 0; tries < 10; tries++) {
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800295 if (manager.batchService.getPendingOperations().isEmpty()) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800296 break;
297 }
298 delay(10);
299 }
300 assertTrue("There are still pending batch operations.",
301 manager.batchService.getPendingOperations().isEmpty());
Brian O'Connor427a1762014-11-19 18:40:32 -0800302
303 extensionService.unregisterCompiler(MockIntent.class);
304 extensionService.unregisterInstaller(MockInstallableIntent.class);
305 service.removeListener(listener);
306 manager.deactivate();
307 // TODO null the other refs?
308 }
309
310 @Test
311 public void submitIntent() {
312 flowRuleService.setFuture(true);
313
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800314 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800315 listener.setLatch(1, Type.INSTALLED);
316 Intent intent = new MockIntent(MockIntent.nextId());
317 service.submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800318 listener.await(Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800319 listener.await(Type.INSTALLED);
320 assertEquals(1L, service.getIntentCount());
321 assertEquals(1L, flowRuleService.getFlowRuleCount());
322 }
323
324 @Test
325 public void withdrawIntent() {
326 flowRuleService.setFuture(true);
327
328 listener.setLatch(1, Type.INSTALLED);
329 Intent intent = new MockIntent(MockIntent.nextId());
330 service.submit(intent);
331 listener.await(Type.INSTALLED);
332 assertEquals(1L, service.getIntentCount());
333 assertEquals(1L, flowRuleService.getFlowRuleCount());
334
335 listener.setLatch(1, Type.WITHDRAWN);
336 service.withdraw(intent);
337 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800338 delay(10); //FIXME this is a race
339 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800340 assertEquals(0L, flowRuleService.getFlowRuleCount());
341 }
342
343 @Test
344 public void stressSubmitWithdraw() {
345 flowRuleService.setFuture(true);
346
347 int count = 500;
348
349 listener.setLatch(count, Type.INSTALLED);
350 listener.setLatch(count, Type.WITHDRAWN);
351
352 Intent intent = new MockIntent(MockIntent.nextId());
353 for (int i = 0; i < count; i++) {
354 service.submit(intent);
355 service.withdraw(intent);
356 }
357
358 listener.await(Type.INSTALLED);
359 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800360 delay(10); //FIXME this is a race
361 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800362 assertEquals(0L, flowRuleService.getFlowRuleCount());
363 }
364
365 @Test
366 public void replaceIntent() {
367 flowRuleService.setFuture(true);
368
369 MockIntent intent = new MockIntent(MockIntent.nextId());
370 listener.setLatch(1, Type.INSTALLED);
371 service.submit(intent);
372 listener.await(Type.INSTALLED);
373 assertEquals(1L, service.getIntentCount());
374 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
375
376 MockIntent intent2 = new MockIntent(MockIntent.nextId());
377 listener.setLatch(1, Type.WITHDRAWN);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800378 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800379 listener.setLatch(1, Type.INSTALLED);
380 service.replace(intent.id(), intent2);
381 listener.await(Type.WITHDRAWN);
382 listener.await(Type.INSTALLED);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800383 delay(10); //FIXME this is a race
384 assertEquals(1L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800385 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
386 assertEquals(intent2.number().intValue(),
387 flowRuleService.flows.iterator().next().priority());
388 }
389
Ray Milkey93508c22014-12-02 11:35:56 -0800390 /**
391 * Tests for proper behavior of installation of an intent that triggers
392 * a compilation error.
393 */
394 @Test
395 public void errorIntentCompile() {
396 final TestIntentCompilerError errorCompiler = new TestIntentCompilerError();
397 extensionService.registerCompiler(MockIntent.class, errorCompiler);
398 MockIntent intent = new MockIntent(MockIntent.nextId());
399 listener.setLatch(1, Type.INSTALL_REQ);
400 listener.setLatch(1, Type.FAILED);
401 service.submit(intent);
402 listener.await(Type.INSTALL_REQ);
403 listener.await(Type.FAILED);
404 }
405
406 /**
407 * Tests handling a future that contains an error as a result of
408 * installing an intent.
409 */
410 @Test
411 public void errorIntentInstallFromFlows() {
412 final Long id = MockIntent.nextId();
413 flowRuleService.setFuture(false, 1);
414 MockIntent intent = new MockIntent(id);
415 listener.setLatch(1, Type.FAILED);
416 listener.setLatch(1, Type.INSTALL_REQ);
417 service.submit(intent);
418 listener.await(Type.INSTALL_REQ);
419 delay(10); // need to make sure we have some failed futures returned first
420 flowRuleService.setFuture(true, 0);
421 listener.await(Type.FAILED);
422 }
423
424 /**
425 * Tests handling of an error that is generated by the intent installer.
426 */
427 @Test
428 public void errorIntentInstallFromInstaller() {
429 final TestIntentErrorInstaller errorInstaller = new TestIntentErrorInstaller();
430 extensionService.registerInstaller(MockInstallableIntent.class, errorInstaller);
431 MockIntent intent = new MockIntent(MockIntent.nextId());
432 listener.setLatch(1, Type.INSTALL_REQ);
433 listener.setLatch(1, Type.FAILED);
434 service.submit(intent);
435 listener.await(Type.INSTALL_REQ);
436 listener.await(Type.FAILED);
Ray Milkey93508c22014-12-02 11:35:56 -0800437 }
438
Brian O'Connor427a1762014-11-19 18:40:32 -0800439 /**
Ray Milkeye9a3e222014-12-03 16:46:06 -0800440 * Tests handling a future that contains an unresolvable error as a result of
441 * installing an intent.
Brian O'Connor427a1762014-11-19 18:40:32 -0800442 */
Ray Milkeye9a3e222014-12-03 16:46:06 -0800443 @Test
444 public void errorIntentInstallNeverTrue() {
445 final Long id = MockIntent.nextId();
446 flowRuleService.setFuture(false, 1);
447 MockIntent intent = new MockIntent(id);
448 listener.setLatch(1, Type.WITHDRAWN);
449 listener.setLatch(1, Type.INSTALL_REQ);
450 service.submit(intent);
451 listener.await(Type.INSTALL_REQ);
452 // The delay here forces the retry loop in the intent manager to time out
453 delay(100);
454 flowRuleService.setFuture(false, 1);
455 service.withdraw(intent);
456 listener.await(Type.WITHDRAWN);
457 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800458
Ray Milkeye9a3e222014-12-03 16:46:06 -0800459 /**
460 * Tests that a compiler for a subclass of an intent that already has a
461 * compiler is automatically added.
462 */
463 @Test
464 public void intentSubclassCompile() {
465 class MockIntentSubclass extends MockIntent {
466 public MockIntentSubclass(Long number) {
467 super(number);
468 }
469 }
470 flowRuleService.setFuture(true);
471
472 listener.setLatch(1, Type.INSTALL_REQ);
473 listener.setLatch(1, Type.INSTALLED);
474 Intent intent = new MockIntentSubclass(MockIntent.nextId());
475 service.submit(intent);
476 listener.await(Type.INSTALL_REQ);
477 listener.await(Type.INSTALLED);
478 assertEquals(1L, service.getIntentCount());
479 assertEquals(1L, flowRuleService.getFlowRuleCount());
480
481 final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers =
482 extensionService.getCompilers();
483 assertEquals(2, compilers.size());
484 assertNotNull(compilers.get(MockIntentSubclass.class));
485 assertNotNull(compilers.get(MockIntent.class));
486 }
487
488 /**
489 * Tests an intent with no compiler.
490 */
491 @Test
492 public void intentWithoutCompiler() {
493 class IntentNoCompiler extends Intent {
494 IntentNoCompiler() {
495 super(APPID, null);
496 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800497 }
498
Ray Milkeye9a3e222014-12-03 16:46:06 -0800499 Intent intent = new IntentNoCompiler();
500 listener.setLatch(1, Type.INSTALL_REQ);
501 listener.setLatch(1, Type.FAILED);
502 service.submit(intent);
503 listener.await(Type.INSTALL_REQ);
504 listener.await(Type.FAILED);
505 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800506
Ray Milkeye9a3e222014-12-03 16:46:06 -0800507 /**
508 * Tests an intent with no installer.
509 */
510 @Test
511 public void intentWithoutInstaller() {
512
513 extensionService.unregisterInstaller(MockInstallableIntent.class);
514
515 MockIntent intent = new MockIntent(MockIntent.nextId());
516 listener.setLatch(1, Type.INSTALL_REQ);
517 listener.setLatch(1, Type.FAILED);
518 service.submit(intent);
519 listener.await(Type.INSTALL_REQ);
520 listener.await(Type.FAILED);
521 }
522
523 /**
524 * Tests that the intent fetching methods are correct.
525 */
526 @Test
527 public void testIntentFetching() {
528 List<Intent> intents;
529
530 flowRuleService.setFuture(true);
531
532 intents = Lists.newArrayList(service.getIntents());
533 assertThat(intents, hasSize(0));
534
535 final MockIntent intent1 = new MockIntent(MockIntent.nextId());
536 final MockIntent intent2 = new MockIntent(MockIntent.nextId());
537
538 listener.setLatch(2, Type.INSTALL_REQ);
539 listener.setLatch(2, Type.INSTALLED);
540 service.submit(intent1);
541 service.submit(intent2);
542 listener.await(Type.INSTALL_REQ);
543 listener.await(Type.INSTALL_REQ);
544 listener.await(Type.INSTALLED);
545 listener.await(Type.INSTALLED);
546
547 intents = Lists.newArrayList(service.getIntents());
548 assertThat(intents, hasSize(2));
549
550 assertThat(intents, hasIntentWithId(intent1.id()));
551 assertThat(intents, hasIntentWithId(intent2.id()));
Brian O'Connor427a1762014-11-19 18:40:32 -0800552 }
553}