blob: 76acae4779002065fb26be36d2ecdeab038519c4 [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;
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080027import org.junit.Ignore;
Brian O'Connor427a1762014-11-19 18:40:32 -080028import org.junit.Test;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.TestApplicationId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.impl.TestCoreManager;
32import org.onosproject.event.impl.TestEventDispatcher;
33import org.onosproject.net.NetworkResource;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.FlowRuleBatchEntry;
36import org.onosproject.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
37import org.onosproject.net.flow.FlowRuleBatchOperation;
38import org.onosproject.net.intent.Intent;
39import org.onosproject.net.intent.IntentCompiler;
40import org.onosproject.net.intent.IntentEvent;
41import org.onosproject.net.intent.IntentEvent.Type;
42import org.onosproject.net.intent.IntentExtensionService;
43import org.onosproject.net.intent.IntentId;
44import org.onosproject.net.intent.IntentInstaller;
45import org.onosproject.net.intent.IntentListener;
46import org.onosproject.net.intent.IntentService;
47import org.onosproject.net.intent.IntentState;
48import org.onosproject.net.intent.IntentTestsMocks;
Ray Milkeyf9af43c2015-02-09 16:45:48 -080049import org.onosproject.net.intent.Key;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.resource.LinkResourceAllocations;
Brian O'Connorabafb502014-12-02 22:26:20 -080051import org.onosproject.store.trivial.impl.SimpleIntentStore;
Brian O'Connor427a1762014-11-19 18:40:32 -080052
Brian O'Connorb499b352015-02-03 16:46:15 -080053import java.util.Collection;
54import java.util.Collections;
55import java.util.List;
56import java.util.Map;
57import java.util.Set;
58import java.util.concurrent.CountDownLatch;
59import java.util.concurrent.TimeUnit;
60import java.util.concurrent.atomic.AtomicLong;
Brian O'Connor427a1762014-11-19 18:40:32 -080061
Ray Milkeye9a3e222014-12-03 16:46:06 -080062import static org.hamcrest.MatcherAssert.assertThat;
63import static org.hamcrest.Matchers.hasSize;
Brian O'Connorb499b352015-02-03 16:46:15 -080064import static org.junit.Assert.*;
Brian O'Connor427a1762014-11-19 18:40:32 -080065import static org.onlab.util.Tools.delay;
Brian O'Connorb499b352015-02-03 16:46:15 -080066import static org.onosproject.net.intent.IntentState.*;
Brian O'Connor427a1762014-11-19 18:40:32 -080067
68/**
69 * Test intent manager and transitions.
70 *
71 * TODO implement the following tests:
72 * - {submit, withdraw, update, replace} intent
Sho SHIMIZU34660962015-01-22 17:58:44 -080073 * - {submit, update, recompiling} intent with failed compilation
Brian O'Connor427a1762014-11-19 18:40:32 -080074 * - failed reservation
75 * - push timeout recovery
76 * - failed items recovery
77 *
78 * in general, verify intents store, flow store, and work queue
79 */
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -080080@Ignore
Brian O'Connor427a1762014-11-19 18:40:32 -080081public class IntentManagerTest {
82
83 private static final ApplicationId APPID = new TestApplicationId("manager-test");
84
85 private IntentManager manager;
86 private MockFlowRuleService flowRuleService;
87
88 protected IntentService service;
89 protected IntentExtensionService extensionService;
90 protected TestListener listener = new TestListener();
91 protected TestIntentCompiler compiler = new TestIntentCompiler();
92 protected TestIntentInstaller installer = new TestIntentInstaller();
93
Ray Milkeye9a3e222014-12-03 16:46:06 -080094 private static class TestListener implements IntentListener {
95 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
96 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
97
98 @Override
99 public void event(IntentEvent event) {
100 events.put(event.type(), event);
101 if (latchMap.containsKey(event.type())) {
102 latchMap.get(event.type()).countDown();
103 }
104 }
105
106 public int getCounts(IntentEvent.Type type) {
107 return events.get(type).size();
108 }
109
110 public void setLatch(int count, IntentEvent.Type type) {
111 latchMap.put(type, new CountDownLatch(count));
112 }
113
114 public void await(IntentEvent.Type type) {
115 try {
116 assertTrue("Timed out waiting for: " + type,
117 latchMap.get(type).await(5, TimeUnit.SECONDS));
118 } catch (InterruptedException e) {
119 e.printStackTrace();
120 }
121 }
122 }
123
124 private static class TestIntentTracker implements ObjectiveTrackerService {
125 private TopologyChangeDelegate delegate;
126 @Override
127 public void setDelegate(TopologyChangeDelegate delegate) {
128 this.delegate = delegate;
129 }
130
131 @Override
132 public void unsetDelegate(TopologyChangeDelegate delegate) {
133 if (delegate.equals(this.delegate)) {
134 this.delegate = null;
135 }
136 }
137
138 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800139 public void addTrackedResources(Key key, Collection<NetworkResource> resources) {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800140 //TODO
141 }
142
143 @Override
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800144 public void removeTrackedResources(Key key, Collection<NetworkResource> resources) {
Ray Milkeye9a3e222014-12-03 16:46:06 -0800145 //TODO
146 }
147 }
148
149 private static class MockIntent extends Intent {
150 private static AtomicLong counter = new AtomicLong(0);
151
152 private final Long number;
153 // Nothing new here
154 public MockIntent(Long number) {
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800155 super(APPID, Collections.emptyList());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800156 this.number = number;
157 }
158
159 public Long number() {
160 return number;
161 }
162
163 public static Long nextId() {
164 return counter.getAndIncrement();
165 }
166 }
167
168 private static class MockInstallableIntent extends MockIntent {
169 public MockInstallableIntent(Long number) {
170 super(number);
171 }
172
173 @Override
174 public boolean isInstallable() {
175 return true;
176 }
177 }
178
179 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
180 @Override
181 public List<Intent> compile(MockIntent intent, List<Intent> installable,
182 Set<LinkResourceAllocations> resources) {
183 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
184 }
185 }
186
187 private static class TestIntentCompilerError implements IntentCompiler<MockIntent> {
188 @Override
189 public List<Intent> compile(MockIntent intent, List<Intent> installable,
190 Set<LinkResourceAllocations> resources) {
191 throw new IntentCompilationException("Compilation always fails");
192 }
193 }
194
195 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
196 @Override
197 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
198 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
199 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
200 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800201 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800202 }
203
204 @Override
205 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
206 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
207 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
208 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800209 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800210 }
211
212 @Override
213 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
214 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
215 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
216 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
217 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
218 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
Brian O'Connor72cb19a2015-01-16 16:14:41 -0800219 return Lists.newArrayList(new FlowRuleBatchOperation(rules, fr.deviceId(), 0));
Ray Milkeye9a3e222014-12-03 16:46:06 -0800220 }
221 }
222
223 private static class TestIntentErrorInstaller implements IntentInstaller<MockInstallableIntent> {
224 @Override
225 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
226 throw new IntentInstallationException("install() always fails");
227 }
228
229 @Override
230 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
231 throw new IntentRemovalException("uninstall() always fails");
232 }
233
234 @Override
235 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
236 throw new IntentInstallationException("replace() always fails");
237 }
238 }
239
240 /**
241 * Hamcrest matcher to check that a conllection of Intents contains an
242 * Intent with the specified Intent Id.
243 */
244 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
245 private final IntentId id;
246
247 public EntryForIntentMatcher(IntentId idValue) {
248 id = idValue;
249 }
250
251 @Override
252 public boolean matchesSafely(Collection<Intent> intents) {
253 for (Intent intent : intents) {
254 if (intent.id().equals(id)) {
255 return true;
256 }
257 }
258 return false;
259 }
260
261 @Override
262 public void describeTo(Description description) {
263 description.appendText("an intent with id \" ").
264 appendText(id.toString()).
265 appendText("\"");
266 }
267 }
268
269 private static EntryForIntentMatcher hasIntentWithId(IntentId id) {
270 return new EntryForIntentMatcher(id);
271 }
272
Brian O'Connor427a1762014-11-19 18:40:32 -0800273 @Before
274 public void setUp() {
275 manager = new IntentManager();
276 flowRuleService = new MockFlowRuleService();
277 manager.store = new SimpleIntentStore();
Brian O'Connor427a1762014-11-19 18:40:32 -0800278 manager.eventDispatcher = new TestEventDispatcher();
279 manager.trackerService = new TestIntentTracker();
280 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -0800281 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -0800282 service = manager;
283 extensionService = manager;
284
285 manager.activate();
286 service.addListener(listener);
287 extensionService.registerCompiler(MockIntent.class, compiler);
288 extensionService.registerInstaller(MockInstallableIntent.class, installer);
289
290 assertTrue("store should be empty",
291 Sets.newHashSet(service.getIntents()).isEmpty());
292 assertEquals(0L, flowRuleService.getFlowRuleCount());
293 }
294
295 @After
296 public void tearDown() {
297 // verify that all intents are parked and the batch operation is unblocked
298 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
299 for (Intent i : service.getIntents()) {
Ray Milkeyf9af43c2015-02-09 16:45:48 -0800300 IntentState state = service.getIntentState(i.key());
Brian O'Connor427a1762014-11-19 18:40:32 -0800301 assertTrue("Intent " + i.id() + " is in invalid state " + state,
302 parked.contains(state));
303 }
304 //the batch has not yet been removed when we receive the last event
305 // FIXME: this doesn't guarantee to avoid the race
Brian O'Connorb499b352015-02-03 16:46:15 -0800306
307 //FIXME
308// for (int tries = 0; tries < 10; tries++) {
309// if (manager.batchService.getPendingOperations().isEmpty()) {
310// break;
311// }
312// delay(10);
313// }
314// assertTrue("There are still pending batch operations.",
315// manager.batchService.getPendingOperations().isEmpty());
Brian O'Connor427a1762014-11-19 18:40:32 -0800316
317 extensionService.unregisterCompiler(MockIntent.class);
318 extensionService.unregisterInstaller(MockInstallableIntent.class);
319 service.removeListener(listener);
320 manager.deactivate();
321 // TODO null the other refs?
322 }
323
324 @Test
325 public void submitIntent() {
326 flowRuleService.setFuture(true);
327
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800328 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800329 listener.setLatch(1, Type.INSTALLED);
330 Intent intent = new MockIntent(MockIntent.nextId());
331 service.submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800332 listener.await(Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800333 listener.await(Type.INSTALLED);
334 assertEquals(1L, service.getIntentCount());
335 assertEquals(1L, flowRuleService.getFlowRuleCount());
336 }
337
338 @Test
339 public void withdrawIntent() {
340 flowRuleService.setFuture(true);
341
342 listener.setLatch(1, Type.INSTALLED);
343 Intent intent = new MockIntent(MockIntent.nextId());
344 service.submit(intent);
345 listener.await(Type.INSTALLED);
346 assertEquals(1L, service.getIntentCount());
347 assertEquals(1L, flowRuleService.getFlowRuleCount());
348
349 listener.setLatch(1, Type.WITHDRAWN);
350 service.withdraw(intent);
351 listener.await(Type.WITHDRAWN);
Jonathan Hart4fd4ebb2015-02-04 17:38:48 -0800352 delay(10000); //FIXME this is a race
353 //assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800354 assertEquals(0L, flowRuleService.getFlowRuleCount());
355 }
356
357 @Test
358 public void stressSubmitWithdraw() {
359 flowRuleService.setFuture(true);
360
361 int count = 500;
362
363 listener.setLatch(count, Type.INSTALLED);
364 listener.setLatch(count, Type.WITHDRAWN);
365
366 Intent intent = new MockIntent(MockIntent.nextId());
367 for (int i = 0; i < count; i++) {
368 service.submit(intent);
369 service.withdraw(intent);
370 }
371
372 listener.await(Type.INSTALLED);
373 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800374 delay(10); //FIXME this is a race
375 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800376 assertEquals(0L, flowRuleService.getFlowRuleCount());
377 }
378
Ray Milkey93508c22014-12-02 11:35:56 -0800379 /**
380 * Tests for proper behavior of installation of an intent that triggers
381 * a compilation error.
382 */
383 @Test
384 public void errorIntentCompile() {
385 final TestIntentCompilerError errorCompiler = new TestIntentCompilerError();
386 extensionService.registerCompiler(MockIntent.class, errorCompiler);
387 MockIntent intent = new MockIntent(MockIntent.nextId());
388 listener.setLatch(1, Type.INSTALL_REQ);
389 listener.setLatch(1, Type.FAILED);
390 service.submit(intent);
391 listener.await(Type.INSTALL_REQ);
392 listener.await(Type.FAILED);
393 }
394
395 /**
396 * Tests handling a future that contains an error as a result of
397 * installing an intent.
398 */
399 @Test
400 public void errorIntentInstallFromFlows() {
401 final Long id = MockIntent.nextId();
Brian O'Connor5811ac22015-02-09 19:17:07 -0800402 flowRuleService.setFuture(false);
Ray Milkey93508c22014-12-02 11:35:56 -0800403 MockIntent intent = new MockIntent(id);
404 listener.setLatch(1, Type.FAILED);
405 listener.setLatch(1, Type.INSTALL_REQ);
406 service.submit(intent);
407 listener.await(Type.INSTALL_REQ);
408 delay(10); // need to make sure we have some failed futures returned first
Brian O'Connor5811ac22015-02-09 19:17:07 -0800409 flowRuleService.setFuture(true);
Ray Milkey93508c22014-12-02 11:35:56 -0800410 listener.await(Type.FAILED);
411 }
412
413 /**
414 * Tests handling of an error that is generated by the intent installer.
415 */
416 @Test
417 public void errorIntentInstallFromInstaller() {
418 final TestIntentErrorInstaller errorInstaller = new TestIntentErrorInstaller();
419 extensionService.registerInstaller(MockInstallableIntent.class, errorInstaller);
420 MockIntent intent = new MockIntent(MockIntent.nextId());
421 listener.setLatch(1, Type.INSTALL_REQ);
422 listener.setLatch(1, Type.FAILED);
423 service.submit(intent);
424 listener.await(Type.INSTALL_REQ);
425 listener.await(Type.FAILED);
Ray Milkey93508c22014-12-02 11:35:56 -0800426 }
427
Brian O'Connor427a1762014-11-19 18:40:32 -0800428 /**
Ray Milkeye9a3e222014-12-03 16:46:06 -0800429 * Tests handling a future that contains an unresolvable error as a result of
430 * installing an intent.
Brian O'Connor427a1762014-11-19 18:40:32 -0800431 */
Ray Milkeye9a3e222014-12-03 16:46:06 -0800432 @Test
433 public void errorIntentInstallNeverTrue() {
434 final Long id = MockIntent.nextId();
Brian O'Connor5811ac22015-02-09 19:17:07 -0800435 flowRuleService.setFuture(false);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800436 MockIntent intent = new MockIntent(id);
437 listener.setLatch(1, Type.WITHDRAWN);
438 listener.setLatch(1, Type.INSTALL_REQ);
439 service.submit(intent);
440 listener.await(Type.INSTALL_REQ);
441 // The delay here forces the retry loop in the intent manager to time out
442 delay(100);
Brian O'Connor5811ac22015-02-09 19:17:07 -0800443 flowRuleService.setFuture(false);
Ray Milkeye9a3e222014-12-03 16:46:06 -0800444 service.withdraw(intent);
445 listener.await(Type.WITHDRAWN);
446 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800447
Ray Milkeye9a3e222014-12-03 16:46:06 -0800448 /**
449 * Tests that a compiler for a subclass of an intent that already has a
450 * compiler is automatically added.
451 */
452 @Test
453 public void intentSubclassCompile() {
454 class MockIntentSubclass extends MockIntent {
455 public MockIntentSubclass(Long number) {
456 super(number);
457 }
458 }
459 flowRuleService.setFuture(true);
460
461 listener.setLatch(1, Type.INSTALL_REQ);
462 listener.setLatch(1, Type.INSTALLED);
463 Intent intent = new MockIntentSubclass(MockIntent.nextId());
464 service.submit(intent);
465 listener.await(Type.INSTALL_REQ);
466 listener.await(Type.INSTALLED);
467 assertEquals(1L, service.getIntentCount());
468 assertEquals(1L, flowRuleService.getFlowRuleCount());
469
470 final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers =
471 extensionService.getCompilers();
472 assertEquals(2, compilers.size());
473 assertNotNull(compilers.get(MockIntentSubclass.class));
474 assertNotNull(compilers.get(MockIntent.class));
475 }
476
477 /**
478 * Tests an intent with no compiler.
479 */
480 @Test
481 public void intentWithoutCompiler() {
482 class IntentNoCompiler extends Intent {
483 IntentNoCompiler() {
Sho SHIMIZUd7d18002015-01-21 14:37:14 -0800484 super(APPID, Collections.emptyList());
Ray Milkeye9a3e222014-12-03 16:46:06 -0800485 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800486 }
487
Ray Milkeye9a3e222014-12-03 16:46:06 -0800488 Intent intent = new IntentNoCompiler();
489 listener.setLatch(1, Type.INSTALL_REQ);
490 listener.setLatch(1, Type.FAILED);
491 service.submit(intent);
492 listener.await(Type.INSTALL_REQ);
493 listener.await(Type.FAILED);
494 }
Brian O'Connor427a1762014-11-19 18:40:32 -0800495
Ray Milkeye9a3e222014-12-03 16:46:06 -0800496 /**
497 * Tests an intent with no installer.
498 */
499 @Test
500 public void intentWithoutInstaller() {
501
502 extensionService.unregisterInstaller(MockInstallableIntent.class);
503
504 MockIntent intent = new MockIntent(MockIntent.nextId());
505 listener.setLatch(1, Type.INSTALL_REQ);
506 listener.setLatch(1, Type.FAILED);
507 service.submit(intent);
508 listener.await(Type.INSTALL_REQ);
509 listener.await(Type.FAILED);
510 }
511
512 /**
513 * Tests that the intent fetching methods are correct.
514 */
515 @Test
516 public void testIntentFetching() {
517 List<Intent> intents;
518
519 flowRuleService.setFuture(true);
520
521 intents = Lists.newArrayList(service.getIntents());
522 assertThat(intents, hasSize(0));
523
524 final MockIntent intent1 = new MockIntent(MockIntent.nextId());
525 final MockIntent intent2 = new MockIntent(MockIntent.nextId());
526
527 listener.setLatch(2, Type.INSTALL_REQ);
528 listener.setLatch(2, Type.INSTALLED);
529 service.submit(intent1);
530 service.submit(intent2);
531 listener.await(Type.INSTALL_REQ);
532 listener.await(Type.INSTALL_REQ);
533 listener.await(Type.INSTALLED);
534 listener.await(Type.INSTALLED);
535
536 intents = Lists.newArrayList(service.getIntents());
537 assertThat(intents, hasSize(2));
538
539 assertThat(intents, hasIntentWithId(intent1.id()));
540 assertThat(intents, hasIntentWithId(intent2.id()));
Brian O'Connor427a1762014-11-19 18:40:32 -0800541 }
542}