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