Merge "Bug 1894: Add LISP configuration options to etc/custom.properties in Karaf"
[controller.git] / opendaylight / md-sal / topology-manager / src / test / java / org / opendaylight / md / controller / topology / manager / FlowCapableTopologyExporterTest.java
1 /*
2  * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.md.controller.topology.manager;
10
11 import static org.junit.Assert.fail;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertTrue;
14 import static org.junit.Assert.assertNotNull;
15 import static org.mockito.Mockito.doReturn;
16 import static org.mockito.Mockito.doAnswer;
17 import static org.mockito.Mockito.any;
18 import static org.mockito.Mockito.eq;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.inOrder;
21 import static org.mockito.Mockito.atLeast;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.verify;
24
25 import java.util.Arrays;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Set;
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.TimeUnit;
33
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.mockito.ArgumentCaptor;
38 import org.mockito.InOrder;
39 import org.mockito.Mock;
40 import org.mockito.MockitoAnnotations;
41 import org.mockito.invocation.InvocationOnMock;
42 import org.mockito.stubbing.Answer;
43 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
44 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
45 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
46 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
47 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
48 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
49 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdatedBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdatedBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkDiscoveredBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.LinkRemovedBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.StateBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemovedBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdatedBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemovedBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdatedBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.topology.inventory.rev131030.InventoryNode;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.topology.inventory.rev131030.InventoryNodeConnector;
69 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
70 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
71 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
72 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
73 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
74 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Destination;
75 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder;
76 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.Source;
77 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder;
78 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
79 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
80 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
81 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
82 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder;
83 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
84 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
85 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
86 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
87 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
88 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
89 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
90
91 import com.google.common.base.Optional;
92 import com.google.common.util.concurrent.CheckedFuture;
93 import com.google.common.util.concurrent.Futures;
94 import com.google.common.util.concurrent.SettableFuture;
95 import com.google.common.util.concurrent.Uninterruptibles;
96
97 public class FlowCapableTopologyExporterTest {
98
99     @Mock
100     private DataBroker mockDataBroker;
101
102     @Mock
103     private BindingTransactionChain mockTxChain;
104
105     private OperationProcessor processor;
106
107     private FlowCapableTopologyExporter exporter;
108
109     private InstanceIdentifier<Topology> topologyIID;
110
111     private final ExecutorService executor = Executors.newFixedThreadPool(1);
112
113     @Before
114     public void setUp() {
115         MockitoAnnotations.initMocks(this);
116
117         doReturn(mockTxChain).when(mockDataBroker)
118                 .createTransactionChain(any(TransactionChainListener.class));
119
120         processor = new OperationProcessor(mockDataBroker);
121
122         topologyIID = InstanceIdentifier.create(NetworkTopology.class)
123                 .child(Topology.class, new TopologyKey(new TopologyId("test")));
124         exporter = new FlowCapableTopologyExporter(processor, topologyIID);
125
126         executor.execute(processor);
127     }
128
129     @After
130     public void tearDown() {
131         executor.shutdownNow();
132     }
133
134     @SuppressWarnings({ "rawtypes" })
135     @Test
136     public void testOnNodeRemoved() {
137
138         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
139                                                                 nodeKey = newInvNodeKey("node1");
140         InstanceIdentifier<?> invNodeID = InstanceIdentifier.create(Nodes.class).child(
141                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
142                 nodeKey);
143
144         List<Link> linkList = Arrays.asList(
145                 newLink("link1", newSourceNode("node1"), newDestNode("dest")),
146                 newLink("link2", newSourceNode("source"), newDestNode("node1")),
147                 newLink("link2", newSourceNode("source2"), newDestNode("dest2")));
148         final Topology topology = new TopologyBuilder().setLink(linkList).build();
149
150         InstanceIdentifier[] expDeletedIIDs = {
151                 topologyIID.child(Link.class, linkList.get(0).getKey()),
152                 topologyIID.child(Link.class, linkList.get(1).getKey()),
153                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
154             };
155
156         SettableFuture<Optional<Topology>> readFuture = SettableFuture.create();
157         ReadWriteTransaction mockTx1 = mock(ReadWriteTransaction.class);
158         doReturn(Futures.makeChecked(readFuture, ReadFailedException.MAPPER)).when(mockTx1)
159                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
160
161         CountDownLatch submitLatch1 = setupStubbedSubmit(mockTx1);
162
163         int expDeleteCalls = expDeletedIIDs.length;
164         CountDownLatch deleteLatch = new CountDownLatch(expDeleteCalls);
165         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
166                 ArgumentCaptor.forClass(InstanceIdentifier.class);
167         setupStubbedDeletes(mockTx1, deletedLinkIDs, deleteLatch);
168
169         ReadWriteTransaction mockTx2 = mock(ReadWriteTransaction.class);
170         setupStubbedDeletes(mockTx2, deletedLinkIDs, deleteLatch);
171         CountDownLatch submitLatch2 = setupStubbedSubmit(mockTx2);
172
173         doReturn(mockTx1).doReturn(mockTx2).when(mockTxChain).newReadWriteTransaction();
174
175         exporter.onNodeRemoved(new NodeRemovedBuilder().setNodeRef(new NodeRef(invNodeID)).build());
176
177         waitForSubmit(submitLatch1);
178
179         setReadFutureAsync(topology, readFuture);
180
181         waitForDeletes(expDeleteCalls, deleteLatch);
182
183         waitForSubmit(submitLatch2);
184
185         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
186
187         verifyMockTx(mockTx1);
188         verifyMockTx(mockTx2);
189     }
190
191     @SuppressWarnings({ "rawtypes" })
192     @Test
193     public void testOnNodeRemovedWithNoTopology() {
194
195         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
196                                                                 nodeKey = newInvNodeKey("node1");
197         InstanceIdentifier<?> invNodeID = InstanceIdentifier.create(Nodes.class).child(
198                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
199                 nodeKey);
200
201         InstanceIdentifier[] expDeletedIIDs = {
202                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
203             };
204
205         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
206         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockTx)
207                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
208         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
209
210         CountDownLatch deleteLatch = new CountDownLatch(1);
211         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
212                 ArgumentCaptor.forClass(InstanceIdentifier.class);
213         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
214
215         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
216
217         exporter.onNodeRemoved(new NodeRemovedBuilder().setNodeRef(new NodeRef(invNodeID)).build());
218
219         waitForSubmit(submitLatch);
220
221         waitForDeletes(1, deleteLatch);
222
223         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
224     }
225
226     @SuppressWarnings("rawtypes")
227     @Test
228     public void testOnNodeConnectorRemoved() {
229
230         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
231                                                                   nodeKey = newInvNodeKey("node1");
232
233         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
234                 newInvNodeConnKey("tp1");
235
236         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
237
238         List<Link> linkList = Arrays.asList(
239                 newLink("link1", newSourceTp("tp1"), newDestTp("dest")),
240                 newLink("link2", newSourceTp("source"), newDestTp("tp1")),
241                 newLink("link3", newSourceTp("source2"), newDestTp("dest2")));
242         final Topology topology = new TopologyBuilder().setLink(linkList).build();
243
244         InstanceIdentifier[] expDeletedIIDs = {
245                 topologyIID.child(Link.class, linkList.get(0).getKey()),
246                 topologyIID.child(Link.class, linkList.get(1).getKey()),
247                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
248                         .child(TerminationPoint.class, new TerminationPointKey(new TpId("tp1")))
249             };
250
251         final SettableFuture<Optional<Topology>> readFuture = SettableFuture.create();
252         ReadWriteTransaction mockTx1 = mock(ReadWriteTransaction.class);
253         doReturn(Futures.makeChecked(readFuture, ReadFailedException.MAPPER)).when(mockTx1)
254                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
255
256         CountDownLatch submitLatch1 = setupStubbedSubmit(mockTx1);
257
258         int expDeleteCalls = expDeletedIIDs.length;
259         CountDownLatch deleteLatch = new CountDownLatch(expDeleteCalls);
260         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
261                 ArgumentCaptor.forClass(InstanceIdentifier.class);
262         setupStubbedDeletes(mockTx1, deletedLinkIDs, deleteLatch);
263
264         ReadWriteTransaction mockTx2 = mock(ReadWriteTransaction.class);
265         setupStubbedDeletes(mockTx2, deletedLinkIDs, deleteLatch);
266         CountDownLatch submitLatch2 = setupStubbedSubmit(mockTx2);
267
268         doReturn(mockTx1).doReturn(mockTx2).when(mockTxChain).newReadWriteTransaction();
269
270         exporter.onNodeConnectorRemoved(new NodeConnectorRemovedBuilder().setNodeConnectorRef(
271                 new NodeConnectorRef(invNodeConnID)).build());
272
273         waitForSubmit(submitLatch1);
274
275         setReadFutureAsync(topology, readFuture);
276
277         waitForDeletes(expDeleteCalls, deleteLatch);
278
279         waitForSubmit(submitLatch2);
280
281         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
282
283         verifyMockTx(mockTx1);
284         verifyMockTx(mockTx2);
285     }
286
287     @SuppressWarnings("rawtypes")
288     @Test
289     public void testOnNodeConnectorRemovedWithNoTopology() {
290
291         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
292                                                                   nodeKey = newInvNodeKey("node1");
293
294         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
295                 newInvNodeConnKey("tp1");
296
297         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
298
299         InstanceIdentifier[] expDeletedIIDs = {
300                 topologyIID.child(Node.class, new NodeKey(new NodeId("node1")))
301                         .child(TerminationPoint.class, new TerminationPointKey(new TpId("tp1")))
302             };
303
304         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
305         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(mockTx)
306                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
307         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
308
309         CountDownLatch deleteLatch = new CountDownLatch(1);
310         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
311                 ArgumentCaptor.forClass(InstanceIdentifier.class);
312         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
313
314         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
315
316         exporter.onNodeConnectorRemoved(new NodeConnectorRemovedBuilder().setNodeConnectorRef(
317                 new NodeConnectorRef(invNodeConnID)).build());
318
319         waitForSubmit(submitLatch);
320
321         waitForDeletes(1, deleteLatch);
322
323         assertDeletedIDs(expDeletedIIDs, deletedLinkIDs);
324     }
325
326     @Test
327     public void testOnNodeUpdated() {
328
329         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
330                                                             nodeKey = newInvNodeKey("node1");
331         InstanceIdentifier<?> invNodeID = InstanceIdentifier.create(Nodes.class).child(
332                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
333                 nodeKey);
334
335         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
336         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
337         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
338
339         exporter.onNodeUpdated(new NodeUpdatedBuilder().setNodeRef(new NodeRef(invNodeID))
340                 .setId(nodeKey.getId()).addAugmentation(FlowCapableNodeUpdated.class,
341                         new FlowCapableNodeUpdatedBuilder().build()).build());
342
343         waitForSubmit(submitLatch);
344
345         ArgumentCaptor<Node> mergedNode = ArgumentCaptor.forClass(Node.class);
346         NodeId expNodeId = new NodeId("node1");
347         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(topologyIID.child(Node.class,
348                 new NodeKey(expNodeId))), mergedNode.capture(), eq(true));
349         assertEquals("getNodeId", expNodeId, mergedNode.getValue().getNodeId());
350         InventoryNode augmentation = mergedNode.getValue().getAugmentation(InventoryNode.class);
351         assertNotNull("Missing augmentation", augmentation);
352         assertEquals("getInventoryNodeRef", new NodeRef(invNodeID), augmentation.getInventoryNodeRef());
353     }
354
355     @SuppressWarnings("rawtypes")
356     @Test
357     public void testOnNodeConnectorUpdated() {
358
359         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
360                                                                  nodeKey = newInvNodeKey("node1");
361
362         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
363                 newInvNodeConnKey("tp1");
364
365         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
366
367         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
368         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
369         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
370
371         exporter.onNodeConnectorUpdated(new NodeConnectorUpdatedBuilder().setNodeConnectorRef(
372                 new NodeConnectorRef(invNodeConnID)).setId(ncKey.getId()).addAugmentation(
373                         FlowCapableNodeConnectorUpdated.class,
374                         new FlowCapableNodeConnectorUpdatedBuilder().build()).build());
375
376         waitForSubmit(submitLatch);
377
378         ArgumentCaptor<TerminationPoint> mergedNode = ArgumentCaptor.forClass(TerminationPoint.class);
379         NodeId expNodeId = new NodeId("node1");
380         TpId expTpId = new TpId("tp1");
381         InstanceIdentifier<TerminationPoint> expTpPath = topologyIID.child(
382                 Node.class, new NodeKey(expNodeId)).child(TerminationPoint.class,
383                         new TerminationPointKey(expTpId));
384         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(expTpPath),
385                 mergedNode.capture(), eq(true));
386         assertEquals("getTpId", expTpId, mergedNode.getValue().getTpId());
387         InventoryNodeConnector augmentation = mergedNode.getValue().getAugmentation(
388                 InventoryNodeConnector.class);
389         assertNotNull("Missing augmentation", augmentation);
390         assertEquals("getInventoryNodeConnectorRef", new NodeConnectorRef(invNodeConnID),
391                 augmentation.getInventoryNodeConnectorRef());
392     }
393
394     @SuppressWarnings("rawtypes")
395     @Test
396     public void testOnNodeConnectorUpdatedWithLinkStateDown() {
397
398         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
399                                                                  nodeKey = newInvNodeKey("node1");
400
401         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
402                 newInvNodeConnKey("tp1");
403
404         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
405
406         List<Link> linkList = Arrays.asList(newLink("link1", newSourceTp("tp1"), newDestTp("dest")));
407         Topology topology = new TopologyBuilder().setLink(linkList).build();
408
409         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
410         doReturn(Futures.immediateCheckedFuture(Optional.of(topology))).when(mockTx)
411                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
412         setupStubbedSubmit(mockTx);
413
414         CountDownLatch deleteLatch = new CountDownLatch(1);
415         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
416                 ArgumentCaptor.forClass(InstanceIdentifier.class);
417         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
418
419         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
420
421         exporter.onNodeConnectorUpdated(new NodeConnectorUpdatedBuilder().setNodeConnectorRef(
422                 new NodeConnectorRef(invNodeConnID)).setId(ncKey.getId()).addAugmentation(
423                         FlowCapableNodeConnectorUpdated.class,
424                         new FlowCapableNodeConnectorUpdatedBuilder().setState(
425                                 new StateBuilder().setLinkDown(true).build()).build()).build());
426
427         waitForDeletes(1, deleteLatch);
428
429         InstanceIdentifier<TerminationPoint> expTpPath = topologyIID.child(
430                 Node.class, new NodeKey(new NodeId("node1"))).child(TerminationPoint.class,
431                         new TerminationPointKey(new TpId("tp1")));
432
433         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(expTpPath),
434                 any(TerminationPoint.class), eq(true));
435
436         assertDeletedIDs(new InstanceIdentifier[]{topologyIID.child(Link.class,
437                 linkList.get(0).getKey())}, deletedLinkIDs);
438     }
439
440
441     @SuppressWarnings("rawtypes")
442     @Test
443     public void testOnNodeConnectorUpdatedWithPortDown() {
444
445         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
446                                                                  nodeKey = newInvNodeKey("node1");
447
448         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey =
449                 newInvNodeConnKey("tp1");
450
451         InstanceIdentifier<?> invNodeConnID = newNodeConnID(nodeKey, ncKey);
452
453         List<Link> linkList = Arrays.asList(newLink("link1", newSourceTp("tp1"), newDestTp("dest")));
454         Topology topology = new TopologyBuilder().setLink(linkList).build();
455
456         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
457         doReturn(Futures.immediateCheckedFuture(Optional.of(topology))).when(mockTx)
458                 .read(LogicalDatastoreType.OPERATIONAL, topologyIID);
459         setupStubbedSubmit(mockTx);
460
461         CountDownLatch deleteLatch = new CountDownLatch(1);
462         ArgumentCaptor<InstanceIdentifier> deletedLinkIDs =
463                 ArgumentCaptor.forClass(InstanceIdentifier.class);
464         setupStubbedDeletes(mockTx, deletedLinkIDs, deleteLatch);
465
466         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
467
468         exporter.onNodeConnectorUpdated(new NodeConnectorUpdatedBuilder().setNodeConnectorRef(
469                 new NodeConnectorRef(invNodeConnID)).setId(ncKey.getId()).addAugmentation(
470                         FlowCapableNodeConnectorUpdated.class,
471                         new FlowCapableNodeConnectorUpdatedBuilder().setConfiguration(
472                                 new PortConfig(true, true, true, true)).build()).build());
473
474         waitForDeletes(1, deleteLatch);
475
476         InstanceIdentifier<TerminationPoint> expTpPath = topologyIID.child(
477                 Node.class, new NodeKey(new NodeId("node1"))).child(TerminationPoint.class,
478                         new TerminationPointKey(new TpId("tp1")));
479
480         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(expTpPath),
481                 any(TerminationPoint.class), eq(true));
482
483         assertDeletedIDs(new InstanceIdentifier[]{topologyIID.child(Link.class,
484                 linkList.get(0).getKey())}, deletedLinkIDs);
485     }
486
487     @Test
488     public void testOnLinkDiscovered() {
489
490         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
491                 sourceNodeKey = newInvNodeKey("sourceNode");
492         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
493                 sourceNodeConnKey = newInvNodeConnKey("sourceTP");
494         InstanceIdentifier<?> sourceConnID = newNodeConnID(sourceNodeKey, sourceNodeConnKey);
495
496         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
497                 destNodeKey = newInvNodeKey("destNode");
498         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
499                 destNodeConnKey = newInvNodeConnKey("destTP");
500         InstanceIdentifier<?> destConnID = newNodeConnID(destNodeKey, destNodeConnKey);
501
502         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
503         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
504         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
505
506         exporter.onLinkDiscovered(new LinkDiscoveredBuilder().setSource(
507                 new NodeConnectorRef(sourceConnID)).setDestination(
508                         new NodeConnectorRef(destConnID)).build());
509
510         waitForSubmit(submitLatch);
511
512         ArgumentCaptor<Link> mergedNode = ArgumentCaptor.forClass(Link.class);
513         verify(mockTx).merge(eq(LogicalDatastoreType.OPERATIONAL), eq(topologyIID.child(
514                 Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId())))),
515                 mergedNode.capture(), eq(true));
516         assertEquals("Source node ID", "sourceNode",
517                 mergedNode.getValue().getSource().getSourceNode().getValue());
518         assertEquals("Dest TP ID", "sourceTP",
519                 mergedNode.getValue().getSource().getSourceTp().getValue());
520         assertEquals("Dest node ID", "destNode",
521                 mergedNode.getValue().getDestination().getDestNode().getValue());
522         assertEquals("Dest TP ID", "destTP",
523                 mergedNode.getValue().getDestination().getDestTp().getValue());
524     }
525
526     @Test
527     public void testOnLinkRemoved() {
528
529         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
530                 sourceNodeKey = newInvNodeKey("sourceNode");
531         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
532                 sourceNodeConnKey = newInvNodeConnKey("sourceTP");
533         InstanceIdentifier<?> sourceConnID = newNodeConnID(sourceNodeKey, sourceNodeConnKey);
534
535         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
536                 destNodeKey = newInvNodeKey("destNode");
537         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey
538                 destNodeConnKey = newInvNodeConnKey("destTP");
539         InstanceIdentifier<?> destConnID = newNodeConnID(destNodeKey, destNodeConnKey);
540
541         ReadWriteTransaction mockTx = mock(ReadWriteTransaction.class);
542         CountDownLatch submitLatch = setupStubbedSubmit(mockTx);
543         doReturn(mockTx).when(mockTxChain).newReadWriteTransaction();
544
545         exporter.onLinkRemoved(new LinkRemovedBuilder().setSource(
546                 new NodeConnectorRef(sourceConnID)).setDestination(
547                         new NodeConnectorRef(destConnID)).build());
548
549         waitForSubmit(submitLatch);
550
551         verify(mockTx).delete(LogicalDatastoreType.OPERATIONAL, topologyIID.child(
552                 Link.class, new LinkKey(new LinkId(sourceNodeConnKey.getId()))));
553     }
554
555     private void verifyMockTx(ReadWriteTransaction mockTx) {
556         InOrder inOrder = inOrder(mockTx);
557         inOrder.verify(mockTx, atLeast(0)).submit();
558         inOrder.verify(mockTx, never()).delete(eq(LogicalDatastoreType.OPERATIONAL),
559               any(InstanceIdentifier.class));
560     }
561
562     @SuppressWarnings("rawtypes")
563     private void assertDeletedIDs(InstanceIdentifier[] expDeletedIIDs,
564             ArgumentCaptor<InstanceIdentifier> deletedLinkIDs) {
565         Set<InstanceIdentifier> actualIIDs = new HashSet<>(deletedLinkIDs.getAllValues());
566         for(InstanceIdentifier id: expDeletedIIDs) {
567             assertTrue("Missing expected deleted IID " + id, actualIIDs.contains(id));
568         }
569     }
570
571     private void setReadFutureAsync(final Topology topology,
572             final SettableFuture<Optional<Topology>> readFuture) {
573         new Thread() {
574             @Override
575             public void run() {
576                 Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
577                 readFuture.set(Optional.of(topology));
578             }
579
580         }.start();
581     }
582
583     private void waitForSubmit(CountDownLatch latch) {
584         assertEquals("Transaction submitted", true,
585                 Uninterruptibles.awaitUninterruptibly(latch, 5, TimeUnit.SECONDS));
586     }
587
588     private void waitForDeletes(int expDeleteCalls, final CountDownLatch latch) {
589         boolean done = Uninterruptibles.awaitUninterruptibly(latch, 5, TimeUnit.SECONDS);
590         if(!done) {
591             fail("Expected " + expDeleteCalls + " delete calls. Actual: " +
592                     (expDeleteCalls - latch.getCount()));
593         }
594     }
595
596     private CountDownLatch setupStubbedSubmit(ReadWriteTransaction mockTx) {
597         final CountDownLatch latch = new CountDownLatch(1);
598         doAnswer(new Answer<CheckedFuture<Void, TransactionCommitFailedException>>() {
599             @Override
600             public CheckedFuture<Void, TransactionCommitFailedException> answer(
601                                                             InvocationOnMock invocation) {
602                 latch.countDown();
603                 return Futures.immediateCheckedFuture(null);
604             }
605         }).when(mockTx).submit();
606
607         return latch;
608     }
609
610     @SuppressWarnings("rawtypes")
611     private void setupStubbedDeletes(ReadWriteTransaction mockTx,
612             ArgumentCaptor<InstanceIdentifier> deletedLinkIDs, final CountDownLatch latch) {
613         doAnswer(new Answer<Void>() {
614             @Override
615             public Void answer(InvocationOnMock invocation) {
616                 latch.countDown();
617                 return null;
618             }
619         }).when(mockTx).delete(eq(LogicalDatastoreType.OPERATIONAL), deletedLinkIDs.capture());
620     }
621
622     private org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey
623                                                                         newInvNodeKey(String id) {
624         org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey nodeKey =
625                 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey(
626                         new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.
627                                                                       rev130819.NodeId(id));
628         return nodeKey;
629     }
630
631     private NodeConnectorKey newInvNodeConnKey(String id) {
632         return new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey(
633                 new org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.
634                                                                NodeConnectorId(id));
635     }
636
637     private KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> newNodeConnID(
638             org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey nodeKey,
639             org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey ncKey) {
640         return InstanceIdentifier.create(Nodes.class).child(
641                 org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
642                 nodeKey).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.
643                         rev130819.node.NodeConnector.class, ncKey);
644     }
645
646     private Link newLink(String id, Source source, Destination dest) {
647         return new LinkBuilder().setLinkId(new LinkId(id))
648                 .setSource(source).setDestination(dest).build();
649     }
650
651     private Destination newDestTp(String id) {
652         return new DestinationBuilder().setDestTp(new TpId(id)).build();
653     }
654
655     private Source newSourceTp(String id) {
656         return new SourceBuilder().setSourceTp(new TpId(id)).build();
657     }
658
659     private Destination newDestNode(String id) {
660         return new DestinationBuilder().setDestNode(new NodeId(id)).build();
661     }
662
663     private Source newSourceNode(String id) {
664         return new SourceBuilder().setSourceNode(new NodeId(id)).build();
665     }
666 }