blob: 87092958cbe3cb481df101374179289c7ea29ef5 [file] [log] [blame]
Brian O'Connor427a1762014-11-19 18:40:32 -08001package org.onlab.onos.net.intent.impl;
2
3import com.google.common.collect.HashMultimap;
4import com.google.common.collect.Lists;
5import com.google.common.collect.Maps;
6import com.google.common.collect.Multimap;
7import com.google.common.collect.Sets;
alshabiba9819bf2014-11-30 18:15:52 -08008
Brian O'Connor427a1762014-11-19 18:40:32 -08009import org.hamcrest.Description;
10import org.hamcrest.Matchers;
11import org.hamcrest.TypeSafeMatcher;
12import org.junit.After;
13import org.junit.Before;
14import org.junit.Test;
15import org.onlab.onos.TestApplicationId;
16import org.onlab.onos.core.ApplicationId;
Brian O'Connor520c0522014-11-23 23:50:47 -080017import org.onlab.onos.core.impl.TestCoreManager;
Brian O'Connor427a1762014-11-19 18:40:32 -080018import org.onlab.onos.event.impl.TestEventDispatcher;
19import org.onlab.onos.net.NetworkResource;
20import org.onlab.onos.net.flow.FlowRule;
21import org.onlab.onos.net.flow.FlowRuleBatchEntry;
22import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
23import org.onlab.onos.net.flow.FlowRuleBatchOperation;
24import org.onlab.onos.net.intent.Intent;
25import org.onlab.onos.net.intent.IntentCompiler;
26import org.onlab.onos.net.intent.IntentEvent;
27import org.onlab.onos.net.intent.IntentEvent.Type;
28import org.onlab.onos.net.intent.IntentExtensionService;
29import org.onlab.onos.net.intent.IntentId;
30import org.onlab.onos.net.intent.IntentInstaller;
31import org.onlab.onos.net.intent.IntentListener;
32import org.onlab.onos.net.intent.IntentService;
33import org.onlab.onos.net.intent.IntentState;
34import org.onlab.onos.net.intent.IntentTestsMocks;
35import org.onlab.onos.net.resource.LinkResourceAllocations;
36import org.onlab.onos.store.trivial.impl.SimpleIntentBatchQueue;
37import org.onlab.onos.store.trivial.impl.SimpleIntentStore;
38
39import java.util.Collection;
40import java.util.List;
41import java.util.Map;
42import java.util.Set;
43import java.util.concurrent.CountDownLatch;
alshabiba9819bf2014-11-30 18:15:52 -080044import java.util.concurrent.TimeUnit;
Brian O'Connor427a1762014-11-19 18:40:32 -080045import java.util.concurrent.atomic.AtomicLong;
46
47import static org.hamcrest.Matchers.equalTo;
48import static org.hamcrest.Matchers.hasItem;
49import static org.junit.Assert.assertEquals;
50import static org.junit.Assert.assertTrue;
51import static org.onlab.onos.net.intent.IntentState.*;
52import static org.onlab.util.Tools.delay;
53
54/**
55 * Test intent manager and transitions.
56 *
57 * TODO implement the following tests:
58 * - {submit, withdraw, update, replace} intent
59 * - {submit, update, recomiling} intent with failed compilation
60 * - failed reservation
61 * - push timeout recovery
62 * - failed items recovery
63 *
64 * in general, verify intents store, flow store, and work queue
65 */
66public class IntentManagerTest {
67
68 private static final ApplicationId APPID = new TestApplicationId("manager-test");
69
70 private IntentManager manager;
71 private MockFlowRuleService flowRuleService;
72
73 protected IntentService service;
74 protected IntentExtensionService extensionService;
75 protected TestListener listener = new TestListener();
76 protected TestIntentCompiler compiler = new TestIntentCompiler();
77 protected TestIntentInstaller installer = new TestIntentInstaller();
78
79 @Before
80 public void setUp() {
81 manager = new IntentManager();
82 flowRuleService = new MockFlowRuleService();
83 manager.store = new SimpleIntentStore();
84 manager.batchService = new SimpleIntentBatchQueue();
85 manager.eventDispatcher = new TestEventDispatcher();
86 manager.trackerService = new TestIntentTracker();
87 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -080088 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -080089 service = manager;
90 extensionService = manager;
91
92 manager.activate();
93 service.addListener(listener);
94 extensionService.registerCompiler(MockIntent.class, compiler);
95 extensionService.registerInstaller(MockInstallableIntent.class, installer);
96
97 assertTrue("store should be empty",
98 Sets.newHashSet(service.getIntents()).isEmpty());
99 assertEquals(0L, flowRuleService.getFlowRuleCount());
100 }
101
102 @After
103 public void tearDown() {
104 // verify that all intents are parked and the batch operation is unblocked
105 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
106 for (Intent i : service.getIntents()) {
107 IntentState state = service.getIntentState(i.id());
108 assertTrue("Intent " + i.id() + " is in invalid state " + state,
109 parked.contains(state));
110 }
111 //the batch has not yet been removed when we receive the last event
112 // FIXME: this doesn't guarantee to avoid the race
113 for (int tries = 0; tries < 10; tries++) {
Brian O'Connor86f6f7f2014-12-01 17:02:45 -0800114 if (manager.batchService.getPendingOperations().isEmpty()) {
Brian O'Connor427a1762014-11-19 18:40:32 -0800115 break;
116 }
117 delay(10);
118 }
119 assertTrue("There are still pending batch operations.",
120 manager.batchService.getPendingOperations().isEmpty());
Brian O'Connor427a1762014-11-19 18:40:32 -0800121
122 extensionService.unregisterCompiler(MockIntent.class);
123 extensionService.unregisterInstaller(MockInstallableIntent.class);
124 service.removeListener(listener);
125 manager.deactivate();
126 // TODO null the other refs?
127 }
128
129 @Test
130 public void submitIntent() {
131 flowRuleService.setFuture(true);
132
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800133 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800134 listener.setLatch(1, Type.INSTALLED);
135 Intent intent = new MockIntent(MockIntent.nextId());
136 service.submit(intent);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800137 listener.await(Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800138 listener.await(Type.INSTALLED);
139 assertEquals(1L, service.getIntentCount());
140 assertEquals(1L, flowRuleService.getFlowRuleCount());
141 }
142
143 @Test
144 public void withdrawIntent() {
145 flowRuleService.setFuture(true);
146
147 listener.setLatch(1, Type.INSTALLED);
148 Intent intent = new MockIntent(MockIntent.nextId());
149 service.submit(intent);
150 listener.await(Type.INSTALLED);
151 assertEquals(1L, service.getIntentCount());
152 assertEquals(1L, flowRuleService.getFlowRuleCount());
153
154 listener.setLatch(1, Type.WITHDRAWN);
155 service.withdraw(intent);
156 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800157 delay(10); //FIXME this is a race
158 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800159 assertEquals(0L, flowRuleService.getFlowRuleCount());
160 }
161
162 @Test
163 public void stressSubmitWithdraw() {
164 flowRuleService.setFuture(true);
165
166 int count = 500;
167
168 listener.setLatch(count, Type.INSTALLED);
169 listener.setLatch(count, Type.WITHDRAWN);
170
171 Intent intent = new MockIntent(MockIntent.nextId());
172 for (int i = 0; i < count; i++) {
173 service.submit(intent);
174 service.withdraw(intent);
175 }
176
177 listener.await(Type.INSTALLED);
178 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800179 delay(10); //FIXME this is a race
180 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800181 assertEquals(0L, flowRuleService.getFlowRuleCount());
182 }
183
184 @Test
185 public void replaceIntent() {
186 flowRuleService.setFuture(true);
187
188 MockIntent intent = new MockIntent(MockIntent.nextId());
189 listener.setLatch(1, Type.INSTALLED);
190 service.submit(intent);
191 listener.await(Type.INSTALLED);
192 assertEquals(1L, service.getIntentCount());
193 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
194
195 MockIntent intent2 = new MockIntent(MockIntent.nextId());
196 listener.setLatch(1, Type.WITHDRAWN);
Brian O'Connor7a71d5d2014-12-02 00:12:27 -0800197 listener.setLatch(1, Type.INSTALL_REQ);
Brian O'Connor427a1762014-11-19 18:40:32 -0800198 listener.setLatch(1, Type.INSTALLED);
199 service.replace(intent.id(), intent2);
200 listener.await(Type.WITHDRAWN);
201 listener.await(Type.INSTALLED);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800202 delay(10); //FIXME this is a race
203 assertEquals(1L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800204 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
205 assertEquals(intent2.number().intValue(),
206 flowRuleService.flows.iterator().next().priority());
207 }
208
Ray Milkey93508c22014-12-02 11:35:56 -0800209 /**
210 * Tests for proper behavior of installation of an intent that triggers
211 * a compilation error.
212 */
213 @Test
214 public void errorIntentCompile() {
215 final TestIntentCompilerError errorCompiler = new TestIntentCompilerError();
216 extensionService.registerCompiler(MockIntent.class, errorCompiler);
217 MockIntent intent = new MockIntent(MockIntent.nextId());
218 listener.setLatch(1, Type.INSTALL_REQ);
219 listener.setLatch(1, Type.FAILED);
220 service.submit(intent);
221 listener.await(Type.INSTALL_REQ);
222 listener.await(Type.FAILED);
223 }
224
225 /**
226 * Tests handling a future that contains an error as a result of
227 * installing an intent.
228 */
229 @Test
230 public void errorIntentInstallFromFlows() {
231 final Long id = MockIntent.nextId();
232 flowRuleService.setFuture(false, 1);
233 MockIntent intent = new MockIntent(id);
234 listener.setLatch(1, Type.FAILED);
235 listener.setLatch(1, Type.INSTALL_REQ);
236 service.submit(intent);
237 listener.await(Type.INSTALL_REQ);
238 delay(10); // need to make sure we have some failed futures returned first
239 flowRuleService.setFuture(true, 0);
240 listener.await(Type.FAILED);
241 }
242
243 /**
244 * Tests handling of an error that is generated by the intent installer.
245 */
246 @Test
247 public void errorIntentInstallFromInstaller() {
248 final TestIntentErrorInstaller errorInstaller = new TestIntentErrorInstaller();
249 extensionService.registerInstaller(MockInstallableIntent.class, errorInstaller);
250 MockIntent intent = new MockIntent(MockIntent.nextId());
251 listener.setLatch(1, Type.INSTALL_REQ);
252 listener.setLatch(1, Type.FAILED);
253 service.submit(intent);
254 listener.await(Type.INSTALL_REQ);
255 listener.await(Type.FAILED);
256
257 }
258
Brian O'Connor427a1762014-11-19 18:40:32 -0800259 private static class TestListener implements IntentListener {
260 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
261 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
262
263 @Override
264 public void event(IntentEvent event) {
265 events.put(event.type(), event);
266 if (latchMap.containsKey(event.type())) {
267 latchMap.get(event.type()).countDown();
268 }
269 }
270
271 public int getCounts(IntentEvent.Type type) {
272 return events.get(type).size();
273 }
274
275 public void setLatch(int count, IntentEvent.Type type) {
276 latchMap.put(type, new CountDownLatch(count));
277 }
278
279 public void await(IntentEvent.Type type) {
280 try {
alshabiba9819bf2014-11-30 18:15:52 -0800281 assertTrue("Timed out waiting for: " + type,
Ray Milkey93508c22014-12-02 11:35:56 -0800282 latchMap.get(type).await(5, TimeUnit.SECONDS));
Brian O'Connor427a1762014-11-19 18:40:32 -0800283 } catch (InterruptedException e) {
284 e.printStackTrace();
285 }
286 }
287 }
288
289 private static class TestIntentTracker implements ObjectiveTrackerService {
290 private TopologyChangeDelegate delegate;
291 @Override
292 public void setDelegate(TopologyChangeDelegate delegate) {
293 this.delegate = delegate;
294 }
295
296 @Override
297 public void unsetDelegate(TopologyChangeDelegate delegate) {
298 if (delegate.equals(this.delegate)) {
299 this.delegate = null;
300 }
301 }
302
303 @Override
304 public void addTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
305 //TODO
306 }
307
308 @Override
309 public void removeTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
310 //TODO
311 }
312 }
313
314 private static class MockIntent extends Intent {
315 private static AtomicLong counter = new AtomicLong(0);
316
317 private final Long number;
318 // Nothing new here
319 public MockIntent(Long number) {
Brian O'Connor520c0522014-11-23 23:50:47 -0800320 super(APPID, null);
Brian O'Connor427a1762014-11-19 18:40:32 -0800321 this.number = number;
322 }
323
324 public Long number() {
325 return number;
326 }
327
328 public static Long nextId() {
329 return counter.getAndIncrement();
330 }
331 }
332
333 private static class MockInstallableIntent extends MockIntent {
334 public MockInstallableIntent(Long number) {
335 super(number);
336 }
337
338 @Override
339 public boolean isInstallable() {
340 return true;
341 }
342 }
343
344 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
345 @Override
346 public List<Intent> compile(MockIntent intent, List<Intent> installable,
347 Set<LinkResourceAllocations> resources) {
348 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
349 }
350 }
351
Ray Milkey93508c22014-12-02 11:35:56 -0800352 private static class TestIntentCompilerError implements IntentCompiler<MockIntent> {
353 @Override
354 public List<Intent> compile(MockIntent intent, List<Intent> installable,
355 Set<LinkResourceAllocations> resources) {
356 throw new IntentCompilationException("Compilation always fails");
357 }
358 }
359
Brian O'Connor427a1762014-11-19 18:40:32 -0800360 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
361 @Override
362 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
363 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
364 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
365 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
366 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
367 }
368
369 @Override
370 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
371 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
372 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
373 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
374 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
375 }
376
377 @Override
378 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
379 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
380 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
381 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
382 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
383 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
384 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
385 }
386 }
387
Ray Milkey93508c22014-12-02 11:35:56 -0800388 private static class TestIntentErrorInstaller implements IntentInstaller<MockInstallableIntent> {
389 @Override
390 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
391 throw new IntentInstallationException("install() always fails");
392 }
393
394 @Override
395 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
396 throw new IntentRemovalException("uninstall() always fails");
397 }
398
399 @Override
400 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
401 throw new IntentInstallationException("replace() always fails");
402 }
403 }
404
Brian O'Connor427a1762014-11-19 18:40:32 -0800405 /**
406 * Hamcrest matcher to check that a conllection of Intents contains an
407 * Intent with the specified Intent Id.
408 */
409 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
410 private final String id;
411
412 public EntryForIntentMatcher(String idValue) {
413 id = idValue;
414 }
415
416 @Override
417 public boolean matchesSafely(Collection<Intent> intents) {
418 return hasItem(Matchers.<Intent>hasProperty("id", equalTo(id))).matches(intents);
419 }
420
421 @Override
422 public void describeTo(Description description) {
423 description.appendText("an intent with id \" ").
424 appendText(id).
425 appendText("\"");
426 }
427 }
428}