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