blob: 20240d49a4346bf4f5d2cafb32716d30380168c6 [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);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800158 delay(10); //FIXME this is a race
159 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800160 assertEquals(0L, flowRuleService.getFlowRuleCount());
161 }
162
163 @Test
164 public void stressSubmitWithdraw() {
165 flowRuleService.setFuture(true);
166
167 int count = 500;
168
169 listener.setLatch(count, Type.INSTALLED);
170 listener.setLatch(count, Type.WITHDRAWN);
171
172 Intent intent = new MockIntent(MockIntent.nextId());
173 for (int i = 0; i < count; i++) {
174 service.submit(intent);
175 service.withdraw(intent);
176 }
177
178 listener.await(Type.INSTALLED);
179 listener.await(Type.WITHDRAWN);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800180 delay(10); //FIXME this is a race
181 assertEquals(0L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800182 assertEquals(0L, flowRuleService.getFlowRuleCount());
183 }
184
185 @Test
186 public void replaceIntent() {
187 flowRuleService.setFuture(true);
188
189 MockIntent intent = new MockIntent(MockIntent.nextId());
190 listener.setLatch(1, Type.INSTALLED);
191 service.submit(intent);
192 listener.await(Type.INSTALLED);
193 assertEquals(1L, service.getIntentCount());
194 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
195
196 MockIntent intent2 = new MockIntent(MockIntent.nextId());
197 listener.setLatch(1, Type.WITHDRAWN);
198 listener.setLatch(1, Type.SUBMITTED);
199 listener.setLatch(1, Type.INSTALLED);
200 service.replace(intent.id(), intent2);
201 listener.await(Type.WITHDRAWN);
202 listener.await(Type.INSTALLED);
Thomas Vachuskae4b6bb22014-11-25 17:09:43 -0800203 delay(10); //FIXME this is a race
204 assertEquals(1L, service.getIntentCount());
Brian O'Connor427a1762014-11-19 18:40:32 -0800205 assertEquals(1L, manager.flowRuleService.getFlowRuleCount());
206 assertEquals(intent2.number().intValue(),
207 flowRuleService.flows.iterator().next().priority());
208 }
209
210 private static class TestListener implements IntentListener {
211 final Multimap<IntentEvent.Type, IntentEvent> events = HashMultimap.create();
212 Map<IntentEvent.Type, CountDownLatch> latchMap = Maps.newHashMap();
213
214 @Override
215 public void event(IntentEvent event) {
216 events.put(event.type(), event);
217 if (latchMap.containsKey(event.type())) {
218 latchMap.get(event.type()).countDown();
219 }
220 }
221
222 public int getCounts(IntentEvent.Type type) {
223 return events.get(type).size();
224 }
225
226 public void setLatch(int count, IntentEvent.Type type) {
227 latchMap.put(type, new CountDownLatch(count));
228 }
229
230 public void await(IntentEvent.Type type) {
231 try {
232 latchMap.get(type).await();
233 } 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}