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