bf7af464f0647c03d3a3345fbc6a5f248c70b458
[onosfw.git] /
1 /*
2  * Copyright 2014-2015 Open Networking Laboratory
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onosproject.store.link.impl;
17
18 import com.google.common.collect.Iterables;
19
20 import org.easymock.Capture;
21 import org.junit.After;
22 import org.junit.AfterClass;
23 import org.junit.Before;
24 import org.junit.BeforeClass;
25 import org.junit.Ignore;
26 import org.junit.Test;
27 import org.onlab.packet.IpAddress;
28 import org.onosproject.cluster.ControllerNode;
29 import org.onosproject.cluster.DefaultControllerNode;
30 import org.onosproject.cluster.NodeId;
31 import org.onosproject.mastership.MastershipServiceAdapter;
32 import org.onosproject.net.ConnectPoint;
33 import org.onosproject.net.DefaultAnnotations;
34 import org.onosproject.net.DeviceId;
35 import org.onosproject.net.Link;
36 import org.onosproject.net.Link.Type;
37 import org.onosproject.net.LinkKey;
38 import org.onosproject.net.PortNumber;
39 import org.onosproject.net.SparseAnnotations;
40 import org.onosproject.net.device.DeviceClockService;
41 import org.onosproject.net.device.DeviceClockServiceAdapter;
42 import org.onosproject.net.link.DefaultLinkDescription;
43 import org.onosproject.net.link.LinkDescription;
44 import org.onosproject.net.link.LinkEvent;
45 import org.onosproject.net.link.LinkStore;
46 import org.onosproject.net.link.LinkStoreDelegate;
47 import org.onosproject.net.provider.ProviderId;
48 import org.onosproject.store.Timestamp;
49 import org.onosproject.store.cluster.StaticClusterService;
50 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
51 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
52 import org.onosproject.store.cluster.messaging.MessageSubject;
53 import org.onosproject.store.impl.MastershipBasedTimestamp;
54
55 import java.util.HashMap;
56 import java.util.Map;
57 import java.util.Set;
58 import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.ExecutorService;
60 import java.util.concurrent.TimeUnit;
61 import java.util.concurrent.atomic.AtomicLong;
62 import java.util.function.Function;
63
64 import static org.easymock.EasyMock.*;
65 import static org.junit.Assert.*;
66 import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
67 import static org.onosproject.net.DeviceId.deviceId;
68 import static org.onosproject.net.Link.Type.DIRECT;
69 import static org.onosproject.net.Link.Type.EDGE;
70 import static org.onosproject.net.Link.Type.INDIRECT;
71 import static org.onosproject.net.NetTestTools.assertAnnotationsEquals;
72 import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
73 import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
74 import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
75
76 /**
77  * Test of the GossipLinkStoreTest implementation.
78  */
79 public class GossipLinkStoreTest {
80
81     private static final ProviderId PID = new ProviderId("of", "foo");
82     private static final ProviderId PIDA = new ProviderId("of", "bar", true);
83     private static final DeviceId DID1 = deviceId("of:foo");
84     private static final DeviceId DID2 = deviceId("of:bar");
85
86     private static final PortNumber P1 = PortNumber.portNumber(1);
87     private static final PortNumber P2 = PortNumber.portNumber(2);
88     private static final PortNumber P3 = PortNumber.portNumber(3);
89
90     private static final SparseAnnotations A1 = DefaultAnnotations.builder()
91             .set("A1", "a1")
92             .set("B1", "b1")
93             .build();
94     private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
95             .remove("A1")
96             .set("B3", "b3")
97             .build();
98     private static final SparseAnnotations A2 = DefaultAnnotations.builder()
99             .set("A2", "a2")
100             .set("B2", "b2")
101             .build();
102     private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
103             .remove("A2")
104             .set("B4", "b4")
105             .build();
106
107     // local node
108     private static final NodeId NID1 = new NodeId("local");
109     private static final ControllerNode ONOS1 =
110             new DefaultControllerNode(NID1, IpAddress.valueOf("127.0.0.1"));
111
112     // remote node
113     private static final NodeId NID2 = new NodeId("remote");
114     private static final ControllerNode ONOS2 =
115             new DefaultControllerNode(NID2, IpAddress.valueOf("127.0.0.2"));
116
117     private GossipLinkStore linkStoreImpl;
118     private LinkStore linkStore;
119
120     private final AtomicLong ticker = new AtomicLong();
121     private DeviceClockService deviceClockService;
122     private ClusterCommunicationService clusterCommunicator;
123
124     @BeforeClass
125     public static void setUpBeforeClass() throws Exception {
126     }
127
128     @AfterClass
129     public static void tearDownAfterClass() throws Exception {
130     }
131
132     @Before
133     public void setUp() throws Exception {
134         // TODO mock clusterCommunicator
135         clusterCommunicator = createNiceMock(ClusterCommunicationService.class);
136         clusterCommunicator.addSubscriber(anyObject(MessageSubject.class),
137                                     anyObject(ClusterMessageHandler.class),
138                                     anyObject(ExecutorService.class));
139         expectLastCall().anyTimes();
140         replay(clusterCommunicator);
141
142         linkStoreImpl = new GossipLinkStore();
143         linkStoreImpl.deviceClockService = deviceClockService;
144         linkStoreImpl.clusterCommunicator = clusterCommunicator;
145         linkStoreImpl.clusterService = new TestClusterService();
146         linkStoreImpl.deviceClockService = new TestDeviceClockService();
147         linkStoreImpl.mastershipService = new TestMastershipService();
148         linkStoreImpl.activate();
149         linkStore = linkStoreImpl;
150
151         verify(clusterCommunicator);
152         reset(clusterCommunicator);
153
154     }
155
156     @After
157     public void tearDown() throws Exception {
158         linkStoreImpl.deactivate();
159     }
160
161     private void putLink(DeviceId srcId, PortNumber srcNum,
162                          DeviceId dstId, PortNumber dstNum, Type type,
163                          SparseAnnotations... annotations) {
164         ConnectPoint src = new ConnectPoint(srcId, srcNum);
165         ConnectPoint dst = new ConnectPoint(dstId, dstNum);
166         reset(clusterCommunicator);
167         clusterCommunicator.<InternalLinkEvent>broadcast(
168                 anyObject(InternalLinkEvent.class), anyObject(MessageSubject.class), anyObject(Function.class));
169         expectLastCall().anyTimes();
170         replay(clusterCommunicator);
171         linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations));
172         verify(clusterCommunicator);
173     }
174
175     private <T> void resetCommunicatorExpectingSingleBroadcast(
176             Capture<T> message,
177             Capture<MessageSubject> subject,
178             Capture<Function<T, byte[]>> encoder) {
179         message.reset();
180         subject.reset();
181         encoder.reset();
182         reset(clusterCommunicator);
183         clusterCommunicator.broadcast(capture(message), capture(subject), capture(encoder));
184         expectLastCall().once();
185         replay(clusterCommunicator);
186     }
187
188     private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
189         putLink(key.src().deviceId(), key.src().port(),
190                 key.dst().deviceId(), key.dst().port(),
191                 type, annotations);
192     }
193
194     private static void assertLink(DeviceId srcId, PortNumber srcNum,
195                             DeviceId dstId, PortNumber dstNum, Type type,
196                             Link link) {
197         assertEquals(srcId, link.src().deviceId());
198         assertEquals(srcNum, link.src().port());
199         assertEquals(dstId, link.dst().deviceId());
200         assertEquals(dstNum, link.dst().port());
201         assertEquals(type, link.type());
202     }
203
204     private static void assertLink(LinkKey key, Type type, Link link) {
205         assertLink(key.src().deviceId(), key.src().port(),
206                    key.dst().deviceId(), key.dst().port(),
207                    type, link);
208     }
209
210     @Test
211     public final void testGetLinkCount() {
212         assertEquals("initialy empty", 0, linkStore.getLinkCount());
213
214         putLink(DID1, P1, DID2, P2, DIRECT);
215         putLink(DID2, P2, DID1, P1, DIRECT);
216         putLink(DID1, P1, DID2, P2, DIRECT);
217
218         assertEquals("expecting 2 unique link", 2, linkStore.getLinkCount());
219     }
220
221     @Test
222     public final void testGetLinks() {
223         assertEquals("initialy empty", 0,
224                 Iterables.size(linkStore.getLinks()));
225
226         LinkKey linkId1 = LinkKey.linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
227         LinkKey linkId2 = LinkKey.linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
228
229         putLink(linkId1, DIRECT);
230         putLink(linkId2, DIRECT);
231         putLink(linkId1, DIRECT);
232
233         assertEquals("expecting 2 unique link", 2,
234                 Iterables.size(linkStore.getLinks()));
235
236         Map<LinkKey, Link> links = new HashMap<>();
237         for (Link link : linkStore.getLinks()) {
238             links.put(LinkKey.linkKey(link), link);
239         }
240
241         assertLink(linkId1, DIRECT, links.get(linkId1));
242         assertLink(linkId2, DIRECT, links.get(linkId2));
243     }
244
245     @Test
246     public final void testGetDeviceEgressLinks() {
247         LinkKey linkId1 = LinkKey.linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
248         LinkKey linkId2 = LinkKey.linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
249         LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
250
251         putLink(linkId1, DIRECT);
252         putLink(linkId2, DIRECT);
253         putLink(linkId3, DIRECT);
254
255         // DID1,P1 => DID2,P2
256         // DID2,P2 => DID1,P1
257         // DID1,P2 => DID2,P3
258
259         Set<Link> links1 = linkStore.getDeviceEgressLinks(DID1);
260         assertEquals(2, links1.size());
261         // check
262
263         Set<Link> links2 = linkStore.getDeviceEgressLinks(DID2);
264         assertEquals(1, links2.size());
265         assertLink(linkId2, DIRECT, links2.iterator().next());
266     }
267
268     @Test
269     public final void testGetDeviceIngressLinks() {
270         LinkKey linkId1 = LinkKey.linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
271         LinkKey linkId2 = LinkKey.linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
272         LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
273
274         putLink(linkId1, DIRECT);
275         putLink(linkId2, DIRECT);
276         putLink(linkId3, DIRECT);
277
278         // DID1,P1 => DID2,P2
279         // DID2,P2 => DID1,P1
280         // DID1,P2 => DID2,P3
281
282         Set<Link> links1 = linkStore.getDeviceIngressLinks(DID2);
283         assertEquals(2, links1.size());
284         // check
285
286         Set<Link> links2 = linkStore.getDeviceIngressLinks(DID1);
287         assertEquals(1, links2.size());
288         assertLink(linkId2, DIRECT, links2.iterator().next());
289     }
290
291     @Test
292     public final void testGetLink() {
293         ConnectPoint src = new ConnectPoint(DID1, P1);
294         ConnectPoint dst = new ConnectPoint(DID2, P2);
295         LinkKey linkId1 = LinkKey.linkKey(src, dst);
296
297         putLink(linkId1, DIRECT);
298
299         Link link = linkStore.getLink(src, dst);
300         assertLink(linkId1, DIRECT, link);
301
302         assertNull("There shouldn't be reverese link",
303                 linkStore.getLink(dst, src));
304     }
305
306     @Test
307     public final void testGetEgressLinks() {
308         final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
309         final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
310         LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
311         LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
312         LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
313
314         putLink(linkId1, DIRECT);
315         putLink(linkId2, DIRECT);
316         putLink(linkId3, DIRECT);
317
318         // DID1,P1 => DID2,P2
319         // DID2,P2 => DID1,P1
320         // DID1,P2 => DID2,P3
321
322         Set<Link> links1 = linkStore.getEgressLinks(d1P1);
323         assertEquals(1, links1.size());
324         assertLink(linkId1, DIRECT, links1.iterator().next());
325
326         Set<Link> links2 = linkStore.getEgressLinks(d2P2);
327         assertEquals(1, links2.size());
328         assertLink(linkId2, DIRECT, links2.iterator().next());
329     }
330
331     @Test
332     public final void testGetIngressLinks() {
333         final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
334         final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
335         LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
336         LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
337         LinkKey linkId3 = LinkKey.linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
338
339         putLink(linkId1, DIRECT);
340         putLink(linkId2, DIRECT);
341         putLink(linkId3, DIRECT);
342
343         // DID1,P1 => DID2,P2
344         // DID2,P2 => DID1,P1
345         // DID1,P2 => DID2,P3
346
347         Set<Link> links1 = linkStore.getIngressLinks(d2P2);
348         assertEquals(1, links1.size());
349         assertLink(linkId1, DIRECT, links1.iterator().next());
350
351         Set<Link> links2 = linkStore.getIngressLinks(d1P1);
352         assertEquals(1, links2.size());
353         assertLink(linkId2, DIRECT, links2.iterator().next());
354     }
355
356     @Test
357     public final void testCreateOrUpdateLink() {
358         ConnectPoint src = new ConnectPoint(DID1, P1);
359         ConnectPoint dst = new ConnectPoint(DID2, P2);
360
361         Capture<InternalLinkEvent> message = new Capture<>();
362         Capture<MessageSubject> subject = new Capture<>();
363         Capture<Function<InternalLinkEvent, byte[]>> encoder = new Capture<>();
364
365         // add link
366         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
367         final DefaultLinkDescription linkDescription = new DefaultLinkDescription(src, dst, INDIRECT);
368         LinkEvent event = linkStore.createOrUpdateLink(PID,
369                     linkDescription);
370         verifyLinkBroadcastMessage(PID, NID1, src, dst, INDIRECT, message, subject, encoder);
371
372         assertLink(DID1, P1, DID2, P2, INDIRECT, event.subject());
373         assertEquals(LINK_ADDED, event.type());
374
375         // update link type
376         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
377         LinkEvent event2 = linkStore.createOrUpdateLink(PID,
378                 new DefaultLinkDescription(src, dst, DIRECT));
379         verifyLinkBroadcastMessage(PID, NID1, src, dst, DIRECT, message, subject, encoder);
380
381         assertLink(DID1, P1, DID2, P2, DIRECT, event2.subject());
382         assertEquals(LINK_UPDATED, event2.type());
383
384         // no change
385         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
386         LinkEvent event3 = linkStore.createOrUpdateLink(PID,
387                 new DefaultLinkDescription(src, dst, DIRECT));
388         verifyNoBroadcastMessage(message);
389
390         assertNull("No change event expected", event3);
391     }
392
393     private <T> void verifyNoBroadcastMessage(Capture<T> message) {
394         assertFalse("No broadcast expected", message.hasCaptured());
395     }
396
397     private void verifyLinkBroadcastMessage(ProviderId providerId,
398             NodeId sender,
399             ConnectPoint src,
400             ConnectPoint dst,
401             Type type,
402             Capture<InternalLinkEvent> actualLinkEvent,
403             Capture<MessageSubject> actualSubject,
404             Capture<Function<InternalLinkEvent, byte[]>> actualEncoder) {
405         verify(clusterCommunicator);
406         assertTrue(actualLinkEvent.hasCaptured());
407         assertEquals(GossipLinkStoreMessageSubjects.LINK_UPDATE, actualSubject.getValue());
408         assertEquals(providerId, actualLinkEvent.getValue().providerId());
409         assertLinkDescriptionEquals(src, dst, type, actualLinkEvent.getValue().linkDescription().value());
410     }
411
412     private static void assertLinkDescriptionEquals(ConnectPoint src,
413                                              ConnectPoint dst,
414                                              Type type,
415                                              LinkDescription actual) {
416         assertEquals(src, actual.src());
417         assertEquals(dst, actual.dst());
418         assertEquals(type, actual.type());
419         // TODO check annotations
420     }
421
422     @Test
423     public final void testCreateOrUpdateLinkAncillary() {
424         ConnectPoint src = new ConnectPoint(DID1, P1);
425         ConnectPoint dst = new ConnectPoint(DID2, P2);
426
427         Capture<InternalLinkEvent> message = new Capture<>();
428         Capture<MessageSubject> subject = new Capture<>();
429         Capture<Function<InternalLinkEvent, byte[]>> encoder = new Capture<>();
430
431         // add Ancillary link
432         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
433         LinkEvent event = linkStore.createOrUpdateLink(PIDA,
434                     new DefaultLinkDescription(src, dst, INDIRECT, A1));
435         verifyLinkBroadcastMessage(PIDA, NID1, src, dst, INDIRECT, message, subject, encoder);
436
437         assertNotNull("Ancillary only link is ignored", event);
438
439         // add Primary link
440         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
441         LinkEvent event2 = linkStore.createOrUpdateLink(PID,
442                 new DefaultLinkDescription(src, dst, INDIRECT, A2));
443         verifyLinkBroadcastMessage(PID, NID1, src, dst, INDIRECT, message, subject, encoder);
444
445         assertLink(DID1, P1, DID2, P2, INDIRECT, event2.subject());
446         assertAnnotationsEquals(event2.subject().annotations(), A2, A1);
447         assertEquals(LINK_UPDATED, event2.type());
448
449         // update link type
450         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
451         LinkEvent event3 = linkStore.createOrUpdateLink(PID,
452                 new DefaultLinkDescription(src, dst, DIRECT, A2));
453         verifyLinkBroadcastMessage(PID, NID1, src, dst, DIRECT, message, subject, encoder);
454
455         assertLink(DID1, P1, DID2, P2, DIRECT, event3.subject());
456         assertAnnotationsEquals(event3.subject().annotations(), A2, A1);
457         assertEquals(LINK_UPDATED, event3.type());
458
459
460         // no change
461         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
462         LinkEvent event4 = linkStore.createOrUpdateLink(PID,
463                 new DefaultLinkDescription(src, dst, DIRECT));
464         verifyNoBroadcastMessage(message);
465
466         assertNull("No change event expected", event4);
467
468         // update link annotation (Primary)
469         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
470         LinkEvent event5 = linkStore.createOrUpdateLink(PID,
471                 new DefaultLinkDescription(src, dst, DIRECT, A2_2));
472         verifyLinkBroadcastMessage(PID, NID1, src, dst, DIRECT, message, subject, encoder);
473
474         assertLink(DID1, P1, DID2, P2, DIRECT, event5.subject());
475         assertAnnotationsEquals(event5.subject().annotations(), A2, A2_2, A1);
476         assertEquals(LINK_UPDATED, event5.type());
477
478         // update link annotation (Ancillary)
479         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
480         LinkEvent event6 = linkStore.createOrUpdateLink(PIDA,
481                 new DefaultLinkDescription(src, dst, DIRECT, A1_2));
482         verifyLinkBroadcastMessage(PIDA, NID1, src, dst, DIRECT, message, subject, encoder);
483
484         assertLink(DID1, P1, DID2, P2, DIRECT, event6.subject());
485         assertAnnotationsEquals(event6.subject().annotations(), A2, A2_2, A1, A1_2);
486         assertEquals(LINK_UPDATED, event6.type());
487
488         // update link type (Ancillary) : ignored
489         resetCommunicatorExpectingSingleBroadcast(message, subject, encoder);
490         LinkEvent event7 = linkStore.createOrUpdateLink(PIDA,
491                 new DefaultLinkDescription(src, dst, EDGE));
492         verifyNoBroadcastMessage(message);
493         assertNull("Ancillary change other than annotation is ignored", event7);
494     }
495
496
497     @Test
498     public final void testRemoveLink() {
499         final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
500         final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
501         LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
502         LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
503
504         putLink(linkId1, DIRECT, A1);
505         putLink(linkId2, DIRECT, A2);
506
507         // DID1,P1 => DID2,P2
508         // DID2,P2 => DID1,P1
509         // DID1,P2 => DID2,P3
510
511         LinkEvent event = linkStore.removeLink(d1P1, d2P2);
512         assertEquals(LINK_REMOVED, event.type());
513         assertAnnotationsEquals(event.subject().annotations(), A1);
514         LinkEvent event2 = linkStore.removeLink(d1P1, d2P2);
515         assertNull(event2);
516
517         assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
518         assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(), A2);
519
520         // annotations, etc. should not survive remove
521         putLink(linkId1, DIRECT);
522         assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
523         assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
524     }
525
526     @Test
527     public final void testAncillaryVisible() {
528         ConnectPoint src = new ConnectPoint(DID1, P1);
529         ConnectPoint dst = new ConnectPoint(DID2, P2);
530
531         // add Ancillary link
532         linkStore.createOrUpdateLink(PIDA,
533                     new DefaultLinkDescription(src, dst, INDIRECT, A1));
534
535         // Ancillary only link should not be visible
536         assertEquals(1, linkStore.getLinkCount());
537         assertNotNull(linkStore.getLink(src, dst));
538     }
539
540     // If Delegates should be called only on remote events,
541     // then Simple* should never call them, thus not test required.
542     @Ignore("Ignore until Delegate spec. is clear.")
543     @Test
544     public final void testEvents() throws InterruptedException {
545
546         final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
547         final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
548         final LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
549
550         final CountDownLatch addLatch = new CountDownLatch(1);
551         LinkStoreDelegate checkAdd = new LinkStoreDelegate() {
552             @Override
553             public void notify(LinkEvent event) {
554                 assertEquals(LINK_ADDED, event.type());
555                 assertLink(linkId1, INDIRECT, event.subject());
556                 addLatch.countDown();
557             }
558         };
559         final CountDownLatch updateLatch = new CountDownLatch(1);
560         LinkStoreDelegate checkUpdate = new LinkStoreDelegate() {
561             @Override
562             public void notify(LinkEvent event) {
563                 assertEquals(LINK_UPDATED, event.type());
564                 assertLink(linkId1, DIRECT, event.subject());
565                 updateLatch.countDown();
566             }
567         };
568         final CountDownLatch removeLatch = new CountDownLatch(1);
569         LinkStoreDelegate checkRemove = new LinkStoreDelegate() {
570             @Override
571             public void notify(LinkEvent event) {
572                 assertEquals(LINK_REMOVED, event.type());
573                 assertLink(linkId1, DIRECT, event.subject());
574                 removeLatch.countDown();
575             }
576         };
577
578         linkStore.setDelegate(checkAdd);
579         putLink(linkId1, INDIRECT);
580         assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
581
582         linkStore.unsetDelegate(checkAdd);
583         linkStore.setDelegate(checkUpdate);
584         putLink(linkId1, DIRECT);
585         assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
586
587         linkStore.unsetDelegate(checkUpdate);
588         linkStore.setDelegate(checkRemove);
589         linkStore.removeLink(d1P1, d2P2);
590         assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
591     }
592
593     private static final class TestClusterService extends StaticClusterService {
594
595         public TestClusterService() {
596             localNode = ONOS1;
597             nodes.put(NID1, ONOS1);
598             nodeStates.put(NID1, ACTIVE);
599
600             nodes.put(NID2, ONOS2);
601             nodeStates.put(NID2, ACTIVE);
602         }
603     }
604
605     private final class TestDeviceClockService extends DeviceClockServiceAdapter {
606
607         private final AtomicLong ticker = new AtomicLong();
608
609         @Override
610         public Timestamp getTimestamp(DeviceId deviceId) {
611             if (DID1.equals(deviceId)) {
612                 return new MastershipBasedTimestamp(1, ticker.getAndIncrement());
613             } else if (DID2.equals(deviceId)) {
614                 return new MastershipBasedTimestamp(2, ticker.getAndIncrement());
615             } else {
616                 throw new IllegalStateException();
617             }
618         }
619
620         @Override
621         public boolean isTimestampAvailable(DeviceId deviceId) {
622             return DID1.equals(deviceId) || DID2.equals(deviceId);
623         }
624     }
625
626     private final class TestMastershipService extends MastershipServiceAdapter {
627         @Override
628         public NodeId getMasterFor(DeviceId deviceId) {
629             return NID1;
630         }
631     }
632 }