blob: 5ff9c9910310dec53c4d4b19852a0e17e40831a6 [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;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080048import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.resource.LinkResourceAllocations;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.store.trivial.impl.SimpleIntentStore;
Brian O'Connor427a1762014-11-19 18:40:32 -080051
Brian O'Connorb499b352015-02-03 16:46:15 -080052import java.util.Collection;
53import java.util.Collections;
54import java.util.List;
55import java.util.Map;
56import java.util.Set;
57import java.util.concurrent.CountDownLatch;
58import java.util.concurrent.TimeUnit;
59import java.util.concurrent.atomic.AtomicLong;
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'Connorb499b352015-02-03 16:46:15 -080063import static org.junit.Assert.*;
Brian O'Connor427a1762014-11-19 18:40:32 -080064import static org.onlab.util.Tools.delay;
Brian O'Connorb499b352015-02-03 16:46:15 -080065import static org.onosproject.net.intent.IntentState.*;
Brian O'Connor427a1762014-11-19 18:40:32 -080066
67/**
68 * Test intent manager and transitions.
69 *
70 * TODO implement the following tests:
71 * - {submit, withdraw, update, replace} intent
Sho SHIMIZU34660962015-01-22 17:58:44 -080072 * - {submit, update, recompiling} intent with failed compilation
Brian O'Connor427a1762014-11-19 18:40:32 -080073 * - failed reservation
74 * - push timeout recovery
75 * - failed items recovery
76 *
77 * in general, verify intents store, flow store, and work queue
78 */
79public class IntentManagerTest {
80
81 private static final ApplicationId APPID = new TestApplicationId("manager-test");
82
83 private IntentManager manager;
84 private MockFlowRuleService flowRuleService;
85
86 protected IntentService service;
87 protected IntentExtensionService extensionService;
88 protected TestListener listener = new TestListener();
89 protected TestIntentCompiler compiler = new TestIntentCompiler();
90 protected TestIntentInstaller installer = new TestIntentInstaller();
91
Ray Milkeye9a3e222014-12-03 16:46:06 -080092 private static class TestListener implements IntentListener {
93 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
94 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
95
96 @Override
97 public void event(IntentEvent event) {
98 events.put(event.type(), event);
99 if (latchMap.containsKey(event.type())) {
100 latchMap.get(event.type()).countDown();
101 }
102 }
103
104 public int getCounts(IntentEvent.Type type) {
105 return events.get(type).size();
106 }
107
108 public void setLatch(int count, IntentEvent.Type type) {
109 latchMap.put(type, new CountDownLatch(count));
110 }
111
112 public void await(IntentEvent.Type type) {
113 try {
114 assertTrue("Timed out waiting for: " + type,
115 latchMap.get(type).await(5, TimeUnit.SECONDS));
116 } catch (InterruptedException e) {
117 e.printStackTrace();
118 }
119 }
120 }
121
122 private static class TestIntentTracker implements ObjectiveTrackerService {
123 private TopologyChangeDelegate delegate;
124 @Override
125 public void setDelegate(TopologyChangeDelegate delegate) {
126 this.delegate = delegate;
127 }
128
129 @Override
130 public void unsetDelegate(TopologyChangeDelegate delegate) {
131 if (delegate.equals(this.delegate)) {
132 this.delegate = null;
133 }
134 }
135
136 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800137 public void addTrackedResources(Key key, Collection<NetworkResource> resources) {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800138 //TODO
139 }
140
141 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800142 public void removeTrackedResources(Key key, Collection<NetworkResource> resources) {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800143 //TODO
144 }
145 }
146
147 private static class MockIntent extends Intent {
148 private static AtomicLong counter = new AtomicLong(0);
149
150 private final Long number;
151 // Nothing new here
152 public MockIntent(Long number) {
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800153 super(APPID, Collections.emptyList());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800154 this.number = number;
155 }
156
157 public Long number() {
158 return number;
159 }
160
161 public static Long nextId() {
162 return counter.getAndIncrement();
163 }
164 }
165
166 private static class MockInstallableIntent extends MockIntent {
167 public MockInstallableIntent(Long number) {
168 super(number);
169 }
170
171 @Override
172 public boolean isInstallable() {
173 return true;
174 }
175 }
176
177 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
178 @Override
179 public List<Intent> compile(MockIntent intent, List<Intent> installable,
180 Set<LinkResourceAllocations> resources) {
181 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
182 }
183 }
184
185 private static class TestIntentCompilerError implements IntentCompiler<MockIntent> {
186 @Override
187 public List<Intent> compile(MockIntent intent, List<Intent> installable,
188 Set<LinkResourceAllocations> resources) {
189 throw new IntentCompilationException("Compilation always fails");
190 }
191 }
192
193 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
194 @Override
195 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
196 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
197 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
198 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800199 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800200 }
201
202 @Override
203 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
204 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
205 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
206 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800207 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800208 }
209
210 @Override
211 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
212 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
213 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
214 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
215 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
216 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800217 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800218 }
219 }
220
221 private static class TestIntentErrorInstaller implements IntentInstaller<MockInstallableIntent> {
222 @Override
223 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
224 throw new IntentInstallationException("install() always fails");
225 }
226
227 @Override
228 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
229 throw new IntentRemovalException("uninstall() always fails");
230 }
231
232 @Override
233 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
234 throw new IntentInstallationException("replace() always fails");
235 }
236 }
237
238 /**
239 * Hamcrest matcher to check that a conllection of Intents contains an
240 * Intent with the specified Intent Id.
241 */
242 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
243 private final IntentId id;
244
245 public EntryForIntentMatcher(IntentId idValue) {
246 id = idValue;
247 }
248
249 @Override
250 public boolean matchesSafely(Collection<Intent> intents) {
251 for (Intent intent : intents) {
252 if (intent.id().equals(id)) {
253 return true;
254 }
255 }
256 return false;
257 }
258
259 @Override
260 public void describeTo(Description description) {
261 description.appendText("an intent with id \" ").
262 appendText(id.toString()).
263 appendText("\"");
264 }
265 }
266
267 private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
268 return new EntryForIntentMatcher(id);
269 }
270
Brian O'Connor427a1762014-11-19 18:40:32 -0800271 @Before
272 public void setUp() {
273 manager = new IntentManager();
274 flowRuleService = new MockFlowRuleService();
275 manager.store = new SimpleIntentStore();
Brian O'Connor427a1762014-11-19 18:40:32 -0800276 manager.eventDispatcher = new TestEventDispatcher();
277 manager.trackerService = new TestIntentTracker();
278 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -0800279 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -0800280 service = manager;
281 extensionService = manager;
282
283 manager.activate();
284 service.addListener(listener);
285 extensionService.registerCompiler(MockIntent.class, compiler);
286 extensionService.registerInstaller(MockInstallableIntent.class, installer);
287
288 assertTrue("store should be empty",
289 Sets.newHashSet(service.getIntents()).isEmpty());
290 assertEquals(0L, flowRuleService.getFlowRuleCount());
291 }
292
293 @After
294 public void tearDown() {
295 // verify that all intents are parked and the batch operation is unblocked
296 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
297 for (Intent i : service.getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800298 IntentState state = service.getIntentState(i.key());
Brian O'Connor427a1762014-11-19 18:40:32 -0800299 assertTrue("Intent " + i.id() + " is in invalid state " + state,
300 parked.contains(state));
301 }
302 //the batch has not yet been removed when we receive the last event
303 // FIXME: this doesn't guarantee to avoid the race
Brian O'Connorb499b352015-02-03 16:46:15 -0800304
305 //FIXME
306// for (int tries = 0; tries < 10; tries++) {
307// if (manager.batchService.getPendingOperations().isEmpty()) {
308// break;
309// }
310// delay(10);
311// }
312// assertTrue("There are still pending batch operations.",
313// manager.batchService.getPendingOperations().isEmpty());
Brian O'Connor427a1762014-11-19 18:40:32 -0800314
315 extensionService.unregisterCompiler(MockIntent.class);
316 extensionService.unregisterInstaller(MockInstallableIntent.class);
317 service.removeListener(listener);
318 manager.deactivate();
319 // TODO null the other refs?
320 }
321
322 @Test
323 public void submitIntent() {
324 flowRuleService.setFuture(true);
325
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800326 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800327 listener.setLatch(1, Type.INSTALLED);
328 Intent intent = new MockIntent(MockIntent.nextId());
329 service.submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800330 listener.await(Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800331 listener.await(Type.INSTALLED);
332 assertEquals(1L, service.getIntentCount());
333 assertEquals(1L, flowRuleService.getFlowRuleCount());
334 }
335
336 @Test
337 public void withdrawIntent() {
338 flowRuleService.setFuture(true);
339
340 listener.setLatch(1, Type.INSTALLED);
341 Intent intent = new MockIntent(MockIntent.nextId());
342 service.submit(intent);
343 listener.await(Type.INSTALLED);
344 assertEquals(1L, service.getIntentCount());
345 assertEquals(1L, flowRuleService.getFlowRuleCount());
346
347 listener.setLatch(1, Type.WITHDRAWN);
348 service.withdraw(intent);
349 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800350 delay(10); //FIXME this is a race
351 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800352 assertEquals(0L, flowRuleService.getFlowRuleCount());
353 }
354
355 @Test
356 public void stressSubmitWithdraw() {
357 flowRuleService.setFuture(true);
358
359 int count = 500;
360
361 listener.setLatch(count, Type.INSTALLED);
362 listener.setLatch(count, Type.WITHDRAWN);
363
364 Intent intent = new MockIntent(MockIntent.nextId());
365 for (int i = 0; i < count; i++) {
366 service.submit(intent);
367 service.withdraw(intent);
368 }
369
370 listener.await(Type.INSTALLED);
371 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800372 delay(10); //FIXME this is a race
373 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800374 assertEquals(0L, flowRuleService.getFlowRuleCount());
375 }
376
Ray Milkey93508c22014-12-02 11:35:56 -0800377 /**
378 * Tests for proper behavior of installation of an intent that triggers
379 * a compilation error.
380 */
381 @Test
382 public void errorIntentCompile() {
383 final TestIntentCompilerError errorCompiler = new TestIntentCompilerError();
384 extensionService.registerCompiler(MockIntent.class, errorCompiler);
385 MockIntent intent = new MockIntent(MockIntent.nextId());
386 listener.setLatch(1, Type.INSTALL_REQ);
387 listener.setLatch(1, Type.FAILED);
388 service.submit(intent);
389 listener.await(Type.INSTALL_REQ);
390 listener.await(Type.FAILED);
391 }
392
393 /**
394 * Tests handling a future that contains an error as a result of
395 * installing an intent.
396 */
397 @Test
398 public void errorIntentInstallFromFlows() {
399 final Long id = MockIntent.nextId();
Brian O'Connor5811ac22015-02-09 19:17:07 -0800400 flowRuleService.setFuture(false);
Ray Milkey93508c22014-12-02 11:35:56 -0800401 MockIntent intent = new MockIntent(id);
402 listener.setLatch(1, Type.FAILED);
403 listener.setLatch(1, Type.INSTALL_REQ);
404 service.submit(intent);
405 listener.await(Type.INSTALL_REQ);
406 delay(10); // need to make sure we have some failed futures returned first
Brian O'Connor5811ac22015-02-09 19:17:07 -0800407 flowRuleService.setFuture(true);
Ray Milkey93508c22014-12-02 11:35:56 -0800408 listener.await(Type.FAILED);
409 }
410
411 /**
412 * Tests handling of an error that is generated by the intent installer.
413 */
414 @Test
415 public void errorIntentInstallFromInstaller() {
416 final TestIntentErrorInstaller errorInstaller = new TestIntentErrorInstaller();
417 extensionService.registerInstaller(MockInstallableIntent.class, errorInstaller);
418 MockIntent intent = new MockIntent(MockIntent.nextId());
419 listener.setLatch(1, Type.INSTALL_REQ);
420 listener.setLatch(1, Type.FAILED);
421 service.submit(intent);
422 listener.await(Type.INSTALL_REQ);
423 listener.await(Type.FAILED);
Ray Milkey93508c22014-12-02 11:35:56 -0800424 }
425
Brian O'Connor427a1762014-11-19 18:40:32 -0800426 /**
Ray Milkeye9a3e222014-12-03 16:46:06 -0800427 * Tests handling a future that contains an unresolvable error as a result of
428 * installing an intent.
Brian O'Connor427a1762014-11-19 18:40:32 -0800429 */
Ray Milkeye9a3e222014-12-03 16:46:06 -0800430 @Test
431 public void errorIntentInstallNeverTrue() {
432 final Long id = MockIntent.nextId();
Brian O'Connor5811ac22015-02-09 19:17:07 -0800433 flowRuleService.setFuture(false);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800434 MockIntent intent = new MockIntent(id);
435 listener.setLatch(1, Type.WITHDRAWN);
436 listener.setLatch(1, Type.INSTALL_REQ);
437 service.submit(intent);
438 listener.await(Type.INSTALL_REQ);
439 // The delay here forces the retry loop in the intent manager to time out
440 delay(100);
Brian O'Connor5811ac22015-02-09 19:17:07 -0800441 flowRuleService.setFuture(false);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800442 service.withdraw(intent);
443 listener.await(Type.WITHDRAWN);
444 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800445
Ray Milkeye9a3e222014-12-03 16:46:06 -0800446 /**
447 * Tests that a compiler for a subclass of an intent that already has a
448 * compiler is automatically added.
449 */
450 @Test
451 public void intentSubclassCompile() {
452 class MockIntentSubclass extends MockIntent {
453 public MockIntentSubclass(Long number) {
454 super(number);
455 }
456 }
457 flowRuleService.setFuture(true);
458
459 listener.setLatch(1, Type.INSTALL_REQ);
460 listener.setLatch(1, Type.INSTALLED);
461 Intent intent = new MockIntentSubclass(MockIntent.nextId());
462 service.submit(intent);
463 listener.await(Type.INSTALL_REQ);
464 listener.await(Type.INSTALLED);
465 assertEquals(1L, service.getIntentCount());
466 assertEquals(1L, flowRuleService.getFlowRuleCount());
467
468 final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers =
469 extensionService.getCompilers();
470 assertEquals(2, compilers.size());
471 assertNotNull(compilers.get(MockIntentSubclass.class));
472 assertNotNull(compilers.get(MockIntent.class));
473 }
474
475 /**
476 * Tests an intent with no compiler.
477 */
478 @Test
479 public void intentWithoutCompiler() {
480 class IntentNoCompiler extends Intent {
481 IntentNoCompiler() {
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800482 super(APPID, Collections.emptyList());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800483 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800484 }
485
Ray Milkeye9a3e222014-12-03 16:46:06 -0800486 Intent intent = new IntentNoCompiler();
487 listener.setLatch(1, Type.INSTALL_REQ);
488 listener.setLatch(1, Type.FAILED);
489 service.submit(intent);
490 listener.await(Type.INSTALL_REQ);
491 listener.await(Type.FAILED);
492 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800493
Ray Milkeye9a3e222014-12-03 16:46:06 -0800494 /**
495 * Tests an intent with no installer.
496 */
497 @Test
498 public void intentWithoutInstaller() {
499
500 extensionService.unregisterInstaller(MockInstallableIntent.class);
501
502 MockIntent intent = new MockIntent(MockIntent.nextId());
503 listener.setLatch(1, Type.INSTALL_REQ);
504 listener.setLatch(1, Type.FAILED);
505 service.submit(intent);
506 listener.await(Type.INSTALL_REQ);
507 listener.await(Type.FAILED);
508 }
509
510 /**
511 * Tests that the intent fetching methods are correct.
512 */
513 @Test
514 public void testIntentFetching() {
515 List<Intent> intents;
516
517 flowRuleService.setFuture(true);
518
519 intents = Lists.newArrayList(service.getIntents());
520 assertThat(intents, hasSize(0));
521
522 final MockIntent intent1 = new MockIntent(MockIntent.nextId());
523 final MockIntent intent2 = new MockIntent(MockIntent.nextId());
524
525 listener.setLatch(2, Type.INSTALL_REQ);
526 listener.setLatch(2, Type.INSTALLED);
527 service.submit(intent1);
528 service.submit(intent2);
529 listener.await(Type.INSTALL_REQ);
530 listener.await(Type.INSTALL_REQ);
531 listener.await(Type.INSTALLED);
532 listener.await(Type.INSTALLED);
533
534 intents = Lists.newArrayList(service.getIntents());
535 assertThat(intents, hasSize(2));
536
537 assertThat(intents, hasIntentWithId(intent1.id()));
538 assertThat(intents, hasIntentWithId(intent2.id()));
Brian O'Connor427a1762014-11-19 18:40:32 -0800539 }
540}