*/
package org.opendaylight.netconf.topology.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
-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 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 java.util.Collection;
-import java.util.HashSet;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import io.netty.util.Timer;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.opendaylight.aaa.encrypt.AAAEncryptionService;
-import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
-import org.opendaylight.controller.config.threadpool.ThreadPool;
import org.opendaylight.mdsal.binding.api.DataBroker;
import org.opendaylight.mdsal.binding.api.DataObjectModification;
+import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
import org.opendaylight.mdsal.binding.api.DataTreeModification;
import org.opendaylight.mdsal.binding.api.RpcProviderService;
import org.opendaylight.mdsal.binding.api.WriteTransaction;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMMountPointService;
-import org.opendaylight.netconf.client.NetconfClientDispatcher;
-import org.opendaylight.netconf.client.NetconfClientSessionListener;
-import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
-import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
+import org.opendaylight.netconf.client.NetconfClientFactory;
import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchemas;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.impl.DefaultBaseNetconfSchemas;
-import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
+import org.opendaylight.netconf.topology.spi.NetconfClientConfigurationBuilderFactory;
+import org.opendaylight.netconf.topology.spi.NetconfTopologySchemaAssembler;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.connection.parameters.Protocol.Name;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.connection.parameters.ProtocolBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev230430.credentials.credentials.LoginPasswordBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.credentials.credentials.LoginPwUnencryptedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencryptedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev231121.NetconfNodeBuilder;
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.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.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.common.Decimal64;
-import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.opendaylight.yangtools.yang.common.Uint16;
import org.opendaylight.yangtools.yang.common.Uint32;
-import org.opendaylight.yangtools.yang.parser.api.YangParserException;
import org.opendaylight.yangtools.yang.parser.impl.DefaultYangParserFactory;
-@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class NetconfTopologyImplTest {
- private static final NodeId NODE_ID = new NodeId("testing-node");
- private static final String TOPOLOGY_ID = "testing-topology";
+@ExtendWith(MockitoExtension.class)
+class NetconfTopologyImplTest {
+ private static final TopologyKey TOPOLOGY_KEY = new TopologyKey(new TopologyId("testing-topology"));
+ private static final KeyedInstanceIdentifier<Topology, TopologyKey> TOPOLOGY_PATH =
+ InstanceIdentifier.builder(NetworkTopology.class).child(Topology.class, TOPOLOGY_KEY).build();
@Mock
- private NetconfClientDispatcher mockedClientDispatcher;
+ private NetconfClientFactory mockedClientFactory;
@Mock
- private EventExecutor mockedEventExecutor;
- @Mock
- private ScheduledThreadPool mockedKeepaliveExecutor;
- @Mock
- private ThreadPool mockedProcessingExecutor;
+ private Timer mockedTimer;
@Mock
private SchemaResourceManager mockedResourceManager;
@Mock
@Mock
private RpcProviderService rpcProviderService;
@Mock
+ private NetconfClientConfigurationBuilderFactory builderFactory;
+ @Mock
private WriteTransaction wtx;
+ @Mock
+ private DataObjectModification<Node> objMod;
+ @Mock
+ private DataTreeModification<Node> treeMod;
- private TestingNetconfTopologyImpl topology;
- private TestingNetconfTopologyImpl spyTopology;
-
- @Before
- public void setUp() {
- doReturn(MoreExecutors.newDirectExecutorService()).when(mockedProcessingExecutor).getExecutor();
+ @Test
+ void testOnDataTreeChange() throws Exception {
doReturn(wtx).when(dataBroker).newWriteOnlyTransaction();
doReturn(CommitInfo.emptyFluentFuture()).when(wtx).commit();
- topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedEventExecutor,
- mockedKeepaliveExecutor, mockedProcessingExecutor, mockedResourceManager, dataBroker, mountPointService,
- encryptionService, rpcProviderService);
- //verify initialization of topology
- verify(wtx).merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class)
- .child(Topology.class, new TopologyKey(new TopologyId(TOPOLOGY_ID))).build(),
- new TopologyBuilder().setTopologyId(new TopologyId(TOPOLOGY_ID)).build());
-
- spyTopology = spy(topology);
- }
-
- @Test
- public void testOnDataTreeChange() {
- final DataObjectModification<Node> newNode = mock(DataObjectModification.class);
- when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.WRITE);
+ try (var schemaAssembler = new NetconfTopologySchemaAssembler(1, 1, 0, TimeUnit.SECONDS)) {
+ final var topology = new TestingNetconfTopologyImpl(TOPOLOGY_KEY.getTopologyId().getValue(),
+ mockedClientFactory, mockedTimer, schemaAssembler, mockedResourceManager, dataBroker, mountPointService,
+ encryptionService, builderFactory, rpcProviderService,
+ new DefaultBaseNetconfSchemas(new DefaultYangParserFactory()));
+ //verify initialization of topology
+ verify(wtx).merge(LogicalDatastoreType.OPERATIONAL, TOPOLOGY_PATH,
+ new TopologyBuilder().withKey(TOPOLOGY_KEY).build());
- NodeKey key = new NodeKey(NODE_ID);
- PathArgument pa = IdentifiableItem.of(Node.class, key);
- when(newNode.getIdentifier()).thenReturn(pa);
+ final var spyTopology = spy(topology);
- final NodeBuilder nn = new NodeBuilder()
+ final var key = new NodeKey(new NodeId("testing-node"));
+ final var node = new NodeBuilder()
.withKey(key)
.addAugmentation(new NetconfNodeBuilder()
+ .setLockDatastore(true)
.setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
.setPort(new PortNumber(Uint16.valueOf(9999)))
.setReconnectOnChangedSchema(true)
.setDefaultRequestTimeoutMillis(Uint32.valueOf(1000))
- .setBetweenAttemptsTimeoutMillis(Uint16.valueOf(100))
+ .setMinBackoffMillis(Uint16.valueOf(100))
.setKeepaliveDelay(Uint32.valueOf(1000))
.setTcpOnly(true)
- .setCredentials(new LoginPasswordBuilder()
- .setUsername("testuser")
- .setPassword("testpassword")
+ .setCredentials(new LoginPwUnencryptedBuilder()
+ .setLoginPasswordUnencrypted(new LoginPasswordUnencryptedBuilder()
+ .setUsername("testuser")
+ .setPassword("testpassword")
+ .build())
.build())
- .build());
-
- when(newNode.getDataAfter()).thenReturn(nn.build());
-
- final Collection<DataTreeModification<Node>> changes = new HashSet<>();
- final DataTreeModification<Node> ch = mock(DataTreeModification.class);
- when(ch.getRootNode()).thenReturn(newNode);
- changes.add(ch);
- spyTopology.onDataTreeChanged(changes);
- verify(spyTopology).connectNode(NetconfTopologyImpl.getNodeId(pa), nn.build());
-
- when(newNode.getModificationType()).thenReturn(DataObjectModification.ModificationType.DELETE);
- spyTopology.onDataTreeChanged(changes);
- verify(spyTopology).disconnectNode(NetconfTopologyImpl.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(NetconfTopologyImpl.getNodeId(pa));
- verify(spyTopology, times(2)).connectNode(NetconfTopologyImpl.getNodeId(pa), nn.build());
- }
-
- @Test
- public void testGetClientConfig() {
- final NetconfClientSessionListener sessionListener = mock(NetconfClientSessionListener.class);
- final NetconfNodeBuilder nodeBuilder = new NetconfNodeBuilder()
- .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
- .setPort(new PortNumber(Uint16.valueOf(9999)))
- .setReconnectOnChangedSchema(true)
- .setDefaultRequestTimeoutMillis(Uint32.valueOf(1000))
- .setBetweenAttemptsTimeoutMillis(Uint16.valueOf(100))
- .setKeepaliveDelay(Uint32.valueOf(1000))
- .setCredentials(new LoginPasswordBuilder().setUsername("testuser").setPassword("testpassword").build())
- .setMaxConnectionAttempts(Uint32.ZERO)
- .setSleepFactor(Decimal64.valueOf("1.5"))
- .setConnectionTimeoutMillis(Uint32.valueOf(20000));
+ .build())
+ .build();
- final NetconfReconnectingClientConfiguration configuration =
- spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(true).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TCP, configuration.getProtocol());
- assertNotNull(configuration.getAuthHandler());
- assertNull(configuration.getSslHandlerFactory());
+ doReturn(DataObjectModification.ModificationType.WRITE).when(objMod).getModificationType();
+ doReturn(node).when(objMod).getDataAfter();
- final NetconfReconnectingClientConfiguration configuration2 =
- spyTopology.getClientConfig(sessionListener, nodeBuilder.setTcpOnly(false).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, configuration2.getProtocol());
- assertNotNull(configuration2.getAuthHandler());
- assertNull(configuration2.getSslHandlerFactory());
+ doReturn(DataTreeIdentifier.create(LogicalDatastoreType.CONFIGURATION,
+ TOPOLOGY_PATH.child(Node.class, key))).when(treeMod).getRootPath();
+ final var changes = List.of(treeMod);
- final NetconfReconnectingClientConfiguration configuration3 =
- spyTopology.getClientConfig(sessionListener, nodeBuilder
- .setProtocol(new ProtocolBuilder().setName(Name.SSH).build()).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.SSH, configuration3.getProtocol());
- assertNotNull(configuration3.getAuthHandler());
- assertNull(configuration3.getSslHandlerFactory());
+ doReturn(objMod).when(treeMod).getRootNode();
+ spyTopology.onDataTreeChanged(changes);
+ verify(spyTopology).ensureNode(node);
- final NetconfReconnectingClientConfiguration configuration4 =
- spyTopology.getClientConfig(sessionListener, nodeBuilder
- .setProtocol(new ProtocolBuilder().setName(Name.TLS).build()).build(), NODE_ID);
- assertEquals(NetconfClientConfiguration.NetconfClientProtocol.TLS, configuration4.getProtocol());
- assertNull(configuration4.getAuthHandler());
- assertNotNull(configuration4.getSslHandlerFactory());
- }
+ doReturn(DataObjectModification.ModificationType.DELETE).when(objMod).getModificationType();
+ spyTopology.onDataTreeChanged(changes);
+ verify(spyTopology).deleteNode(key.getNodeId());
- public static class TestingNetconfTopologyImpl extends NetconfTopologyImpl {
- private static final BaseNetconfSchemas BASE_SCHEMAS;
+ doReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED).when(objMod).getModificationType();
+ spyTopology.onDataTreeChanged(changes);
- static {
- try {
- BASE_SCHEMAS = new DefaultBaseNetconfSchemas(new DefaultYangParserFactory());
- } catch (YangParserException e) {
- throw new ExceptionInInitializerError(e);
- }
+ // one in previous creating and deleting node and one in updating
+ verify(spyTopology, times(2)).ensureNode(node);
}
+ }
- public TestingNetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
- final EventExecutor eventExecutor,
- final ScheduledThreadPool keepaliveExecutor,
- final ThreadPool processingExecutor,
- final SchemaResourceManager schemaRepositoryProvider,
- final DataBroker dataBroker, final DOMMountPointService mountPointService,
- final AAAEncryptionService encryptionService,
- final RpcProviderService rpcProviderService) {
- super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor,
- processingExecutor, schemaRepositoryProvider, dataBroker,
- mountPointService, encryptionService, rpcProviderService, BASE_SCHEMAS, null);
+ private static class TestingNetconfTopologyImpl extends NetconfTopologyImpl {
+ TestingNetconfTopologyImpl(final String topologyId, final NetconfClientFactory clientFactory, final Timer timer,
+ final NetconfTopologySchemaAssembler schemaAssembler,
+ final SchemaResourceManager schemaRepositoryProvider, final DataBroker dataBroker,
+ final DOMMountPointService mountPointService, final AAAEncryptionService encryptionService,
+ final NetconfClientConfigurationBuilderFactory builderFactory,
+ final RpcProviderService rpcProviderService, final BaseNetconfSchemas baseSchemas) {
+ super(topologyId, clientFactory, timer, schemaAssembler, schemaRepositoryProvider,
+ dataBroker, mountPointService, encryptionService, builderFactory, rpcProviderService, baseSchemas);
}
@Override
- public ListenableFuture<Empty> connectNode(final NodeId nodeId, final Node configNode) {
- return Futures.immediateFuture(Empty.value());
+ public void ensureNode(final Node configNode) {
+ // No-op
}
@Override
- public ListenableFuture<Empty> disconnectNode(final NodeId nodeId) {
- return Futures.immediateFuture(Empty.value());
+ public void deleteNode(final NodeId nodeId) {
+ // No-op
}
}
-
- @Test
- public void hideCredentialsTest() {
- final String userName = "admin";
- final String password = "pa$$word";
- final Node node = new NodeBuilder()
- .addAugmentation(new NetconfNodeBuilder()
- .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
- .setPort(new PortNumber(Uint16.valueOf(9999)))
- .setReconnectOnChangedSchema(true)
- .setDefaultRequestTimeoutMillis(Uint32.valueOf(1000))
- .setBetweenAttemptsTimeoutMillis(Uint16.valueOf(100))
- .setKeepaliveDelay(Uint32.valueOf(1000))
- .setTcpOnly(false)
- .setProtocol(new ProtocolBuilder().setName(Name.TLS).build())
- .setCredentials(new LoginPasswordBuilder()
- .setUsername(userName)
- .setPassword(password)
- .build())
- .build())
- .setNodeId(NodeId.getDefaultInstance("junos"))
- .build();
- final String transformedNetconfNode = AbstractNetconfTopology.hideCredentials(node);
- assertTrue(transformedNetconfNode.contains("credentials=***"));
- assertFalse(transformedNetconfNode.contains(userName));
- assertFalse(transformedNetconfNode.contains(password));
- }
}