--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.util;
+
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class TopologyUtil {
+
+ private TopologyUtil() {
+ throw new AssertionError("Instantiating utility class.");
+ }
+
+ /**
+ * Determines the Netconf Node Node ID, given the node's instance
+ * identifier.
+ *
+ * @param pathArgument Node's path argument
+ * @return NodeId for the node
+ */
+ public static NodeId getNodeId(final InstanceIdentifier.PathArgument pathArgument) {
+ if (pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?>) {
+
+ final Identifier key = ((InstanceIdentifier.IdentifiableItem) pathArgument).getKey();
+ if(key instanceof NodeKey) {
+ return ((NodeKey) key).getNodeId();
+ }
+ }
+ throw new IllegalStateException("Unable to create NodeId from: " + pathArgument);
+ }
+
+ public static InstanceIdentifier<Topology> createTopologyId(final String topologyId) {
+ final InstanceIdentifier<NetworkTopology> networkTopology = InstanceIdentifier.create(NetworkTopology.class);
+ return networkTopology.child(Topology.class, new TopologyKey(new TopologyId(topologyId)));
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.netconf.topology.impl;
-
-import akka.actor.TypedActor;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-import java.util.List;
-import org.opendaylight.netconf.topology.StateAggregator;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class OnlySuccessStateAggregator implements StateAggregator{
-
- private static final Logger LOG = LoggerFactory.getLogger(OnlySuccessStateAggregator.class);
-
- @Override
- public ListenableFuture<Node> combineCreateAttempts(List<ListenableFuture<Node>> stateFutures) {
- final SettableFuture<Node> future = SettableFuture.create();
- final ListenableFuture<List<Node>> allAsList = Futures.allAsList(stateFutures);
- Futures.addCallback(allAsList, new FutureCallback<List<Node>>() {
- @Override
- public void onSuccess(List<Node> result) {
- for (int i = 0; i < result.size() - 1; i++) {
- if (!result.get(i).equals(result.get(i + 1))) {
- future.setException(new IllegalStateException("Create futures have different result"));
- }
- }
- future.set(result.get(0));
- }
-
- @Override
- public void onFailure(Throwable t) {
- LOG.error("One of the combined create attempts failed {}", t);
- future.setException(t);
- }
- }, TypedActor.context().dispatcher());
-
- return future;
- }
-
- @Override
- public ListenableFuture<Node> combineUpdateAttempts(List<ListenableFuture<Node>> stateFutures) {
- final SettableFuture<Node> future = SettableFuture.create();
- final ListenableFuture<List<Node>> allAsList = Futures.allAsList(stateFutures);
- Futures.addCallback(allAsList, new FutureCallback<List<Node>>() {
- @Override
- public void onSuccess(List<Node> result) {
- for (int i = 0; i < result.size() - 1; i++) {
- if (!result.get(i).equals(result.get(i + 1))) {
- future.setException(new IllegalStateException("Update futures have different result"));
- }
- }
- future.set(result.get(0));
- }
-
- @Override
- public void onFailure(Throwable t) {
- LOG.error("One of the combined update attempts failed {}", t);
- future.setException(t);
- }
- });
- return future;
- }
-
- @Override
- public ListenableFuture<Void> combineDeleteAttempts(List<ListenableFuture<Void>> stateFutures) {
- final SettableFuture<Void> future = SettableFuture.create();
- final ListenableFuture<List<Void>> allAsList = Futures.allAsList(stateFutures);
- Futures.addCallback(allAsList, new FutureCallback<List<Void>>() {
- @Override
- public void onSuccess(List<Void> result) {
- future.set(null);
- }
-
- @Override
- public void onFailure(Throwable t) {
- LOG.error("One of the combined delete attempts failed {}", t);
- future.setException(t);
- }
- });
- return future;
- }
-}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilitiesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatusBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.clustered.connection.status.NodeStatus;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.clustered.connection.status.NodeStatusBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+
+public class NetconfNodeOperationalDataAggregatorTest {
+
+ private List<ListenableFuture<Node>> stateFutures;
+
+ private NetconfNodeOperationalDataAggregator aggregator;
+
+ @Before
+ public void setUp() {
+ aggregator = new NetconfNodeOperationalDataAggregator();
+ stateFutures = Lists.newArrayList();
+ }
+
+ @Test
+ public void testCombineCreateAttempts() throws ExecutionException, InterruptedException {
+ NetconfNode testingNode = new NetconfNodeBuilder().setAvailableCapabilities(
+ new AvailableCapabilitiesBuilder().setAvailableCapability(Lists.<String>newArrayList()).build())
+ .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder().setNodeStatus(Lists.newArrayList(
+ new NodeStatusBuilder().setStatus(NodeStatus.Status.Connected).build())).build())
+ .setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected).build();
+ stateFutures.add(Futures.immediateFuture(new NodeBuilder().addAugmentation(NetconfNode.class, testingNode).build()));
+
+ ListenableFuture<Node> aggregatedCreateFuture = aggregator.combineCreateAttempts(stateFutures);
+ assertTrue(aggregatedCreateFuture.isDone());
+
+ NetconfNode aggregatedNode = aggregatedCreateFuture.get().getAugmentation(NetconfNode.class);
+ assertEquals(aggregatedNode.getClusteredConnectionStatus().getNodeStatus().get(0).getStatus(),
+ NodeStatus.Status.Connected);
+ }
+
+ @Test
+ public void testSuccessfulCombineUpdateAttempts() throws ExecutionException, InterruptedException {
+ NetconfNode testingNode = new NetconfNodeBuilder().setAvailableCapabilities(
+ new AvailableCapabilitiesBuilder().setAvailableCapability(Lists.<String>newArrayList()).build())
+ .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder().setNodeStatus(Lists.newArrayList(
+ new NodeStatusBuilder().setStatus(NodeStatus.Status.Connected).build())).build())
+ .setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected).build();
+ stateFutures.add(Futures.immediateFuture(new NodeBuilder().addAugmentation(NetconfNode.class, testingNode).build()));
+
+ ListenableFuture<Node> aggregatedUpdateFuture = aggregator.combineUpdateAttempts(stateFutures);
+ assertTrue(aggregatedUpdateFuture.isDone());
+
+ NetconfNode aggregatedNode = aggregatedUpdateFuture.get().getAugmentation(NetconfNode.class);
+ assertEquals(aggregatedNode.getClusteredConnectionStatus().getNodeStatus().get(0).getStatus(),
+ NodeStatus.Status.Connected);
+ }
+
+ @Test
+ public void testSuccessfulCombineDeleteAttempts() throws ExecutionException, InterruptedException {
+ List deleteStateFutures = Lists.newArrayList(Futures.immediateFuture(null), Futures.immediateFuture(null));
+
+ ListenableFuture<Void> deleteFuture = aggregator.combineDeleteAttempts(deleteStateFutures);
+ assertTrue(deleteFuture.isDone());
+ assertEquals(deleteFuture.get(), null);
+ }
+
+ @Test
+ public void testFailedCombineDeleteAttempts() {
+ Exception cause = new Exception("Fail");
+ List deleteStateFutures = Lists.newArrayList(Futures.immediateFuture(null), Futures.immediateFuture(null),
+ Futures.immediateFailedFuture(cause));
+
+ ListenableFuture<Void> deleteFuture = aggregator.combineDeleteAttempts(deleteStateFutures);
+ assertTrue(deleteFuture.isDone());
+
+ try {
+ deleteFuture.get();
+ fail("Exception expected");
+ } catch(Exception e) {
+ assertSame(e.getCause(), cause);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.impl;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorContext;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import io.netty.util.concurrent.EventExecutor;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.ImmediateEventExecutor;
+import io.netty.util.concurrent.SucceededFuture;
+import java.util.Collection;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
+import org.opendaylight.controller.config.threadpool.ThreadPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.util.TopologyUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+
+public class NetconfTopologyImplTest {
+
+ private static final NodeId NODE_ID = new NodeId("testing-node");
+ private static final String TOPOLOGY_ID = "testing-topology";
+
+ @Mock
+ private Broker mockedDataBroker;
+
+ @Mock
+ private NetconfClientDispatcher mockedClientDispatcher;
+
+ @Mock
+ private BindingAwareBroker mockedBindingAwareBroker;
+
+ @Mock
+ private EventExecutor mockedEventExecutor;
+
+ @Mock
+ private ScheduledThreadPool mockedKeepaliveExecutor;
+
+ @Mock
+ private ThreadPool mockedProcessingExecutor;
+
+ @Mock
+ private SchemaRepositoryProvider mockedSchemaRepositoryProvider;
+
+
+ private TestingNetconfTopologyImpl topology;
+ private TestingNetconfTopologyImpl spyTopology;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mockedSchemaRepositoryProvider.getSharedSchemaRepository()).thenReturn(new SharedSchemaRepository("testingSharedSchemaRepo"));
+ when(mockedProcessingExecutor.getExecutor()).thenReturn(MoreExecutors.newDirectExecutorService());
+ Future future = new SucceededFuture(ImmediateEventExecutor.INSTANCE, new NetconfDeviceCapabilities());
+ when(mockedClientDispatcher.createReconnectingClient(any(NetconfReconnectingClientConfiguration.class))).thenReturn(future);
+
+ topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedBindingAwareBroker,
+ mockedDataBroker, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider);
+
+ spyTopology = spy(topology);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testRegisterMountPointNotSupported() {
+ ActorContext context = mock(ActorContext.class);
+ topology.registerMountPoint(context, NODE_ID);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testUnregisterMountPointNotSupported() {
+ topology.unregisterMountPoint(NODE_ID);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testRegisterConnectionStatusListener() {
+ RemoteDeviceHandler<NetconfSessionPreferences> listener = mock(RemoteDeviceHandler.class);
+ topology.registerConnectionStatusListener(NODE_ID, listener);
+ }
+
+ @Test
+ public void testOnSessionInitiated() {
+ BindingAwareBroker.ProviderContext session = mock(BindingAwareBroker.ProviderContext.class);
+ DataBroker dataBroker = mock(DataBroker.class);
+ when(session.getSALService(DataBroker.class)).thenReturn(dataBroker);
+ WriteTransaction wtx = mock(WriteTransaction.class);
+ when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
+ doNothing().when(wtx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
+ when(wtx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
+ topology.onSessionInitiated(session);
+
+ //verify initialization of topology
+ final InstanceIdentifier<NetworkTopology> networkTopologyId = InstanceIdentifier.builder(NetworkTopology.class).build();
+ final Topology topo = new TopologyBuilder().setTopologyId(new TopologyId(TOPOLOGY_ID)).build();
+ final NetworkTopology networkTopology = new NetworkTopologyBuilder().build();
+ verify(wtx).merge(LogicalDatastoreType.CONFIGURATION, networkTopologyId, networkTopology);
+ verify(wtx).merge(LogicalDatastoreType.OPERATIONAL, networkTopologyId, networkTopology);
+ verify(wtx).merge(LogicalDatastoreType.CONFIGURATION, networkTopologyId.child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))), topo);
+ verify(wtx).merge(LogicalDatastoreType.OPERATIONAL, networkTopologyId.child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))), topo);
+ }
+
+ @Test
+ public void testOnDataTreeChange() {
+
+ DataObjectModification<Node> newNode = mock(DataObjectModification.class);
+ when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE);
+
+ InstanceIdentifier.PathArgument pa = null;
+ for (InstanceIdentifier.PathArgument p : TopologyUtil.createTopologyId(TOPOLOGY_ID).child(Node.class, new NodeKey(NODE_ID)).getPathArguments()) {
+ pa = p;
+ }
+
+ when(newNode.getIdentifier()).thenReturn(pa);
+
+
+ NetconfNode testingNode = new NetconfNodeBuilder()
+ .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
+ .setPort(new PortNumber(9999))
+ .setReconnectOnChangedSchema(true)
+ .setDefaultRequestTimeoutMillis(1000L)
+ .setBetweenAttemptsTimeoutMillis(100)
+ .setKeepaliveDelay(1000L)
+ .setTcpOnly(true)
+ .setCredentials(new LoginPasswordBuilder().setUsername("testuser").setPassword("testpassword").build())
+ .build();
+
+ NodeBuilder nn = new NodeBuilder().addAugmentation(NetconfNode.class, testingNode);
+
+ when(newNode.getDataAfter()).thenReturn(nn.build());
+
+
+
+ Collection<DataTreeModification<Node>> changes = Sets.newHashSet();
+ DataTreeModification<Node> ch = mock(DataTreeModification.class);
+ when(ch.getRootNode()).thenReturn(newNode);
+ changes.add(ch);
+ spyTopology.onDataTreeChanged(changes);
+ verify(spyTopology).connectNode(TopologyUtil.getNodeId(pa), nn.build());
+
+ when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
+ spyTopology.onDataTreeChanged(changes);
+ verify(spyTopology).disconnectNode(TopologyUtil.getNodeId(pa));
+
+ when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED);
+ spyTopology.onDataTreeChanged(changes);
+
+ //one in previous creating and deleting node and one in updating
+ verify(spyTopology, times(2)).disconnectNode(TopologyUtil.getNodeId(pa));
+ verify(spyTopology, times(2)).connectNode(TopologyUtil.getNodeId(pa), nn.build());
+
+
+ }
+
+ public static class TestingNetconfTopologyImpl extends NetconfTopologyImpl {
+
+ public TestingNetconfTopologyImpl(String topologyId, NetconfClientDispatcher clientDispatcher, BindingAwareBroker bindingAwareBroker, Broker domBroker, EventExecutor eventExecutor, ScheduledThreadPool keepaliveExecutor, ThreadPool processingExecutor, SchemaRepositoryProvider schemaRepositoryProvider) {
+ super(topologyId, clientDispatcher, bindingAwareBroker, domBroker, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider);
+ }
+
+ @Override
+ public ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode) {
+ return Futures.immediateFuture(new NetconfDeviceCapabilities());
+ }
+
+ @Override
+ public ListenableFuture<Void> disconnectNode(NodeId nodeId) {
+ return Futures.immediateFuture(null);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.pipeline;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.net.InetSocketAddress;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.netconf.api.NetconfTerminationReason;
+import org.opendaylight.netconf.client.NetconfClientSession;
+import org.opendaylight.netconf.client.NetconfClientSessionListener;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+
+public class ClusteredNetconfDeviceCommunicatorTest {
+
+ private static final RemoteDeviceId REMOTE_DEVICE_ID = new RemoteDeviceId("testing-device", new InetSocketAddress(9999));
+
+ @Mock
+ private ClusteredNetconfDevice remoteDevice;
+
+ @Mock
+ private EntityOwnershipService ownershipService;
+
+ @Mock
+ private NetconfClientSession session;
+
+ @Mock
+ private NetconfClientSessionListener listener1;
+
+ @Mock
+ private NetconfClientSessionListener listener2;
+
+ @Mock
+ private EntityOwnershipListenerRegistration ownershipListenerRegistration;
+
+ private ClusteredNetconfDeviceCommunicator communicator;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(ownershipListenerRegistration).when(ownershipService).registerListener(
+ "netconf-node/" + REMOTE_DEVICE_ID.getName(), remoteDevice);
+
+ communicator = new ClusteredNetconfDeviceCommunicator(REMOTE_DEVICE_ID, remoteDevice, ownershipService);
+ communicator.registerNetconfClientSessionListener(listener1);
+ communicator.registerNetconfClientSessionListener(listener2);
+ }
+
+ @Test
+ public void testOnSessionDown() {
+ communicator.onSessionUp(session);
+
+ Exception exception = mock(Exception.class);
+ communicator.onSessionDown(session, exception);
+
+ verify(ownershipListenerRegistration).close();
+
+ verify(listener1).onSessionDown(eq(session), eq(exception));
+ verify(listener2).onSessionDown(eq(session), eq(exception));
+ }
+
+ @Test
+ public void testOnSessionUp() {
+ communicator.onSessionUp(session);
+
+ verify(ownershipService).registerListener("netconf-node/" + REMOTE_DEVICE_ID.getName(), remoteDevice);
+
+ verify(listener1).onSessionUp(eq(session));
+ verify(listener2).onSessionUp(eq(session));
+ }
+
+ @Test
+ public void testOnSessionTerminated() {
+ communicator.onSessionUp(session);
+
+ NetconfTerminationReason reason = mock(NetconfTerminationReason.class);
+ communicator.onSessionTerminated(session, reason);
+
+ verify(ownershipListenerRegistration).close();
+
+ verify(listener1).onSessionTerminated(eq(session), eq(reason));
+ verify(listener2).onSessionTerminated(eq(session), eq(reason));
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.pipeline;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class TopologyMountPointFacadeTest {
+
+ private static final RemoteDeviceId REMOTE_DEVICE_ID = new RemoteDeviceId("testing-device", new InetSocketAddress(9999));
+ private static final String TOPOLOGY_ID = "testing-topology";
+
+ @Mock
+ Broker domBroker;
+
+ @Mock
+ BindingAwareBroker bindingBroker;
+
+ @Mock
+ RemoteDeviceHandler<NetconfSessionPreferences> connectionStatusListener1;
+
+ @Mock
+ RemoteDeviceHandler<NetconfSessionPreferences> connectionStatusListener2;
+
+
+ private TopologyMountPointFacade mountPointFacade;
+
+ @Before
+ public void setUp() {
+
+ MockitoAnnotations.initMocks(this);
+
+ mountPointFacade = new TopologyMountPointFacade(TOPOLOGY_ID, REMOTE_DEVICE_ID, domBroker, bindingBroker, 1L);
+
+ mountPointFacade.registerConnectionStatusListener(connectionStatusListener1);
+ mountPointFacade.registerConnectionStatusListener(connectionStatusListener2);
+
+ }
+
+ @Test
+ public void testOnDeviceConnected() {
+ SchemaContext mockedContext = Mockito.mock(SchemaContext.class);
+ NetconfSessionPreferences mockedPreferences = NetconfSessionPreferences.fromStrings(Collections.<String>emptyList());
+ DOMRpcService mockedRpcService = Mockito.mock(DOMRpcService.class);
+ mountPointFacade.onDeviceConnected(mockedContext, mockedPreferences, mockedRpcService);
+
+ Mockito.verify(connectionStatusListener1).onDeviceConnected(mockedContext, mockedPreferences, mockedRpcService);
+ Mockito.verify(connectionStatusListener2).onDeviceConnected(mockedContext, mockedPreferences, mockedRpcService);
+ }
+
+ @Test
+ public void testOnDeviceDisconnected() {
+ mountPointFacade.onDeviceDisconnected();
+
+ Mockito.verify(connectionStatusListener1).onDeviceDisconnected();
+ Mockito.verify(connectionStatusListener2).onDeviceDisconnected();
+ }
+
+ @Test
+ public void testOnDeviceFailed() {
+ Throwable mockedException = Mockito.mock(Throwable.class);
+ mountPointFacade.onDeviceFailed(mockedException);
+
+ Mockito.verify(connectionStatusListener1).onDeviceFailed(mockedException);
+ Mockito.verify(connectionStatusListener2).onDeviceFailed(mockedException);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.pipeline.tx;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorSystem;
+import akka.dispatch.ExecutionContexts;
+import akka.dispatch.Futures;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.net.InetSocketAddress;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.netconf.topology.pipeline.ProxyNetconfDeviceDataBroker;
+import org.opendaylight.netconf.topology.util.messages.NormalizedNodeMessage;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ProxyReadOnlyTransactionTest {
+ private static final RemoteDeviceId REMOTE_DEVICE_ID = new RemoteDeviceId("testing-device", new InetSocketAddress(9999));
+ private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
+
+ @Mock
+ private ProxyNetconfDeviceDataBroker mockedProxyDataBroker;
+
+ @Mock
+ private ActorSystem mockedActorSystem;
+
+ @Mock
+ private NormalizedNodeMessage mockedNodeMessage;
+
+ @Mock
+ private NormalizedNode mockedNode;
+
+ private ProxyReadOnlyTransaction proxyReadOnlyTx;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mockedActorSystem.dispatcher()).thenReturn(ExecutionContexts.fromExecutorService(MoreExecutors.newDirectExecutorService()));
+ when(mockedNodeMessage.getNode()).thenReturn(mockedNode);
+
+ proxyReadOnlyTx = new ProxyReadOnlyTransaction(mockedActorSystem, REMOTE_DEVICE_ID, mockedProxyDataBroker);
+ }
+
+ @Test
+ public void testSuccessfulRead() throws ReadFailedException {
+ when(mockedProxyDataBroker.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+ .thenReturn(Futures.successful(Optional.of(mockedNodeMessage)));
+ CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readResultFuture = proxyReadOnlyTx.read(LogicalDatastoreType.CONFIGURATION, path);
+ verify(mockedProxyDataBroker).read(eq(LogicalDatastoreType.CONFIGURATION), eq(path));
+ assertTrue(readResultFuture.isDone());
+ assertEquals(readResultFuture.checkedGet().get(), mockedNode);
+ }
+
+ @Test
+ public void testFailedRead() {
+ when(mockedProxyDataBroker.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+ .thenReturn(Futures.<Optional<NormalizedNodeMessage>>failed(new ReadFailedException("Test read failed!")));
+ CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readResultFuture = proxyReadOnlyTx.read(LogicalDatastoreType.CONFIGURATION, path);
+ verify(mockedProxyDataBroker).read(eq(LogicalDatastoreType.CONFIGURATION), eq(path));
+ assertTrue(readResultFuture.isDone());
+ try {
+ readResultFuture.checkedGet();
+ fail("Exception expected");
+ } catch(Exception e) {
+ assertTrue(e instanceof ReadFailedException);
+ }
+ }
+
+
+ @Test
+ public void testFailedExists() {
+ when(mockedProxyDataBroker.exists(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+ .thenReturn(Futures.<Boolean>failed(new ReadFailedException("Test read failed!")));
+ CheckedFuture existsFuture = proxyReadOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path);
+ verify(mockedProxyDataBroker).exists(eq(LogicalDatastoreType.OPERATIONAL), eq(path));
+ assertTrue(existsFuture.isDone());
+ try {
+ existsFuture.checkedGet();
+ fail("Exception expected");
+ } catch(Exception e) {
+ assertTrue(e instanceof ReadFailedException);
+ }
+ }
+
+ @Test
+ public void testExists() throws Exception {
+ when(mockedProxyDataBroker.exists(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)))
+ .thenReturn(Futures.successful(true));
+ CheckedFuture<Boolean, ReadFailedException> existsFuture = proxyReadOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path);
+ verify(mockedProxyDataBroker).exists(eq(LogicalDatastoreType.OPERATIONAL), eq(path));
+ assertTrue(existsFuture.isDone());
+ assertTrue(existsFuture.checkedGet());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.netconf.topology.pipeline.tx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import akka.actor.ActorSystem;
+import akka.dispatch.ExecutionContexts;
+import akka.dispatch.Futures;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.netconf.topology.pipeline.ProxyNetconfDeviceDataBroker;
+import org.opendaylight.netconf.topology.util.messages.NormalizedNodeMessage;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ProxyWriteOnlyTransactionTest {
+ private static final YangInstanceIdentifier path = YangInstanceIdentifier.create();
+ private ArgumentCaptor<NormalizedNodeMessage> nodeMessageArgumentCaptor;
+
+ @Mock
+ private ProxyNetconfDeviceDataBroker mockedDelegate;
+
+ @Mock
+ private ActorSystem mockedActorSystem;
+
+ @Mock
+ private NormalizedNode<?, ?> normalizedNode;
+
+ private ProxyWriteOnlyTransaction tx;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mockedActorSystem.dispatcher()).thenReturn(ExecutionContexts.fromExecutorService(MoreExecutors.newDirectExecutorService()));
+
+ nodeMessageArgumentCaptor = ArgumentCaptor.forClass(NormalizedNodeMessage.class);
+ tx = new ProxyWriteOnlyTransaction(mockedActorSystem, mockedDelegate);
+ }
+
+ @Test
+ public void testPut() {
+ doNothing().when(mockedDelegate).put(any(LogicalDatastoreType.class), any(NormalizedNodeMessage.class));
+ tx.put(LogicalDatastoreType.OPERATIONAL, path, normalizedNode);
+ verify(mockedDelegate).put(eq(LogicalDatastoreType.OPERATIONAL), nodeMessageArgumentCaptor.capture());
+ assertEquals(path, nodeMessageArgumentCaptor.getValue().getIdentifier());
+ assertEquals(normalizedNode, nodeMessageArgumentCaptor.getValue().getNode());
+ }
+
+ @Test
+ public void testMerge() {
+ doNothing().when(mockedDelegate).merge(any(LogicalDatastoreType.class), any(NormalizedNodeMessage.class));
+ tx.merge(LogicalDatastoreType.CONFIGURATION, path, normalizedNode);
+ verify(mockedDelegate).merge(eq(LogicalDatastoreType.CONFIGURATION), nodeMessageArgumentCaptor.capture());
+ assertEquals(path, nodeMessageArgumentCaptor.getValue().getIdentifier());
+ assertEquals(normalizedNode, nodeMessageArgumentCaptor.getValue().getNode());
+ }
+
+ @Test
+ public void testCancel() {
+ when(mockedDelegate.cancel()).thenReturn(true);
+ assertTrue(tx.cancel());
+ verify(mockedDelegate).cancel();
+ }
+
+ @Test
+ public void testDelete() {
+ doNothing().when(mockedDelegate).delete(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class));
+ tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+ verify(mockedDelegate).delete(eq(LogicalDatastoreType.OPERATIONAL), eq(path));
+ }
+
+ @Test
+ public void testSuccessfulSubmit() throws Exception {
+ when(mockedDelegate.submit()).thenReturn(Futures.<Void>successful(null));
+ CheckedFuture submitFuture = tx.submit();
+ verify(mockedDelegate).submit();
+ assertTrue(submitFuture.isDone());
+ assertEquals(submitFuture.checkedGet(), null);
+ }
+
+ @Test
+ public void testFailedSubmit() {
+ when(mockedDelegate.submit()).thenReturn(Futures.<Void>failed(new TransactionCommitFailedException("fail")));
+ CheckedFuture submitFuture = tx.submit();
+ verify(mockedDelegate).submit();
+ assertTrue(submitFuture.isDone());
+ try {
+ submitFuture.checkedGet();
+ fail("Exception expected");
+ } catch(Exception e) {
+ assertTrue(e instanceof TransactionCommitFailedException);
+ }
+ }
+
+ @Test
+ public void testSuccessfulCommit() throws ExecutionException, InterruptedException {
+ RpcResult<TransactionStatus> rpcResult = mock(RpcResult.class);
+ when(mockedDelegate.commit()).thenReturn(Futures.successful(rpcResult));
+ ListenableFuture<RpcResult<TransactionStatus>> submitFuture = tx.commit();
+ verify(mockedDelegate).commit();
+ assertTrue(submitFuture.isDone());
+ assertEquals(submitFuture.get(), rpcResult);
+ }
+
+ @Test
+ public void testFailedCommit() {
+ when(mockedDelegate.commit()).thenReturn(Futures.<RpcResult<TransactionStatus>>failed(new TransactionCommitFailedException("faile")));
+ ListenableFuture<RpcResult<TransactionStatus>> submitFuture = tx.commit();
+ verify(mockedDelegate).commit();
+ assertTrue(submitFuture.isDone());
+ try {
+ submitFuture.get();
+ fail("Exception expected");
+ } catch(Exception e) {
+ assertTrue(e.getCause() instanceof TransactionCommitFailedException);
+ }
+ }
+}
\ No newline at end of file