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