blob: dc7383b3f3112e7679e8e6b59b33754aebdee6c2 [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;
8import org.hamcrest.Description;
9import org.hamcrest.Matchers;
10import org.hamcrest.TypeSafeMatcher;
11import org.junit.After;
12import org.junit.Before;
13import org.junit.Test;
14import org.onlab.onos.TestApplicationId;
15import org.onlab.onos.core.ApplicationId;
Brian O'Connor520c0522014-11-23 23:50:47 -080016import org.onlab.onos.core.impl.TestCoreManager;
Brian O'Connor427a1762014-11-19 18:40:32 -080017import org.onlab.onos.event.impl.TestEventDispatcher;
18import org.onlab.onos.net.NetworkResource;
19import org.onlab.onos.net.flow.FlowRule;
20import org.onlab.onos.net.flow.FlowRuleBatchEntry;
21import org.onlab.onos.net.flow.FlowRuleBatchEntry.FlowRuleOperation;
22import org.onlab.onos.net.flow.FlowRuleBatchOperation;
23import org.onlab.onos.net.intent.Intent;
24import org.onlab.onos.net.intent.IntentCompiler;
25import org.onlab.onos.net.intent.IntentEvent;
26import org.onlab.onos.net.intent.IntentEvent.Type;
27import org.onlab.onos.net.intent.IntentExtensionService;
28import org.onlab.onos.net.intent.IntentId;
29import org.onlab.onos.net.intent.IntentInstaller;
30import org.onlab.onos.net.intent.IntentListener;
31import org.onlab.onos.net.intent.IntentService;
32import org.onlab.onos.net.intent.IntentState;
33import org.onlab.onos.net.intent.IntentTestsMocks;
34import org.onlab.onos.net.resource.LinkResourceAllocations;
35import org.onlab.onos.store.trivial.impl.SimpleIntentBatchQueue;
36import org.onlab.onos.store.trivial.impl.SimpleIntentStore;
37
38import java.util.Collection;
39import java.util.List;
40import java.util.Map;
41import java.util.Set;
42import java.util.concurrent.CountDownLatch;
43import java.util.concurrent.atomic.AtomicLong;
44
45import static org.hamcrest.Matchers.equalTo;
46import static org.hamcrest.Matchers.hasItem;
47import static org.junit.Assert.assertEquals;
48import static org.junit.Assert.assertTrue;
49import static org.onlab.onos.net.intent.IntentState.*;
50import static org.onlab.util.Tools.delay;
51
52/**
53 * Test intent manager and transitions.
54 *
55 * TODO implement the following tests:
56 * - {submit, withdraw, update, replace} intent
57 * - {submit, update, recomiling} intent with failed compilation
58 * - failed reservation
59 * - push timeout recovery
60 * - failed items recovery
61 *
62 * in general, verify intents store, flow store, and work queue
63 */
64public class IntentManagerTest {
65
66 private static final ApplicationId APPID = new TestApplicationId("manager-test");
67
68 private IntentManager manager;
69 private MockFlowRuleService flowRuleService;
70
71 protected IntentService service;
72 protected IntentExtensionService extensionService;
73 protected TestListener listener = new TestListener();
74 protected TestIntentCompiler compiler = new TestIntentCompiler();
75 protected TestIntentInstaller installer = new TestIntentInstaller();
76
77 @Before
78 public void setUp() {
79 manager = new IntentManager();
80 flowRuleService = new MockFlowRuleService();
81 manager.store = new SimpleIntentStore();
82 manager.batchService = new SimpleIntentBatchQueue();
83 manager.eventDispatcher = new TestEventDispatcher();
84 manager.trackerService = new TestIntentTracker();
85 manager.flowRuleService = flowRuleService;
Brian O'Connor520c0522014-11-23 23:50:47 -080086 manager.coreService = new TestCoreManager();
Brian O'Connor427a1762014-11-19 18:40:32 -080087 service = manager;
88 extensionService = manager;
89
90 manager.activate();
91 service.addListener(listener);
92 extensionService.registerCompiler(MockIntent.class, compiler);
93 extensionService.registerInstaller(MockInstallableIntent.class, installer);
94
95 assertTrue("store should be empty",
96 Sets.newHashSet(service.getIntents()).isEmpty());
97 assertEquals(0L, flowRuleService.getFlowRuleCount());
98 }
99
100 @After
101 public void tearDown() {
102 // verify that all intents are parked and the batch operation is unblocked
103 Set<IntentState> parked = Sets.newHashSet(INSTALLED, WITHDRAWN, FAILED);
104 for (Intent i : service.getIntents()) {
105 IntentState state = service.getIntentState(i.id());
106 assertTrue("Intent " + i.id() + " is in invalid state " + state,
107 parked.contains(state));
108 }
109 //the batch has not yet been removed when we receive the last event
110 // FIXME: this doesn't guarantee to avoid the race
111 for (int tries = 0; tries < 10; tries++) {
112 if (manager.batchService.getPendingOperations().isEmpty() &&
113 manager.batchService.getCurrentOperations().isEmpty()) {
114 break;
115 }
116 delay(10);
117 }
118 assertTrue("There are still pending batch operations.",
119 manager.batchService.getPendingOperations().isEmpty());
120 assertTrue("There are still outstanding batch operations.",
121 manager.batchService.getCurrentOperations().isEmpty());
122
123 extensionService.unregisterCompiler(MockIntent.class);
124 extensionService.unregisterInstaller(MockInstallableIntent.class);
125 service.removeListener(listener);
126 manager.deactivate();
127 // TODO null the other refs?
128 }
129
130 @Test
131 public void submitIntent() {
132 flowRuleService.setFuture(true);
133
134 listener.setLatch(1, Type.SUBMITTED);
135 listener.setLatch(1, Type.INSTALLED);
136 Intent intent = new MockIntent(MockIntent.nextId());
137 service.submit(intent);
138 listener.await(Type.SUBMITTED);
139 listener.await(Type.INSTALLED);
140 assertEquals(1L, service.getIntentCount());
141 assertEquals(1L, flowRuleService.getFlowRuleCount());
142 }
143
144 @Test
145 public void withdrawIntent() {
146 flowRuleService.setFuture(true);
147
148 listener.setLatch(1, Type.INSTALLED);
149 Intent intent = new MockIntent(MockIntent.nextId());
150 service.submit(intent);
151 listener.await(Type.INSTALLED);
152 assertEquals(1L, service.getIntentCount());
153 assertEquals(1L, flowRuleService.getFlowRuleCount());
154
155 listener.setLatch(1, Type.WITHDRAWN);
156 service.withdraw(intent);
157 listener.await(Type.WITHDRAWN);
158 assertEquals(1L, service.getIntentCount());
159 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);
179 assertEquals(1L, service.getIntentCount());
180 assertEquals(0L, flowRuleService.getFlowRuleCount());
181 }
182
183 @Test
184 public void replaceIntent() {
185 flowRuleService.setFuture(true);
186
187 MockIntent intent = new MockIntent(MockIntent.nextId());
188 listener.setLatch(1, Type.INSTALLED);
189 service.submit(intent);
190 listener.await(Type.INSTALLED);
191 assertEquals(1L, service.getIntentCount());
192 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
193
194 MockIntent intent2 = new MockIntent(MockIntent.nextId());
195 listener.setLatch(1, Type.WITHDRAWN);
196 listener.setLatch(1, Type.SUBMITTED);
197 listener.setLatch(1, Type.INSTALLED);
198 service.replace(intent.id(), intent2);
199 listener.await(Type.WITHDRAWN);
200 listener.await(Type.INSTALLED);
201 assertEquals(2L, service.getIntentCount());
202 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
203 assertEquals(intent2.number().intValue(),
204 flowRuleService.flows.iterator().next().priority());
205 }
206
207 private static class TestListener implements IntentListener {
208 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
209 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
210
211 @Override
212 public void event(IntentEvent event) {
213 events.put(event.type(), event);
214 if (latchMap.containsKey(event.type())) {
215 latchMap.get(event.type()).countDown();
216 }
217 }
218
219 public int getCounts(IntentEvent.Type type) {
220 return events.get(type).size();
221 }
222
223 public void setLatch(int count, IntentEvent.Type type) {
224 latchMap.put(type, new CountDownLatch(count));
225 }
226
227 public void await(IntentEvent.Type type) {
228 try {
229 latchMap.get(type).await();
230 } catch (InterruptedException e) {
231 e.printStackTrace();
232 }
233 }
234 }
235
236 private static class TestIntentTracker implements ObjectiveTrackerService {
237 private TopologyChangeDelegate delegate;
238 @Override
239 public void setDelegate(TopologyChangeDelegate delegate) {
240 this.delegate = delegate;
241 }
242
243 @Override
244 public void unsetDelegate(TopologyChangeDelegate delegate) {
245 if (delegate.equals(this.delegate)) {
246 this.delegate = null;
247 }
248 }
249
250 @Override
251 public void addTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
252 //TODO
253 }
254
255 @Override
256 public void removeTrackedResources(IntentId intentId, Collection<NetworkResource> resources) {
257 //TODO
258 }
259 }
260
261 private static class MockIntent extends Intent {
262 private static AtomicLong counter = new AtomicLong(0);
263
264 private final Long number;
265 // Nothing new here
266 public MockIntent(Long number) {
Brian O'Connor520c0522014-11-23 23:50:47 -0800267 super(APPID, null);
Brian O'Connor427a1762014-11-19 18:40:32 -0800268 this.number = number;
269 }
270
271 public Long number() {
272 return number;
273 }
274
275 public static Long nextId() {
276 return counter.getAndIncrement();
277 }
278 }
279
280 private static class MockInstallableIntent extends MockIntent {
281 public MockInstallableIntent(Long number) {
282 super(number);
283 }
284
285 @Override
286 public boolean isInstallable() {
287 return true;
288 }
289 }
290
291 private static class TestIntentCompiler implements IntentCompiler<MockIntent> {
292 @Override
293 public List<Intent> compile(MockIntent intent, List<Intent> installable,
294 Set<LinkResourceAllocations> resources) {
295 return Lists.newArrayList(new MockInstallableIntent(intent.number()));
296 }
297 }
298
299 private static class TestIntentInstaller implements IntentInstaller<MockInstallableIntent> {
300 @Override
301 public List<FlowRuleBatchOperation> install(MockInstallableIntent intent) {
302 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
303 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
304 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr));
305 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
306 }
307
308 @Override
309 public List<FlowRuleBatchOperation> uninstall(MockInstallableIntent intent) {
310 FlowRule fr = new IntentTestsMocks.MockFlowRule(intent.number().intValue());
311 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
312 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
313 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
314 }
315
316 @Override
317 public List<FlowRuleBatchOperation> replace(MockInstallableIntent oldIntent, MockInstallableIntent newIntent) {
318 FlowRule fr = new IntentTestsMocks.MockFlowRule(oldIntent.number().intValue());
319 FlowRule fr2 = new IntentTestsMocks.MockFlowRule(newIntent.number().intValue());
320 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
321 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, fr));
322 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, fr2));
323 return Lists.newArrayList(new FlowRuleBatchOperation(rules));
324 }
325 }
326
327 /**
328 * Hamcrest matcher to check that a conllection of Intents contains an
329 * Intent with the specified Intent Id.
330 */
331 public static class EntryForIntentMatcher extends TypeSafeMatcher<Collection<Intent>> {
332 private final String id;
333
334 public EntryForIntentMatcher(String idValue) {
335 id = idValue;
336 }
337
338 @Override
339 public boolean matchesSafely(Collection<Intent> intents) {
340 return hasItem(Matchers.<Intent>hasProperty("id", equalTo(id))).matches(intents);
341 }
342
343 @Override
344 public void describeTo(Description description) {
345 description.appendText("an intent with id \" ").
346 appendText(id).
347 appendText("\"");
348 }
349 }
350}