blob: 49efad5b82fba9046e2aee7e34588b265148f767 [file] [log] [blame]
alshabibab984662014-12-04 18:56:18 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.net.intent.impl;
Brian O'Connor427a1762014-11-19 18:40:32 -080017
Brian O'Connorb499b352015-02-03 16:46:15 -080018import com.google.common.collect.HashMultimap;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Multimap;
22import com.google.common.collect.Sets;
Brian O'Connor427a1762014-11-19 18:40:32 -080023import org.hamcrest.Description;
Brian O'Connor427a1762014-11-19 18:40:32 -080024import org.hamcrest.TypeSafeMatcher;
25import org.junit.After;
26import org.junit.Before;
27import org.junit.Test;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.TestApplicationId;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.impl.TestCoreManager;
31import org.onosproject.event.impl.TestEventDispatcher;
32import org.onosproject.net.NetworkResource;
33import org.onosproject.net.flow.FlowRule;
34import org.onosproject.net.flow.FlowRuleBatchEntry;
35import org.onosproject.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
36import org.onosproject.net.flow.FlowRuleBatchOperation;
37import org.onosproject.net.intent.Intent;
38import org.onosproject.net.intent.IntentCompiler;
39import org.onosproject.net.intent.IntentEvent;
40import org.onosproject.net.intent.IntentEvent.Type;
41import org.onosproject.net.intent.IntentExtensionService;
42import org.onosproject.net.intent.IntentId;
43import org.onosproject.net.intent.IntentInstaller;
44import org.onosproject.net.intent.IntentListener;
45import org.onosproject.net.intent.IntentService;
46import org.onosproject.net.intent.IntentState;
47import org.onosproject.net.intent.IntentTestsMocks;
48import org.onosproject.net.resource.LinkResourceAllocations;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.store.trivial.impl.SimpleIntentStore;
Brian O'Connor427a1762014-11-19 18:40:32 -080050
Brian O'Connorb499b352015-02-03 16:46:15 -080051import java.util.Collection;
52import java.util.Collections;
53import java.util.List;
54import java.util.Map;
55import java.util.Set;
56import java.util.concurrent.CountDownLatch;
57import java.util.concurrent.TimeUnit;
58import java.util.concurrent.atomic.AtomicLong;
Brian O'Connor427a1762014-11-19 18:40:32 -080059
Ray Milkeye9a3e222014-12-03 16:46:06 -080060import static org.hamcrest.MatcherAssert.assertThat;
61import static org.hamcrest.Matchers.hasSize;
Brian O'Connorb499b352015-02-03 16:46:15 -080062import static org.junit.Assert.*;
Brian O'Connor427a1762014-11-19 18:40:32 -080063import static org.onlab.util.Tools.delay;
Brian O'Connorb499b352015-02-03 16:46:15 -080064import static org.onosproject.net.intent.IntentState.*;
Brian O'Connor427a1762014-11-19 18:40:32 -080065
66/**
67 * Test intent manager and transitions.
68 *
69 * TODO implement the following tests:
70 * - {submit, withdraw, update, replace} intent
Sho SHIMIZU34660962015-01-22 17:58:44 -080071 * - {submit, update, recompiling} intent with failed compilation
Brian O'Connor427a1762014-11-19 18:40:32 -080072 * - failed reservation
73 * - push timeout recovery
74 * - failed items recovery
75 *
76 * in general, verify intents store, flow store, and work queue
77 */
78public class IntentManagerTest {
79
80 private static final ApplicationId APPID = new TestApplicationId("manager-test");
81
82 private IntentManager manager;
83 private MockFlowRuleService flowRuleService;
84
85 protected IntentService service;
86 protected IntentExtensionService extensionService;
87 protected TestListener listener = new TestListener();
88 protected TestIntentCompiler compiler = new TestIntentCompiler();
89 protected TestIntentInstaller installer = new TestIntentInstaller();
90
Ray Milkeye9a3e222014-12-03 16:46:06 -080091 private static class TestListener implements IntentListener {
92 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
93 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
94
95 @Override
96 public void event(IntentEvent event) {
97 events.put(event.type(), event);
98 if (latchMap.containsKey(event.type())) {
99 latchMap.get(event.type()).countDown();
100 }
101 }
102
103 public int getCounts(IntentEvent.Type type) {
104 return events.get(type).size();
105 }
106
107 public void setLatch(int count, IntentEvent.Type type) {
108 latchMap.put(type, new CountDownLatch(count));
109 }
110
111 public void await(IntentEvent.Type type) {
112 try {
113 assertTrue("Timed out waiting for: " + type,
114 latchMap.get(type).await(5, TimeUnit.SECONDS));
115 } catch (InterruptedException e) {
116 e.printStackTrace();
117 }
118 }
119 }
120
121 private static class TestIntentTracker implements ObjectiveTrackerService {
122 private TopologyChangeDelegate delegate;
123 @Override
124 public void setDelegate(TopologyChangeDelegate delegate) {
125 this.delegate = delegate;
126 }
127
128 @Override
129 public void unsetDelegate(TopologyChangeDelegate delegate) {
130 if (delegate.equals(this.delegate)) {
131 this.delegate = null;
132 }
133 }
134
135 @Override
136 public void addTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
137 //TODO
138 }
139
140 @Override
141 public void removeTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
142 //TODO
143 }
144 }
145
146 private static class MockIntent extends Intent {
147 private static AtomicLong counter = new AtomicLong(0);
148
149 private final Long number;
150 // Nothing new here
151 public MockIntent(Long number) {
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800152 super(APPID, Collections.emptyList());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800153 this.number = number;
154 }
155
156 public Long number() {
157 return number;
158 }
159
160 public static Long nextId() {
161 return counter.getAndIncrement();
162 }
163 }
164
165 private static class MockInstallableIntent extends MockIntent {
166 public MockInstallableIntent(Long number) {
167 super(number);
168 }
169
170 @Override
171 public boolean isInstallable() {
172 return true;
173 }
174 }
175
176 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
177 @Override
178 public List<Intent> compile(MockIntent intent, List<Intent> installable,
179 Set<LinkResourceAllocations> resources) {
180 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
181 }
182 }
183
184 private static class TestIntentCompilerError implements IntentCompiler<MockIntent> {
185 @Override
186 public List<Intent> compile(MockIntent intent, List<Intent> installable,
187 Set<LinkResourceAllocations> resources) {
188 throw new IntentCompilationException("Compilation always fails");
189 }
190 }
191
192 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
193 @Override
194 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
195 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
196 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
197 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800198 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800199 }
200
201 @Override
202 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
203 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
204 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
205 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800206 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800207 }
208
209 @Override
210 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
211 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
212 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
213 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
214 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
215 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800216 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800217 }
218 }
219
220 private static class TestIntentErrorInstaller implements IntentInstaller<MockInstallableIntent> {
221 @Override
222 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
223 throw new IntentInstallationException("install() always fails");
224 }
225
226 @Override
227 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
228 throw new IntentRemovalException("uninstall() always fails");
229 }
230
231 @Override
232 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
233 throw new IntentInstallationException("replace() always fails");
234 }
235 }
236
237 /**
238 * Hamcrest matcher to check that a conllection of Intents contains an
239 * Intent with the specified Intent Id.
240 */
241 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
242 private final IntentId id;
243
244 public EntryForIntentMatcher(IntentId idValue) {
245 id = idValue;
246 }
247
248 @Override
249 public boolean matchesSafely(Collection<Intent> intents) {
250 for (Intent intent : intents) {
251 if (intent.id().equals(id)) {
252 return true;
253 }
254 }
255 return false;
256 }
257
258 @Override
259 public void describeTo(Description description) {
260 description.appendText("an intent with id \" ").
261 appendText(id.toString()).
262 appendText("\"");
263 }
264 }
265
266 private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
267 return new EntryForIntentMatcher(id);
268 }
269
Brian O'Connor427a1762014-11-19 18:40:32 -0800270 @Before
271 public void setUp() {
272 manager = new IntentManager();
273 flowRuleService = new MockFlowRuleService();
274 manager.store = new SimpleIntentStore();
Brian O'Connor427a1762014-11-19 18:40:32 -0800275 manager.eventDispatcher = new TestEventDispatcher();
276 manager.trackerService = new TestIntentTracker();
277 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -0800278 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -0800279 service = manager;
280 extensionService = manager;
281
282 manager.activate();
283 service.addListener(listener);
284 extensionService.registerCompiler(MockIntent.class, compiler);
285 extensionService.registerInstaller(MockInstallableIntent.class, installer);
286
287 assertTrue("store should be empty",
288 Sets.newHashSet(service.getIntents()).isEmpty());
289 assertEquals(0L, flowRuleService.getFlowRuleCount());
290 }
291
292 @After
293 public void tearDown() {
294 // verify that all intents are parked and the batch operation is unblocked
295 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
296 for (Intent i : service.getIntents()) {
297 IntentState state = service.getIntentState(i.id());
298 assertTrue("Intent " + i.id() + " is in invalid state " + state,
299 parked.contains(state));
300 }
301 //the batch has not yet been removed when we receive the last event
302 // FIXME: this doesn't guarantee to avoid the race
Brian O'Connorb499b352015-02-03 16:46:15 -0800303
304 //FIXME
305// for (int tries = 0; tries < 10; tries++) {
306// if (manager.batchService.getPendingOperations().isEmpty()) {
307// break;
308// }
309// delay(10);
310// }
311// assertTrue("There are still pending batch operations.",
312// manager.batchService.getPendingOperations().isEmpty());
Brian O'Connor427a1762014-11-19 18:40:32 -0800313
314 extensionService.unregisterCompiler(MockIntent.class);
315 extensionService.unregisterInstaller(MockInstallableIntent.class);
316 service.removeListener(listener);
317 manager.deactivate();
318 // TODO null the other refs?
319 }
320
321 @Test
322 public void submitIntent() {
323 flowRuleService.setFuture(true);
324
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800325 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800326 listener.setLatch(1, Type.INSTALLED);
327 Intent intent = new MockIntent(MockIntent.nextId());
328 service.submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800329 listener.await(Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800330 listener.await(Type.INSTALLED);
331 assertEquals(1L, service.getIntentCount());
332 assertEquals(1L, flowRuleService.getFlowRuleCount());
333 }
334
335 @Test
336 public void withdrawIntent() {
337 flowRuleService.setFuture(true);
338
339 listener.setLatch(1, Type.INSTALLED);
340 Intent intent = new MockIntent(MockIntent.nextId());
341 service.submit(intent);
342 listener.await(Type.INSTALLED);
343 assertEquals(1L, service.getIntentCount());
344 assertEquals(1L, flowRuleService.getFlowRuleCount());
345
346 listener.setLatch(1, Type.WITHDRAWN);
347 service.withdraw(intent);
348 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800349 delay(10); //FIXME this is a race
350 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800351 assertEquals(0L, flowRuleService.getFlowRuleCount());
352 }
353
354 @Test
355 public void stressSubmitWithdraw() {
356 flowRuleService.setFuture(true);
357
358 int count = 500;
359
360 listener.setLatch(count, Type.INSTALLED);
361 listener.setLatch(count, Type.WITHDRAWN);
362
363 Intent intent = new MockIntent(MockIntent.nextId());
364 for (int i = 0; i < count; i++) {
365 service.submit(intent);
366 service.withdraw(intent);
367 }
368
369 listener.await(Type.INSTALLED);
370 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800371 delay(10); //FIXME this is a race
372 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800373 assertEquals(0L, flowRuleService.getFlowRuleCount());
374 }
375
376 @Test
377 public void replaceIntent() {
378 flowRuleService.setFuture(true);
379
380 MockIntent intent = new MockIntent(MockIntent.nextId());
381 listener.setLatch(1, Type.INSTALLED);
382 service.submit(intent);
383 listener.await(Type.INSTALLED);
384 assertEquals(1L, service.getIntentCount());
385 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
386
387 MockIntent intent2 = new MockIntent(MockIntent.nextId());
388 listener.setLatch(1, Type.WITHDRAWN);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800389 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800390 listener.setLatch(1, Type.INSTALLED);
391 service.replace(intent.id(), intent2);
392 listener.await(Type.WITHDRAWN);
393 listener.await(Type.INSTALLED);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800394 delay(10); //FIXME this is a race
395 assertEquals(1L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800396 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
397 assertEquals(intent2.number().intValue(),
398 flowRuleService.flows.iterator().next().priority());
399 }
400
Ray Milkey93508c22014-12-02 11:35:56 -0800401 /**
402 * Tests for proper behavior of installation of an intent that triggers
403 * a compilation error.
404 */
405 @Test
406 public void errorIntentCompile() {
407 final TestIntentCompilerError errorCompiler = new TestIntentCompilerError();
408 extensionService.registerCompiler(MockIntent.class, errorCompiler);
409 MockIntent intent = new MockIntent(MockIntent.nextId());
410 listener.setLatch(1, Type.INSTALL_REQ);
411 listener.setLatch(1, Type.FAILED);
412 service.submit(intent);
413 listener.await(Type.INSTALL_REQ);
414 listener.await(Type.FAILED);
415 }
416
417 /**
418 * Tests handling a future that contains an error as a result of
419 * installing an intent.
420 */
421 @Test
422 public void errorIntentInstallFromFlows() {
423 final Long id = MockIntent.nextId();
424 flowRuleService.setFuture(false, 1);
425 MockIntent intent = new MockIntent(id);
426 listener.setLatch(1, Type.FAILED);
427 listener.setLatch(1, Type.INSTALL_REQ);
428 service.submit(intent);
429 listener.await(Type.INSTALL_REQ);
430 delay(10); // need to make sure we have some failed futures returned first
431 flowRuleService.setFuture(true, 0);
432 listener.await(Type.FAILED);
433 }
434
435 /**
436 * Tests handling of an error that is generated by the intent installer.
437 */
438 @Test
439 public void errorIntentInstallFromInstaller() {
440 final TestIntentErrorInstaller errorInstaller = new TestIntentErrorInstaller();
441 extensionService.registerInstaller(MockInstallableIntent.class, errorInstaller);
442 MockIntent intent = new MockIntent(MockIntent.nextId());
443 listener.setLatch(1, Type.INSTALL_REQ);
444 listener.setLatch(1, Type.FAILED);
445 service.submit(intent);
446 listener.await(Type.INSTALL_REQ);
447 listener.await(Type.FAILED);
Ray Milkey93508c22014-12-02 11:35:56 -0800448 }
449
Brian O'Connor427a1762014-11-19 18:40:32 -0800450 /**
Ray Milkeye9a3e222014-12-03 16:46:06 -0800451 * Tests handling a future that contains an unresolvable error as a result of
452 * installing an intent.
Brian O'Connor427a1762014-11-19 18:40:32 -0800453 */
Ray Milkeye9a3e222014-12-03 16:46:06 -0800454 @Test
455 public void errorIntentInstallNeverTrue() {
456 final Long id = MockIntent.nextId();
457 flowRuleService.setFuture(false, 1);
458 MockIntent intent = new MockIntent(id);
459 listener.setLatch(1, Type.WITHDRAWN);
460 listener.setLatch(1, Type.INSTALL_REQ);
461 service.submit(intent);
462 listener.await(Type.INSTALL_REQ);
463 // The delay here forces the retry loop in the intent manager to time out
464 delay(100);
465 flowRuleService.setFuture(false, 1);
466 service.withdraw(intent);
467 listener.await(Type.WITHDRAWN);
468 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800469
Ray Milkeye9a3e222014-12-03 16:46:06 -0800470 /**
471 * Tests that a compiler for a subclass of an intent that already has a
472 * compiler is automatically added.
473 */
474 @Test
475 public void intentSubclassCompile() {
476 class MockIntentSubclass extends MockIntent {
477 public MockIntentSubclass(Long number) {
478 super(number);
479 }
480 }
481 flowRuleService.setFuture(true);
482
483 listener.setLatch(1, Type.INSTALL_REQ);
484 listener.setLatch(1, Type.INSTALLED);
485 Intent intent = new MockIntentSubclass(MockIntent.nextId());
486 service.submit(intent);
487 listener.await(Type.INSTALL_REQ);
488 listener.await(Type.INSTALLED);
489 assertEquals(1L, service.getIntentCount());
490 assertEquals(1L, flowRuleService.getFlowRuleCount());
491
492 final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers =
493 extensionService.getCompilers();
494 assertEquals(2, compilers.size());
495 assertNotNull(compilers.get(MockIntentSubclass.class));
496 assertNotNull(compilers.get(MockIntent.class));
497 }
498
499 /**
500 * Tests an intent with no compiler.
501 */
502 @Test
503 public void intentWithoutCompiler() {
504 class IntentNoCompiler extends Intent {
505 IntentNoCompiler() {
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800506 super(APPID, Collections.emptyList());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800507 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800508 }
509
Ray Milkeye9a3e222014-12-03 16:46:06 -0800510 Intent intent = new IntentNoCompiler();
511 listener.setLatch(1, Type.INSTALL_REQ);
512 listener.setLatch(1, Type.FAILED);
513 service.submit(intent);
514 listener.await(Type.INSTALL_REQ);
515 listener.await(Type.FAILED);
516 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800517
Ray Milkeye9a3e222014-12-03 16:46:06 -0800518 /**
519 * Tests an intent with no installer.
520 */
521 @Test
522 public void intentWithoutInstaller() {
523
524 extensionService.unregisterInstaller(MockInstallableIntent.class);
525
526 MockIntent intent = new MockIntent(MockIntent.nextId());
527 listener.setLatch(1, Type.INSTALL_REQ);
528 listener.setLatch(1, Type.FAILED);
529 service.submit(intent);
530 listener.await(Type.INSTALL_REQ);
531 listener.await(Type.FAILED);
532 }
533
534 /**
535 * Tests that the intent fetching methods are correct.
536 */
537 @Test
538 public void testIntentFetching() {
539 List<Intent> intents;
540
541 flowRuleService.setFuture(true);
542
543 intents = Lists.newArrayList(service.getIntents());
544 assertThat(intents, hasSize(0));
545
546 final MockIntent intent1 = new MockIntent(MockIntent.nextId());
547 final MockIntent intent2 = new MockIntent(MockIntent.nextId());
548
549 listener.setLatch(2, Type.INSTALL_REQ);
550 listener.setLatch(2, Type.INSTALLED);
551 service.submit(intent1);
552 service.submit(intent2);
553 listener.await(Type.INSTALL_REQ);
554 listener.await(Type.INSTALL_REQ);
555 listener.await(Type.INSTALLED);
556 listener.await(Type.INSTALLED);
557
558 intents = Lists.newArrayList(service.getIntents());
559 assertThat(intents, hasSize(2));
560
561 assertThat(intents, hasIntentWithId(intent1.id()));
562 assertThat(intents, hasIntentWithId(intent2.id()));
Brian O'Connor427a1762014-11-19 18:40:32 -0800563 }
564}