blob: 09c309805ed6018e0fcdaafd1c8fbafc7f83ec21 [file] [log] [blame]
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -07001package org.onlab.onos.store.device.impl;
2
3import static org.junit.Assert.*;
4import static org.onlab.onos.net.Device.Type.SWITCH;
5import static org.onlab.onos.net.DeviceId.deviceId;
6import static org.onlab.onos.net.device.DeviceEvent.Type.*;
7
8import java.util.Arrays;
9import java.util.HashMap;
10import java.util.List;
11import java.util.Map;
12import java.util.Set;
13import java.util.concurrent.CountDownLatch;
14import java.util.concurrent.TimeUnit;
15
16import org.junit.After;
17import org.junit.AfterClass;
18import org.junit.Before;
19import org.junit.BeforeClass;
20import org.junit.Ignore;
21import org.junit.Test;
22import org.onlab.onos.cluster.MastershipTerm;
23import org.onlab.onos.cluster.NodeId;
24import org.onlab.onos.net.Annotations;
25import org.onlab.onos.net.DefaultAnnotations;
26import org.onlab.onos.net.Device;
27import org.onlab.onos.net.DeviceId;
28import org.onlab.onos.net.Port;
29import org.onlab.onos.net.PortNumber;
30import org.onlab.onos.net.SparseAnnotations;
31import org.onlab.onos.net.device.DefaultDeviceDescription;
32import org.onlab.onos.net.device.DefaultPortDescription;
33import org.onlab.onos.net.device.DeviceDescription;
34import org.onlab.onos.net.device.DeviceEvent;
35import org.onlab.onos.net.device.DeviceStore;
36import org.onlab.onos.net.device.DeviceStoreDelegate;
37import org.onlab.onos.net.device.PortDescription;
38import org.onlab.onos.net.provider.ProviderId;
39import org.onlab.onos.store.ClockService;
40
41import com.google.common.collect.Iterables;
42import com.google.common.collect.Sets;
43
44
45// TODO add tests for remote replication
46/**
47 * Test of the gossip based distributed DeviceStore implementation.
48 */
49public class GossipDeviceStoreTest {
50
51 private static final ProviderId PID = new ProviderId("of", "foo");
52 private static final ProviderId PIDA = new ProviderId("of", "bar", true);
53 private static final DeviceId DID1 = deviceId("of:foo");
54 private static final DeviceId DID2 = deviceId("of:bar");
55 private static final String MFR = "whitebox";
56 private static final String HW = "1.1.x";
57 private static final String SW1 = "3.8.1";
58 private static final String SW2 = "3.9.5";
59 private static final String SN = "43311-12345";
60
61 private static final PortNumber P1 = PortNumber.portNumber(1);
62 private static final PortNumber P2 = PortNumber.portNumber(2);
63 private static final PortNumber P3 = PortNumber.portNumber(3);
64
65 private static final SparseAnnotations A1 = DefaultAnnotations.builder()
66 .set("A1", "a1")
67 .set("B1", "b1")
68 .build();
69 private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
70 .remove("A1")
71 .set("B3", "b3")
72 .build();
73 private static final SparseAnnotations A2 = DefaultAnnotations.builder()
74 .set("A2", "a2")
75 .set("B2", "b2")
76 .build();
77 private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
78 .remove("A2")
79 .set("B4", "b4")
80 .build();
81
82 private static final NodeId MYSELF = new NodeId("myself");
83
84 private GossipDeviceStore gossipDeviceStore;
85 private DeviceStore deviceStore;
86
87 private DeviceClockManager deviceClockManager;
88 private ClockService clockService;
89
90 @BeforeClass
91 public static void setUpBeforeClass() throws Exception {
92 }
93
94 @AfterClass
95 public static void tearDownAfterClass() throws Exception {
96 }
97
98
99 @Before
100 public void setUp() throws Exception {
101 deviceClockManager = new DeviceClockManager();
102 deviceClockManager.activate();
103 clockService = deviceClockManager;
104
105 deviceClockManager.setMastershipTerm(DID1, MastershipTerm.of(MYSELF, 1));
106 deviceClockManager.setMastershipTerm(DID2, MastershipTerm.of(MYSELF, 2));
107
108 gossipDeviceStore = new TestGossipDeviceStore(clockService);
109 gossipDeviceStore.activate();
110 deviceStore = gossipDeviceStore;
111 }
112
113 @After
114 public void tearDown() throws Exception {
115 gossipDeviceStore.deactivate();
116 deviceClockManager.deactivate();
117 }
118
119 private void putDevice(DeviceId deviceId, String swVersion) {
120 DeviceDescription description =
121 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
122 HW, swVersion, SN);
123 deviceStore.createOrUpdateDevice(PID, deviceId, description);
124 }
125
126 private void putDeviceAncillary(DeviceId deviceId, String swVersion) {
127 DeviceDescription description =
128 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
129 HW, swVersion, SN);
130 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
131 }
132
133 private static void assertDevice(DeviceId id, String swVersion, Device device) {
134 assertNotNull(device);
135 assertEquals(id, device.id());
136 assertEquals(MFR, device.manufacturer());
137 assertEquals(HW, device.hwVersion());
138 assertEquals(swVersion, device.swVersion());
139 assertEquals(SN, device.serialNumber());
140 }
141
142 /**
143 * Verifies that Annotations created by merging {@code annotations} is
144 * equal to actual Annotations.
145 *
146 * @param actual Annotations to check
147 * @param annotations
148 */
149 private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
150 DefaultAnnotations expected = DefaultAnnotations.builder().build();
151 for (SparseAnnotations a : annotations) {
152 expected = DefaultAnnotations.merge(expected, a);
153 }
154 assertEquals(expected.keys(), actual.keys());
155 for (String key : expected.keys()) {
156 assertEquals(expected.value(key), actual.value(key));
157 }
158 }
159
160 @Test
161 public final void testGetDeviceCount() {
162 assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
163
164 putDevice(DID1, SW1);
165 putDevice(DID2, SW2);
166 putDevice(DID1, SW1);
167
168 assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
169 }
170
171 @Test
172 public final void testGetDevices() {
173 assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));
174
175 putDevice(DID1, SW1);
176 putDevice(DID2, SW2);
177 putDevice(DID1, SW1);
178
179 assertEquals("expect 2 uniq devices",
180 2, Iterables.size(deviceStore.getDevices()));
181
182 Map<DeviceId, Device> devices = new HashMap<>();
183 for (Device device : deviceStore.getDevices()) {
184 devices.put(device.id(), device);
185 }
186
187 assertDevice(DID1, SW1, devices.get(DID1));
188 assertDevice(DID2, SW2, devices.get(DID2));
189
190 // add case for new node?
191 }
192
193 @Test
194 public final void testGetDevice() {
195
196 putDevice(DID1, SW1);
197
198 assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
199 assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
200 }
201
202 @Test
203 public final void testCreateOrUpdateDevice() {
204 DeviceDescription description =
205 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
206 HW, SW1, SN);
207 DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
208 assertEquals(DEVICE_ADDED, event.type());
209 assertDevice(DID1, SW1, event.subject());
210
211 DeviceDescription description2 =
212 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
213 HW, SW2, SN);
214 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
215 assertEquals(DEVICE_UPDATED, event2.type());
216 assertDevice(DID1, SW2, event2.subject());
217
218 assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
219 }
220
221 @Test
222 public final void testCreateOrUpdateDeviceAncillary() {
223 DeviceDescription description =
224 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
225 HW, SW1, SN, A2);
226 DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
227 assertEquals(DEVICE_ADDED, event.type());
228 assertDevice(DID1, SW1, event.subject());
229 assertEquals(PIDA, event.subject().providerId());
230 assertAnnotationsEquals(event.subject().annotations(), A2);
231 assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
232
233 DeviceDescription description2 =
234 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
235 HW, SW2, SN, A1);
236 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
237 assertEquals(DEVICE_UPDATED, event2.type());
238 assertDevice(DID1, SW2, event2.subject());
239 assertEquals(PID, event2.subject().providerId());
240 assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
241 assertTrue(deviceStore.isAvailable(DID1));
242
243 assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
244
245 // For now, Ancillary is ignored once primary appears
246 assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
247
248 // But, Ancillary annotations will be in effect
249 DeviceDescription description3 =
250 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
251 HW, SW1, SN, A2_2);
252 DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
253 assertEquals(DEVICE_UPDATED, event3.type());
254 // basic information will be the one from Primary
255 assertDevice(DID1, SW2, event3.subject());
256 assertEquals(PID, event3.subject().providerId());
257 // but annotation from Ancillary will be merged
258 assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
259 assertTrue(deviceStore.isAvailable(DID1));
260 }
261
262
263 @Test
264 public final void testMarkOffline() {
265
266 putDevice(DID1, SW1);
267 assertTrue(deviceStore.isAvailable(DID1));
268
269 DeviceEvent event = deviceStore.markOffline(DID1);
270 assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
271 assertDevice(DID1, SW1, event.subject());
272 assertFalse(deviceStore.isAvailable(DID1));
273
274 DeviceEvent event2 = deviceStore.markOffline(DID1);
275 assertNull("No change, no event", event2);
276}
277
278 @Test
279 public final void testUpdatePorts() {
280 putDevice(DID1, SW1);
281 List<PortDescription> pds = Arrays.<PortDescription>asList(
282 new DefaultPortDescription(P1, true),
283 new DefaultPortDescription(P2, true)
284 );
285
286 List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
287
288 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
289 for (DeviceEvent event : events) {
290 assertEquals(PORT_ADDED, event.type());
291 assertDevice(DID1, SW1, event.subject());
292 assertTrue("PortNumber is one of expected",
293 expectedPorts.remove(event.port().number()));
294 assertTrue("Port is enabled", event.port().isEnabled());
295 }
296 assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
297
298
299 List<PortDescription> pds2 = Arrays.<PortDescription>asList(
300 new DefaultPortDescription(P1, false),
301 new DefaultPortDescription(P2, true),
302 new DefaultPortDescription(P3, true)
303 );
304
305 events = deviceStore.updatePorts(PID, DID1, pds2);
306 assertFalse("event should be triggered", events.isEmpty());
307 for (DeviceEvent event : events) {
308 PortNumber num = event.port().number();
309 if (P1.equals(num)) {
310 assertEquals(PORT_UPDATED, event.type());
311 assertDevice(DID1, SW1, event.subject());
312 assertFalse("Port is disabled", event.port().isEnabled());
313 } else if (P2.equals(num)) {
314 fail("P2 event not expected.");
315 } else if (P3.equals(num)) {
316 assertEquals(PORT_ADDED, event.type());
317 assertDevice(DID1, SW1, event.subject());
318 assertTrue("Port is enabled", event.port().isEnabled());
319 } else {
320 fail("Unknown port number encountered: " + num);
321 }
322 }
323
324 List<PortDescription> pds3 = Arrays.<PortDescription>asList(
325 new DefaultPortDescription(P1, false),
326 new DefaultPortDescription(P2, true)
327 );
328 events = deviceStore.updatePorts(PID, DID1, pds3);
329 assertFalse("event should be triggered", events.isEmpty());
330 for (DeviceEvent event : events) {
331 PortNumber num = event.port().number();
332 if (P1.equals(num)) {
333 fail("P1 event not expected.");
334 } else if (P2.equals(num)) {
335 fail("P2 event not expected.");
336 } else if (P3.equals(num)) {
337 assertEquals(PORT_REMOVED, event.type());
338 assertDevice(DID1, SW1, event.subject());
339 assertTrue("Port was enabled", event.port().isEnabled());
340 } else {
341 fail("Unknown port number encountered: " + num);
342 }
343 }
344
345 }
346
347 @Test
348 public final void testUpdatePortStatus() {
349 putDevice(DID1, SW1);
350 List<PortDescription> pds = Arrays.<PortDescription>asList(
351 new DefaultPortDescription(P1, true)
352 );
353 deviceStore.updatePorts(PID, DID1, pds);
354
355 DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
356 new DefaultPortDescription(P1, false));
357 assertEquals(PORT_UPDATED, event.type());
358 assertDevice(DID1, SW1, event.subject());
359 assertEquals(P1, event.port().number());
360 assertFalse("Port is disabled", event.port().isEnabled());
361
362 }
363 @Test
364 public final void testUpdatePortStatusAncillary() {
365 putDeviceAncillary(DID1, SW1);
366 putDevice(DID1, SW1);
367 List<PortDescription> pds = Arrays.<PortDescription>asList(
368 new DefaultPortDescription(P1, true, A1)
369 );
370 deviceStore.updatePorts(PID, DID1, pds);
371
372 DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
373 new DefaultPortDescription(P1, false, A1_2));
374 assertEquals(PORT_UPDATED, event.type());
375 assertDevice(DID1, SW1, event.subject());
376 assertEquals(P1, event.port().number());
377 assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
378 assertFalse("Port is disabled", event.port().isEnabled());
379
380 DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
381 new DefaultPortDescription(P1, true));
382 assertNull("Ancillary is ignored if primary exists", event2);
383
384 // but, Ancillary annotation update will be notified
385 DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
386 new DefaultPortDescription(P1, true, A2));
387 assertEquals(PORT_UPDATED, event3.type());
388 assertDevice(DID1, SW1, event3.subject());
389 assertEquals(P1, event3.port().number());
390 assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
391 assertFalse("Port is disabled", event3.port().isEnabled());
392
393 // port only reported from Ancillary will be notified as down
394 DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1,
395 new DefaultPortDescription(P2, true));
396 assertEquals(PORT_ADDED, event4.type());
397 assertDevice(DID1, SW1, event4.subject());
398 assertEquals(P2, event4.port().number());
399 assertAnnotationsEquals(event4.port().annotations());
400 assertFalse("Port is disabled if not given from primary provider",
401 event4.port().isEnabled());
402 }
403
404 @Test
405 public final void testGetPorts() {
406 putDevice(DID1, SW1);
407 putDevice(DID2, SW1);
408 List<PortDescription> pds = Arrays.<PortDescription>asList(
409 new DefaultPortDescription(P1, true),
410 new DefaultPortDescription(P2, true)
411 );
412 deviceStore.updatePorts(PID, DID1, pds);
413
414 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
415 List<Port> ports = deviceStore.getPorts(DID1);
416 for (Port port : ports) {
417 assertTrue("Port is enabled", port.isEnabled());
418 assertTrue("PortNumber is one of expected",
419 expectedPorts.remove(port.number()));
420 }
421 assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
422
423
424 assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
425 }
426
427 @Test
428 public final void testGetPort() {
429 putDevice(DID1, SW1);
430 putDevice(DID2, SW1);
431 List<PortDescription> pds = Arrays.<PortDescription>asList(
432 new DefaultPortDescription(P1, true),
433 new DefaultPortDescription(P2, false)
434 );
435 deviceStore.updatePorts(PID, DID1, pds);
436
437 Port port1 = deviceStore.getPort(DID1, P1);
438 assertEquals(P1, port1.number());
439 assertTrue("Port is enabled", port1.isEnabled());
440
441 Port port2 = deviceStore.getPort(DID1, P2);
442 assertEquals(P2, port2.number());
443 assertFalse("Port is disabled", port2.isEnabled());
444
445 Port port3 = deviceStore.getPort(DID1, P3);
446 assertNull("P3 not expected", port3);
447 }
448
449 @Test
450 public final void testRemoveDevice() {
451 putDevice(DID1, SW1);
452 putDevice(DID2, SW1);
453
454 assertEquals(2, deviceStore.getDeviceCount());
455
456 DeviceEvent event = deviceStore.removeDevice(DID1);
457 assertEquals(DEVICE_REMOVED, event.type());
458 assertDevice(DID1, SW1, event.subject());
459
460 assertEquals(1, deviceStore.getDeviceCount());
461 }
462
463 // If Delegates should be called only on remote events,
464 // then Simple* should never call them, thus not test required.
465 // TODO add test for Port events when we have them
466 @Ignore("Ignore until Delegate spec. is clear.")
467 @Test
468 public final void testEvents() throws InterruptedException {
469 final CountDownLatch addLatch = new CountDownLatch(1);
470 DeviceStoreDelegate checkAdd = new DeviceStoreDelegate() {
471 @Override
472 public void notify(DeviceEvent event) {
473 assertEquals(DEVICE_ADDED, event.type());
474 assertDevice(DID1, SW1, event.subject());
475 addLatch.countDown();
476 }
477 };
478 final CountDownLatch updateLatch = new CountDownLatch(1);
479 DeviceStoreDelegate checkUpdate = new DeviceStoreDelegate() {
480 @Override
481 public void notify(DeviceEvent event) {
482 assertEquals(DEVICE_UPDATED, event.type());
483 assertDevice(DID1, SW2, event.subject());
484 updateLatch.countDown();
485 }
486 };
487 final CountDownLatch removeLatch = new CountDownLatch(1);
488 DeviceStoreDelegate checkRemove = new DeviceStoreDelegate() {
489 @Override
490 public void notify(DeviceEvent event) {
491 assertEquals(DEVICE_REMOVED, event.type());
492 assertDevice(DID1, SW2, event.subject());
493 removeLatch.countDown();
494 }
495 };
496
497 DeviceDescription description =
498 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
499 HW, SW1, SN);
500 deviceStore.setDelegate(checkAdd);
501 deviceStore.createOrUpdateDevice(PID, DID1, description);
502 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
503
504
505 DeviceDescription description2 =
506 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
507 HW, SW2, SN);
508 deviceStore.unsetDelegate(checkAdd);
509 deviceStore.setDelegate(checkUpdate);
510 deviceStore.createOrUpdateDevice(PID, DID1, description2);
511 assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
512
513 deviceStore.unsetDelegate(checkUpdate);
514 deviceStore.setDelegate(checkRemove);
515 deviceStore.removeDevice(DID1);
516 assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
517 }
518
519 private static final class TestGossipDeviceStore extends GossipDeviceStore {
520
521 public TestGossipDeviceStore(ClockService clockService) {
522 this.clockService = clockService;
523 }
524 }
525}