blob: 94de9b2c2f1ace6d4f6d483be43fd0f1d5b32fc5 [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
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700119 private void putDevice(DeviceId deviceId, String swVersion,
120 SparseAnnotations... annotations) {
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700121 DeviceDescription description =
122 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700123 HW, swVersion, SN, annotations);
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700124 deviceStore.createOrUpdateDevice(PID, deviceId, description);
125 }
126
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700127 private void putDeviceAncillary(DeviceId deviceId, String swVersion,
128 SparseAnnotations... annotations) {
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700129 DeviceDescription description =
130 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700131 HW, swVersion, SN, annotations);
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700132 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
133 }
134
135 private static void assertDevice(DeviceId id, String swVersion, Device device) {
136 assertNotNull(device);
137 assertEquals(id, device.id());
138 assertEquals(MFR, device.manufacturer());
139 assertEquals(HW, device.hwVersion());
140 assertEquals(swVersion, device.swVersion());
141 assertEquals(SN, device.serialNumber());
142 }
143
144 /**
145 * Verifies that Annotations created by merging {@code annotations} is
146 * equal to actual Annotations.
147 *
148 * @param actual Annotations to check
149 * @param annotations
150 */
151 private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
152 DefaultAnnotations expected = DefaultAnnotations.builder().build();
153 for (SparseAnnotations a : annotations) {
154 expected = DefaultAnnotations.merge(expected, a);
155 }
156 assertEquals(expected.keys(), actual.keys());
157 for (String key : expected.keys()) {
158 assertEquals(expected.value(key), actual.value(key));
159 }
160 }
161
162 @Test
163 public final void testGetDeviceCount() {
164 assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
165
166 putDevice(DID1, SW1);
167 putDevice(DID2, SW2);
168 putDevice(DID1, SW1);
169
170 assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
171 }
172
173 @Test
174 public final void testGetDevices() {
175 assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));
176
177 putDevice(DID1, SW1);
178 putDevice(DID2, SW2);
179 putDevice(DID1, SW1);
180
181 assertEquals("expect 2 uniq devices",
182 2, Iterables.size(deviceStore.getDevices()));
183
184 Map<DeviceId, Device> devices = new HashMap<>();
185 for (Device device : deviceStore.getDevices()) {
186 devices.put(device.id(), device);
187 }
188
189 assertDevice(DID1, SW1, devices.get(DID1));
190 assertDevice(DID2, SW2, devices.get(DID2));
191
192 // add case for new node?
193 }
194
195 @Test
196 public final void testGetDevice() {
197
198 putDevice(DID1, SW1);
199
200 assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
201 assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
202 }
203
204 @Test
205 public final void testCreateOrUpdateDevice() {
206 DeviceDescription description =
207 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
208 HW, SW1, SN);
209 DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
210 assertEquals(DEVICE_ADDED, event.type());
211 assertDevice(DID1, SW1, event.subject());
212
213 DeviceDescription description2 =
214 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
215 HW, SW2, SN);
216 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
217 assertEquals(DEVICE_UPDATED, event2.type());
218 assertDevice(DID1, SW2, event2.subject());
219
220 assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
221 }
222
223 @Test
224 public final void testCreateOrUpdateDeviceAncillary() {
225 DeviceDescription description =
226 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
227 HW, SW1, SN, A2);
228 DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
229 assertEquals(DEVICE_ADDED, event.type());
230 assertDevice(DID1, SW1, event.subject());
231 assertEquals(PIDA, event.subject().providerId());
232 assertAnnotationsEquals(event.subject().annotations(), A2);
233 assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
234
235 DeviceDescription description2 =
236 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
237 HW, SW2, SN, A1);
238 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
239 assertEquals(DEVICE_UPDATED, event2.type());
240 assertDevice(DID1, SW2, event2.subject());
241 assertEquals(PID, event2.subject().providerId());
242 assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
243 assertTrue(deviceStore.isAvailable(DID1));
244
245 assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
246
247 // For now, Ancillary is ignored once primary appears
248 assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
249
250 // But, Ancillary annotations will be in effect
251 DeviceDescription description3 =
252 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
253 HW, SW1, SN, A2_2);
254 DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
255 assertEquals(DEVICE_UPDATED, event3.type());
256 // basic information will be the one from Primary
257 assertDevice(DID1, SW2, event3.subject());
258 assertEquals(PID, event3.subject().providerId());
259 // but annotation from Ancillary will be merged
260 assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
261 assertTrue(deviceStore.isAvailable(DID1));
262 }
263
264
265 @Test
266 public final void testMarkOffline() {
267
268 putDevice(DID1, SW1);
269 assertTrue(deviceStore.isAvailable(DID1));
270
271 DeviceEvent event = deviceStore.markOffline(DID1);
272 assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
273 assertDevice(DID1, SW1, event.subject());
274 assertFalse(deviceStore.isAvailable(DID1));
275
276 DeviceEvent event2 = deviceStore.markOffline(DID1);
277 assertNull("No change, no event", event2);
278}
279
280 @Test
281 public final void testUpdatePorts() {
282 putDevice(DID1, SW1);
283 List<PortDescription> pds = Arrays.<PortDescription>asList(
284 new DefaultPortDescription(P1, true),
285 new DefaultPortDescription(P2, true)
286 );
287
288 List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
289
290 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
291 for (DeviceEvent event : events) {
292 assertEquals(PORT_ADDED, event.type());
293 assertDevice(DID1, SW1, event.subject());
294 assertTrue("PortNumber is one of expected",
295 expectedPorts.remove(event.port().number()));
296 assertTrue("Port is enabled", event.port().isEnabled());
297 }
298 assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
299
300
301 List<PortDescription> pds2 = Arrays.<PortDescription>asList(
302 new DefaultPortDescription(P1, false),
303 new DefaultPortDescription(P2, true),
304 new DefaultPortDescription(P3, true)
305 );
306
307 events = deviceStore.updatePorts(PID, DID1, pds2);
308 assertFalse("event should be triggered", events.isEmpty());
309 for (DeviceEvent event : events) {
310 PortNumber num = event.port().number();
311 if (P1.equals(num)) {
312 assertEquals(PORT_UPDATED, event.type());
313 assertDevice(DID1, SW1, event.subject());
314 assertFalse("Port is disabled", event.port().isEnabled());
315 } else if (P2.equals(num)) {
316 fail("P2 event not expected.");
317 } else if (P3.equals(num)) {
318 assertEquals(PORT_ADDED, event.type());
319 assertDevice(DID1, SW1, event.subject());
320 assertTrue("Port is enabled", event.port().isEnabled());
321 } else {
322 fail("Unknown port number encountered: " + num);
323 }
324 }
325
326 List<PortDescription> pds3 = Arrays.<PortDescription>asList(
327 new DefaultPortDescription(P1, false),
328 new DefaultPortDescription(P2, true)
329 );
330 events = deviceStore.updatePorts(PID, DID1, pds3);
331 assertFalse("event should be triggered", events.isEmpty());
332 for (DeviceEvent event : events) {
333 PortNumber num = event.port().number();
334 if (P1.equals(num)) {
335 fail("P1 event not expected.");
336 } else if (P2.equals(num)) {
337 fail("P2 event not expected.");
338 } else if (P3.equals(num)) {
339 assertEquals(PORT_REMOVED, event.type());
340 assertDevice(DID1, SW1, event.subject());
341 assertTrue("Port was enabled", event.port().isEnabled());
342 } else {
343 fail("Unknown port number encountered: " + num);
344 }
345 }
346
347 }
348
349 @Test
350 public final void testUpdatePortStatus() {
351 putDevice(DID1, SW1);
352 List<PortDescription> pds = Arrays.<PortDescription>asList(
353 new DefaultPortDescription(P1, true)
354 );
355 deviceStore.updatePorts(PID, DID1, pds);
356
357 DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
358 new DefaultPortDescription(P1, false));
359 assertEquals(PORT_UPDATED, event.type());
360 assertDevice(DID1, SW1, event.subject());
361 assertEquals(P1, event.port().number());
362 assertFalse("Port is disabled", event.port().isEnabled());
363
364 }
365 @Test
366 public final void testUpdatePortStatusAncillary() {
367 putDeviceAncillary(DID1, SW1);
368 putDevice(DID1, SW1);
369 List<PortDescription> pds = Arrays.<PortDescription>asList(
370 new DefaultPortDescription(P1, true, A1)
371 );
372 deviceStore.updatePorts(PID, DID1, pds);
373
374 DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
375 new DefaultPortDescription(P1, false, A1_2));
376 assertEquals(PORT_UPDATED, event.type());
377 assertDevice(DID1, SW1, event.subject());
378 assertEquals(P1, event.port().number());
379 assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
380 assertFalse("Port is disabled", event.port().isEnabled());
381
382 DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
383 new DefaultPortDescription(P1, true));
384 assertNull("Ancillary is ignored if primary exists", event2);
385
386 // but, Ancillary annotation update will be notified
387 DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
388 new DefaultPortDescription(P1, true, A2));
389 assertEquals(PORT_UPDATED, event3.type());
390 assertDevice(DID1, SW1, event3.subject());
391 assertEquals(P1, event3.port().number());
392 assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
393 assertFalse("Port is disabled", event3.port().isEnabled());
394
395 // port only reported from Ancillary will be notified as down
396 DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1,
397 new DefaultPortDescription(P2, true));
398 assertEquals(PORT_ADDED, event4.type());
399 assertDevice(DID1, SW1, event4.subject());
400 assertEquals(P2, event4.port().number());
401 assertAnnotationsEquals(event4.port().annotations());
402 assertFalse("Port is disabled if not given from primary provider",
403 event4.port().isEnabled());
404 }
405
406 @Test
407 public final void testGetPorts() {
408 putDevice(DID1, SW1);
409 putDevice(DID2, SW1);
410 List<PortDescription> pds = Arrays.<PortDescription>asList(
411 new DefaultPortDescription(P1, true),
412 new DefaultPortDescription(P2, true)
413 );
414 deviceStore.updatePorts(PID, DID1, pds);
415
416 Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
417 List<Port> ports = deviceStore.getPorts(DID1);
418 for (Port port : ports) {
419 assertTrue("Port is enabled", port.isEnabled());
420 assertTrue("PortNumber is one of expected",
421 expectedPorts.remove(port.number()));
422 }
423 assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
424
425
426 assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
427 }
428
429 @Test
430 public final void testGetPort() {
431 putDevice(DID1, SW1);
432 putDevice(DID2, SW1);
433 List<PortDescription> pds = Arrays.<PortDescription>asList(
434 new DefaultPortDescription(P1, true),
435 new DefaultPortDescription(P2, false)
436 );
437 deviceStore.updatePorts(PID, DID1, pds);
438
439 Port port1 = deviceStore.getPort(DID1, P1);
440 assertEquals(P1, port1.number());
441 assertTrue("Port is enabled", port1.isEnabled());
442
443 Port port2 = deviceStore.getPort(DID1, P2);
444 assertEquals(P2, port2.number());
445 assertFalse("Port is disabled", port2.isEnabled());
446
447 Port port3 = deviceStore.getPort(DID1, P3);
448 assertNull("P3 not expected", port3);
449 }
450
451 @Test
452 public final void testRemoveDevice() {
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700453 putDevice(DID1, SW1, A1);
454 List<PortDescription> pds = Arrays.<PortDescription>asList(
455 new DefaultPortDescription(P1, true, A2)
456 );
457 deviceStore.updatePorts(PID, DID1, pds);
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700458 putDevice(DID2, SW1);
459
460 assertEquals(2, deviceStore.getDeviceCount());
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700461 assertEquals(1, deviceStore.getPorts(DID1).size());
462 assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations(), A1);
463 assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations(), A2);
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700464
465 DeviceEvent event = deviceStore.removeDevice(DID1);
466 assertEquals(DEVICE_REMOVED, event.type());
467 assertDevice(DID1, SW1, event.subject());
468
469 assertEquals(1, deviceStore.getDeviceCount());
Yuta HIGUCHI0d6a5e62014-10-03 15:54:09 -0700470 assertEquals(0, deviceStore.getPorts(DID1).size());
471
472 // putBack Device, Port w/o annotation
473 putDevice(DID1, SW1);
474 List<PortDescription> pds2 = Arrays.<PortDescription>asList(
475 new DefaultPortDescription(P1, true)
476 );
477 deviceStore.updatePorts(PID, DID1, pds2);
478
479 // annotations should not survive
480 assertEquals(2, deviceStore.getDeviceCount());
481 assertEquals(1, deviceStore.getPorts(DID1).size());
482 assertAnnotationsEquals(deviceStore.getDevice(DID1).annotations());
483 assertAnnotationsEquals(deviceStore.getPort(DID1, P1).annotations());
Yuta HIGUCHI67a527f2014-10-02 22:23:54 -0700484 }
485
486 // If Delegates should be called only on remote events,
487 // then Simple* should never call them, thus not test required.
488 // TODO add test for Port events when we have them
489 @Ignore("Ignore until Delegate spec. is clear.")
490 @Test
491 public final void testEvents() throws InterruptedException {
492 final CountDownLatch addLatch = new CountDownLatch(1);
493 DeviceStoreDelegate checkAdd = new DeviceStoreDelegate() {
494 @Override
495 public void notify(DeviceEvent event) {
496 assertEquals(DEVICE_ADDED, event.type());
497 assertDevice(DID1, SW1, event.subject());
498 addLatch.countDown();
499 }
500 };
501 final CountDownLatch updateLatch = new CountDownLatch(1);
502 DeviceStoreDelegate checkUpdate = new DeviceStoreDelegate() {
503 @Override
504 public void notify(DeviceEvent event) {
505 assertEquals(DEVICE_UPDATED, event.type());
506 assertDevice(DID1, SW2, event.subject());
507 updateLatch.countDown();
508 }
509 };
510 final CountDownLatch removeLatch = new CountDownLatch(1);
511 DeviceStoreDelegate checkRemove = new DeviceStoreDelegate() {
512 @Override
513 public void notify(DeviceEvent event) {
514 assertEquals(DEVICE_REMOVED, event.type());
515 assertDevice(DID1, SW2, event.subject());
516 removeLatch.countDown();
517 }
518 };
519
520 DeviceDescription description =
521 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
522 HW, SW1, SN);
523 deviceStore.setDelegate(checkAdd);
524 deviceStore.createOrUpdateDevice(PID, DID1, description);
525 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
526
527
528 DeviceDescription description2 =
529 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
530 HW, SW2, SN);
531 deviceStore.unsetDelegate(checkAdd);
532 deviceStore.setDelegate(checkUpdate);
533 deviceStore.createOrUpdateDevice(PID, DID1, description2);
534 assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
535
536 deviceStore.unsetDelegate(checkUpdate);
537 deviceStore.setDelegate(checkRemove);
538 deviceStore.removeDevice(DID1);
539 assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
540 }
541
542 private static final class TestGossipDeviceStore extends GossipDeviceStore {
543
544 public TestGossipDeviceStore(ClockService clockService) {
545 this.clockService = clockService;
546 }
547 }
548}