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