blob: ea7bc449e8c8742a62b869ac97efe776791d2d16 [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
133 listener.setLatch(1, Type.SUBMITTED);
134 listener.setLatch(1, Type.INSTALLED);
135 Intent intent = new MockIntent(MockIntent.nextId());
136 service.submit(intent);
137 listener.await(Type.SUBMITTED);
138 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);
197 listener.setLatch(1, Type.SUBMITTED);
198 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
209 private static class TestListener implements IntentListener {
210 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
211 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
212
213 @Override
214 public void event(IntentEvent event) {
215 events.put(event.type(), event);
216 if (latchMap.containsKey(event.type())) {
217 latchMap.get(event.type()).countDown();
218 }
219 }
220
221 public int getCounts(IntentEvent.Type type) {
222 return events.get(type).size();
223 }
224
225 public void setLatch(int count, IntentEvent.Type type) {
226 latchMap.put(type, new CountDownLatch(count));
227 }
228
229 public void await(IntentEvent.Type type) {
230 try {
alshabiba9819bf2014-11-30 18:15:52 -0800231 assertTrue("Timed out waiting for: " + type,
232 latchMap.get(type).await(5, TimeUnit.SECONDS));
Brian O'Connor427a1762014-11-19 18:40:32 -0800233 } catch (InterruptedException e) {
234 e.printStackTrace();
235 }
236 }
237 }
238
239 private static class TestIntentTracker implements ObjectiveTrackerService {
240 private TopologyChangeDelegate delegate;
241 @Override
242 public void setDelegate(TopologyChangeDelegate delegate) {
243 this.delegate = delegate;
244 }
245
246 @Override
247 public void unsetDelegate(TopologyChangeDelegate delegate) {
248 if (delegate.equals(this.delegate)) {
249 this.delegate = null;
250 }
251 }
252
253 @Override
254 public void addTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
255 //TODO
256 }
257
258 @Override
259 public void removeTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
260 //TODO
261 }
262 }
263
264 private static class MockIntent extends Intent {
265 private static AtomicLong counter = new AtomicLong(0);
266
267 private final Long number;
268 // Nothing new here
269 public MockIntent(Long number) {
Brian O'Connor520c0522014-11-23 23:50:47 -0800270 super(APPID, null);
Brian O'Connor427a1762014-11-19 18:40:32 -0800271 this.number = number;
272 }
273
274 public Long number() {
275 return number;
276 }
277
278 public static Long nextId() {
279 return counter.getAndIncrement();
280 }
281 }
282
283 private static class MockInstallableIntent extends MockIntent {
284 public MockInstallableIntent(Long number) {
285 super(number);
286 }
287
288 @Override
289 public boolean isInstallable() {
290 return true;
291 }
292 }
293
294 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
295 @Override
296 public List<Intent> compile(MockIntent intent, List<Intent> installable,
297 Set<LinkResourceAllocations> resources) {
298 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
299 }
300 }
301
302 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
303 @Override
304 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
305 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
306 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
307 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
308 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
309 }
310
311 @Override
312 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
313 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
314 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
315 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
316 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
317 }
318
319 @Override
320 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
321 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
322 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
323 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
324 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
325 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
326 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
327 }
328 }
329
330 /**
331 * Hamcrest matcher to check that a conllection of Intents contains an
332 * Intent with the specified Intent Id.
333 */
334 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
335 private final String id;
336
337 public EntryForIntentMatcher(String idValue) {
338 id = idValue;
339 }
340
341 @Override
342 public boolean matchesSafely(Collection<Intent> intents) {
343 return hasItem(Matchers.<Intent>hasProperty("id", equalTo(id))).matches(intents);
344 }
345
346 @Override
347 public void describeTo(Description description) {
348 description.appendText("an intent with id \" ").
349 appendText(id).
350 appendText("\"");
351 }
352 }
353}