2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.openflowplugin.applications.frsync.impl;
10 import com.google.common.util.concurrent.Futures;
11 import java.text.ParseException;
12 import java.text.SimpleDateFormat;
13 import java.util.Collections;
15 import java.util.Optional;
16 import org.junit.Assert;
17 import org.junit.Before;
18 import org.junit.Test;
19 import org.junit.runner.RunWith;
20 import org.mockito.ArgumentMatchers;
21 import org.mockito.Mock;
22 import org.mockito.Mockito;
23 import org.mockito.junit.MockitoJUnitRunner;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.DataObjectModification;
26 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
27 import org.opendaylight.mdsal.binding.api.DataTreeModification;
28 import org.opendaylight.mdsal.binding.api.ReadTransaction;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
31 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
32 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
33 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
34 import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
35 import org.opendaylight.openflowplugin.applications.frsync.impl.clustering.DeviceMastershipManager;
36 import org.opendaylight.openflowplugin.applications.frsync.util.ReconciliationRegistry;
37 import org.opendaylight.openflowplugin.applications.frsync.util.SyncupEntry;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableStatisticsGatheringStatus;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.snapshot.gathering.status.grouping.SnapshotGatheringStatusEnd;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
48 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 * Test for {@link SimplifiedOperationalListener}.
54 @RunWith(MockitoJUnitRunner.class)
55 public class SimplifiedOperationalListenerTest {
57 private static final NodeId NODE_ID = new NodeId("testNode");
58 private InstanceIdentifier<FlowCapableNode> fcNodePath;
59 private SimplifiedOperationalListener nodeListenerOperational;
60 private final LogicalDatastoreType configDS = LogicalDatastoreType.CONFIGURATION;
61 private final LogicalDatastoreType operationalDS = LogicalDatastoreType.OPERATIONAL;
62 private final SimpleDateFormat simpleDateFormat =
63 new SimpleDateFormat(SimplifiedOperationalListener.DATE_AND_TIME_FORMAT);
66 private SyncReactor reactor;
68 private ReadTransaction roTx;
70 private DataTreeModification<Node> dataTreeModification;
72 private DataObjectModification<Node> operationalModification;
74 private FlowCapableNode configNode;
76 private Node operationalNode;
78 private FlowCapableNode fcOperationalNode;
80 private FlowCapableStatisticsGatheringStatus statisticsGatheringStatus;
82 private SnapshotGatheringStatusEnd snapshotGatheringStatusEnd;
84 private ReconciliationRegistry reconciliationRegistry;
86 private DeviceMastershipManager deviceMastershipManager;
88 private Map<NodeConnectorKey, NodeConnector> nodeConnector;
90 private Node operationalNodeEmpty;
94 final DataBroker db = Mockito.mock(DataBroker.class);
95 final FlowCapableNodeSnapshotDao configSnapshot = new FlowCapableNodeSnapshotDao();
96 final FlowCapableNodeSnapshotDao operationalSnapshot = new FlowCapableNodeSnapshotDao();
97 final FlowCapableNodeDao configDao = new FlowCapableNodeCachedDao(configSnapshot,
98 new FlowCapableNodeOdlDao(db, LogicalDatastoreType.CONFIGURATION));
100 nodeListenerOperational = new SimplifiedOperationalListener(reactor, operationalSnapshot, configDao,
101 reconciliationRegistry, deviceMastershipManager);
102 InstanceIdentifier<Node> nodePath = InstanceIdentifier.create(Nodes.class).child(Node.class,
103 new NodeKey(NODE_ID));
104 fcNodePath = nodePath.augmentation(FlowCapableNode.class);
106 final DataTreeIdentifier<Node> dataTreeIdentifier =
107 DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, nodePath);
109 Mockito.when(db.newReadOnlyTransaction()).thenReturn(roTx);
110 Mockito.when(operationalNode.getId()).thenReturn(NODE_ID);
111 Mockito.when(dataTreeModification.getRootPath()).thenReturn(dataTreeIdentifier);
112 Mockito.when(dataTreeModification.getRootNode()).thenReturn(operationalModification);
113 Mockito.when(operationalNode.augmentation(FlowCapableNode.class)).thenReturn(fcOperationalNode);
117 public void testDSLogicalType() {
118 Assert.assertEquals(LogicalDatastoreType.OPERATIONAL, nodeListenerOperational.dsType());
122 public void testOnDataTreeChangedAddPhysical() {
124 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
125 Mockito.verify(deviceMastershipManager).onDeviceConnected(NODE_ID);
126 Mockito.verifyZeroInteractions(reactor);
130 public void testOnDataTreeChangedDeletePhysical() {
131 Mockito.when(operationalModification.getDataBefore()).thenReturn(operationalNode);
132 Mockito.when(operationalModification.getDataAfter()).thenReturn(null);
134 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
136 Mockito.verify(deviceMastershipManager).onDeviceDisconnected(NODE_ID);
137 Mockito.verifyZeroInteractions(reactor);
141 public void testOnDataTreeChangedDeleteLogical() {
142 Mockito.when(operationalModification.getDataBefore()).thenReturn(operationalNode);
143 Mockito.when(operationalNode.getNodeConnector()).thenReturn(nodeConnector);
144 Mockito.when(operationalNodeEmpty.getId()).thenReturn(NODE_ID);
145 Mockito.when(operationalModification.getDataAfter()).thenReturn(operationalNodeEmpty);
146 Mockito.when(operationalNodeEmpty.getNodeConnector()).thenReturn(null);
148 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
150 Mockito.verify(deviceMastershipManager).onDeviceDisconnected(NODE_ID);
151 Mockito.verifyZeroInteractions(reactor);
155 public void testOnDataTreeChangedReconcileNotRegistered() {
156 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(false);
159 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
161 Mockito.verifyZeroInteractions(reactor);
165 public void testOnDataTreeChangedReconcileButStaticsGatheringNotStarted() {
166 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
168 Mockito.when(operationalNode.augmentation(FlowCapableStatisticsGatheringStatus.class)).thenReturn(null);
170 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
172 Mockito.verifyZeroInteractions(reactor);
176 public void testOnDataTreeChangedReconcileButStaticsGatheringNotFinished() {
177 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
179 Mockito.when(operationalNode.augmentation(FlowCapableStatisticsGatheringStatus.class))
180 .thenReturn(statisticsGatheringStatus);
181 Mockito.when(statisticsGatheringStatus.getSnapshotGatheringStatusEnd()).thenReturn(null);
183 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
185 Mockito.verifyZeroInteractions(reactor);
189 public void testOnDataTreeChangedReconcileButStaticsGatheringNotSuccessful() {
190 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
192 Mockito.when(operationalNode.augmentation(FlowCapableStatisticsGatheringStatus.class))
193 .thenReturn(statisticsGatheringStatus);
194 Mockito.when(statisticsGatheringStatus.getSnapshotGatheringStatusEnd()).thenReturn(snapshotGatheringStatusEnd);
195 Mockito.when(snapshotGatheringStatusEnd.isSucceeded()).thenReturn(false);
197 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
199 Mockito.verifyZeroInteractions(reactor);
203 public void testOnDataTreeChangedReconcileAndFreshOperationalNotPresent() throws ParseException {
204 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
206 prepareFreshOperational(false);
208 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
210 Mockito.verifyZeroInteractions(reactor);
214 public void testOnDataTreeChangedReconcileAndFreshOperationalPresent() throws Exception {
215 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
217 prepareFreshOperational(true);
218 final SyncupEntry syncupEntry = loadConfigDSAndPrepareSyncupEntry(
219 configNode, configDS, fcOperationalNode, operationalDS);
221 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
223 Mockito.verify(reactor).syncup(fcNodePath, syncupEntry);
224 Mockito.verify(roTx).close();
229 public void testOnDataTreeChangedReconcileAndFreshOperationalNotPresentButAdd() throws ParseException {
230 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
232 prepareFreshOperational(false);
233 final SyncupEntry syncupEntry = loadConfigDSAndPrepareSyncupEntry(
234 configNode, configDS, fcOperationalNode, operationalDS);
236 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
238 Mockito.verify(reactor).syncup(fcNodePath, syncupEntry);
239 Mockito.verify(roTx).close();
243 public void testOnDataTreeChangedReconcileAndConfigNotPresent() throws Exception {
244 // Related to bug 5920 -> https://bugs.opendaylight.org/show_bug.cgi?id=5920
245 Mockito.when(reconciliationRegistry.isRegistered(NODE_ID)).thenReturn(true);
247 prepareFreshOperational(true);
249 Mockito.doReturn(FluentFutures.immediateFluentFuture(Optional.empty())).when(roTx)
250 .read(LogicalDatastoreType.CONFIGURATION, fcNodePath);
252 nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
254 Mockito.verify(reconciliationRegistry).unregisterIfRegistered(NODE_ID);
255 Mockito.verifyZeroInteractions(reactor);
256 Mockito.verify(roTx).close();
259 private void prepareFreshOperational(final boolean afterRegistration) throws ParseException {
260 Mockito.when(operationalNode.augmentation(FlowCapableStatisticsGatheringStatus.class))
261 .thenReturn(statisticsGatheringStatus);
262 Mockito.when(statisticsGatheringStatus.getSnapshotGatheringStatusEnd()).thenReturn(snapshotGatheringStatusEnd);
263 Mockito.when(snapshotGatheringStatusEnd.isSucceeded()).thenReturn(true);
264 Mockito.when(snapshotGatheringStatusEnd.getEnd()).thenReturn(Mockito.mock(DateAndTime.class));
265 final String timestampBefore = "0000-12-12T01:01:01.000-07:00";
266 final String timestampAfter = "9999-12-12T01:01:01.000-07:00";
267 if (afterRegistration) {
268 Mockito.when(snapshotGatheringStatusEnd.getEnd().getValue()).thenReturn(timestampAfter);
269 Mockito.when(reconciliationRegistry.getRegistrationTimestamp(NODE_ID))
270 .thenReturn(simpleDateFormat.parse(timestampBefore));
272 Mockito.when(snapshotGatheringStatusEnd.getEnd().getValue()).thenReturn(timestampBefore);
273 Mockito.when(reconciliationRegistry.getRegistrationTimestamp(NODE_ID))
274 .thenReturn(simpleDateFormat.parse(timestampAfter));
278 private void operationalAdd() {
279 Mockito.when(operationalModification.getDataBefore()).thenReturn(null);
280 Mockito.when(operationalModification.getDataAfter()).thenReturn(operationalNode);
283 private void operationalUpdate() {
284 Mockito.when(operationalModification.getDataBefore()).thenReturn(operationalNode);
285 Mockito.when(operationalModification.getDataAfter()).thenReturn(operationalNode);
288 private SyncupEntry loadConfigDSAndPrepareSyncupEntry(final FlowCapableNode after,
289 final LogicalDatastoreType dsTypeAfter, final FlowCapableNode before,
290 final LogicalDatastoreType dsTypeBefore) {
292 Mockito.doReturn(FluentFutures.immediateFluentFuture(Optional.of(configNode))).when(roTx)
293 .read(LogicalDatastoreType.CONFIGURATION, fcNodePath);
294 final SyncupEntry syncupEntry = new SyncupEntry(after, dsTypeAfter, before, dsTypeBefore);
295 Mockito.when(reactor.syncup(ArgumentMatchers.any(),
296 Mockito.eq(syncupEntry))).thenReturn(Futures.immediateFuture(Boolean.TRUE));