import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
import org.opendaylight.netconf.nettyutil.ReconnectFuture;
import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
-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.network.topology.topology.Node;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
topology.disconnectNode(nodeId);
});
if (deviceContext != null) {
- final NodeId nodeId = deviceContext.getId();
final Node configNode = deviceContext.getConfigNode();
LOG.info("Provisioning fake config {}", configNode);
- topology.connectNode(nodeId, configNode);
+ topology.connectNode(configNode);
}
}
*/
package org.opendaylight.netconf.callhome.mount;
+import com.google.common.annotations.VisibleForTesting;
import io.netty.util.concurrent.EventExecutor;
import org.opendaylight.aaa.encrypt.AAAEncryptionService;
import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
+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.network.topology.topology.Node;
// Non-final for mocking
public class CallHomeTopology extends AbstractNetconfTopology {
schemaRepositoryProvider, dataBroker, mountPointService, encryptionService, deviceActionFactory,
baseSchemas, credentialProvider, sslHandlerFactoryProvider);
}
+
+ void disconnectNode(final NodeId nodeId) {
+ deleteNode(nodeId);
+ }
+
+ @VisibleForTesting
+ public void connectNode(final Node node) {
+ ensureNode(node);
+ }
}
// when
instance.onNetconfSubsystemOpened(mockProtoSess, activator);
// then
- verify(instance.topology, times(1)).connectNode(any(NodeId.class), any(Node.class));
+ verify(instance.topology, times(1)).connectNode(any(Node.class));
}
}
import org.opendaylight.netconf.client.mdsal.api.SchemaResourceManager;
import org.opendaylight.netconf.client.mdsal.api.SslHandlerFactoryProvider;
import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
-import org.opendaylight.netconf.topology.spi.NetconfConnectorDTO;
import org.opendaylight.netconf.topology.spi.NetconfNodeUtils;
import org.opendaylight.netconf.topology.spi.NetconfTopologyRPCProvider;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNodeTopologyService;
}
// close all existing connectors, delete whole topology in datastore?
- for (final NetconfConnectorDTO connectorDTO : activeConnectors.values()) {
- connectorDTO.close();
- }
- activeConnectors.clear();
+ deleteAllNodes();
if (dtclReg != null) {
dtclReg.close();
final DataObjectModification<Node> rootNode = change.getRootNode();
final NodeId nodeId;
switch (rootNode.getModificationType()) {
- case SUBTREE_MODIFIED:
- nodeId = getNodeId(rootNode.getIdentifier());
- LOG.debug("Config for node {} updated", nodeId);
- disconnectNode(nodeId);
- connectNode(nodeId, rootNode.getDataAfter());
- break;
- case WRITE:
- nodeId = getNodeId(rootNode.getIdentifier());
- LOG.debug("Config for node {} created", nodeId);
- if (activeConnectors.containsKey(nodeId)) {
- LOG.warn("RemoteDevice{{}} was already configured, reconfiguring..", nodeId);
- disconnectNode(nodeId);
- }
- connectNode(nodeId, rootNode.getDataAfter());
- break;
- case DELETE:
+ case SUBTREE_MODIFIED -> {
+ LOG.debug("Config for node {} updated", getNodeId(rootNode.getIdentifier()));
+ ensureNode(rootNode.getDataAfter());
+ }
+ case WRITE -> {
+ LOG.debug("Config for node {} created", getNodeId(rootNode.getIdentifier()));
+ ensureNode(rootNode.getDataAfter());
+ }
+ case DELETE -> {
nodeId = getNodeId(rootNode.getIdentifier());
LOG.debug("Config for node {} deleted", nodeId);
- disconnectNode(nodeId);
- break;
- default:
- LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType());
+ deleteNode(nodeId);
+ }
+ default -> LOG.debug("Unsupported modification type: {}.", rootNode.getModificationType());
}
}
}
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-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 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.common.Uint16;
import org.opendaylight.yangtools.yang.common.Uint32;
import org.opendaylight.yangtools.yang.parser.api.YangParserException;
doReturn(newNode).when(ch).getRootNode();
changes.add(ch);
spyTopology.onDataTreeChanged(changes);
- verify(spyTopology).connectNode(NetconfTopologyImpl.getNodeId(pa), nn.build());
+ verify(spyTopology).ensureNode(nn.build());
doReturn(DataObjectModification.ModificationType.DELETE).when(newNode).getModificationType();
spyTopology.onDataTreeChanged(changes);
- verify(spyTopology).disconnectNode(NetconfTopologyImpl.getNodeId(pa));
+ verify(spyTopology).deleteNode(NetconfTopologyImpl.getNodeId(pa));
doReturn(DataObjectModification.ModificationType.SUBTREE_MODIFIED).when(newNode).getModificationType();
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());
+ // one in previous creating and deleting node and one in updating
+ verify(spyTopology, times(2)).ensureNode(nn.build());
}
@Test
}
@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
}
}
final ServiceGroupIdentifier serviceGroupIdent, final NetconfTopologySetup setup,
final CredentialProvider credentialProvider, final SslHandlerFactoryProvider sslHandlerFactoryProvider) {
this.serviceGroupIdent = requireNonNull(serviceGroupIdent);
- this.remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(setup.getNode().getNodeId(),
+ remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(setup.getNode().getNodeId(),
setup.getNode().augmentation(NetconfNode.class));
- this.topologySingleton = new NetconfTopologySingletonImpl(topologyId, clientDispatcher,
+ topologySingleton = new NetconfTopologySingletonImpl(topologyId, clientDispatcher,
eventExecutor, keepaliveExecutor, processingExecutor, schemaManager, dataBroker, mountPointService,
encryptionService, deviceActionFactory, baseSchemas, remoteDeviceId, setup, actorResponseWaitTime,
credentialProvider, sslHandlerFactoryProvider);
remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(node.getNodeId(), node.augmentation(NetconfNode.class));
if (isMaster) {
- getTopologySingleton().disconnectNode(setup.getNode().getNodeId());
+ getTopologySingleton().dropNode(setup.getNode().getNodeId());
getTopologySingleton().refreshSetupConnection(setup, remoteDeviceId);
} else {
getTopologySingleton().refreshDevice(setup, remoteDeviceId);
import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData;
import org.opendaylight.netconf.topology.spi.AbstractNetconfTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
remoteDeviceId.name(), masterAddress));
// setup connection to device
- connectNode(setup.getNode().getNodeId(), setup.getNode());
+ ensureNode(setup.getNode());
}
void becomeTopologyFollower() {
registerNodeManager();
// disconnect device from this node and listen for changes from leader
- disconnectNode(setup.getNode().getNodeId());
+ deleteNode(setup.getNode().getNodeId());
if (masterActorRef != null) {
// was leader before
setup.getActorSystem().stop(masterActorRef);
netconfNodeManager.close();
}
+ void dropNode(final NodeId nodeId) {
+ deleteNode(nodeId);
+ }
+
@Override
public void close() {
unregisterNodeManager();
+++ /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.api;
-
-import com.google.common.util.concurrent.ListenableFuture;
-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.network.topology.topology.Node;
-import org.opendaylight.yangtools.yang.common.Empty;
-
-public interface NetconfTopology {
-
- ListenableFuture<Empty> connectNode(NodeId nodeId, Node configNode);
-
- ListenableFuture<Empty> disconnectNode(NodeId nodeId);
-}
import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
-import org.opendaylight.netconf.topology.api.NetconfTopology;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
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.credentials.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public abstract class AbstractNetconfTopology implements NetconfTopology {
+public abstract class AbstractNetconfTopology {
private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
+ private final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
private final NetconfClientDispatcher clientDispatcher;
private final EventExecutor eventExecutor;
private final DeviceActionFactory deviceActionFactory;
protected final DOMMountPointService mountPointService;
protected final String topologyId;
protected final AAAEncryptionService encryptionService;
- protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
LOG.debug("Topology {} initialized", topologyId);
}
- @Override
- public ListenableFuture<Empty> connectNode(final NodeId nodeId, final Node configNode) {
- LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode));
- return setupConnection(nodeId, configNode);
+ // Non-final for testing
+ protected void ensureNode(final Node node) {
+ lockedEnsureNode(node);
}
- /**
- * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
- *
- * @param nodeConfiguration Node configuration container.
- * @return String representation of node configuration with credentials replaced by asterisks.
- */
- @VisibleForTesting
- public static String hideCredentials(final Node nodeConfiguration) {
- final NetconfNode netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
- final String nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
- final String nodeConfigurationString = nodeConfiguration.toString();
- return nodeConfigurationString.replace(nodeCredentials, "***");
+ private synchronized void lockedEnsureNode(final Node node) {
+ final var nodeId = node.requireNodeId();
+ final var prev = activeConnectors.remove(nodeId);
+ if (prev != null) {
+ LOG.info("RemoteDevice{{}} was already configured, disconnecting", nodeId);
+ prev.close();
+ }
+
+ LOG.info("Connecting RemoteDevice{{}}, with config {}", nodeId, hideCredentials(node));
+ setupConnection(nodeId, node);
+ }
+
+ // Non-final for testing
+ protected void deleteNode(final NodeId nodeId) {
+ lockedDeleteNode(nodeId);
}
- @Override
- public ListenableFuture<Empty> disconnectNode(final NodeId nodeId) {
+ private synchronized void lockedDeleteNode(final NodeId nodeId) {
final var nodeName = nodeId.getValue();
LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
- final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
- if (connectorDTO == null) {
- return Futures.immediateFailedFuture(
- new IllegalStateException("Cannot disconnect " + nodeName + " as it is not connected"));
+ final var connectorDTO = activeConnectors.remove(nodeId);
+ if (connectorDTO != null) {
+ connectorDTO.close();
}
+ }
- connectorDTO.close();
- return Futures.immediateFuture(Empty.value());
+ protected final synchronized void deleteAllNodes() {
+ activeConnectors.values().forEach(NetconfConnectorDTO::close);
+ activeConnectors.clear();
}
- protected ListenableFuture<Empty> setupConnection(final NodeId nodeId, final Node configNode) {
+ protected final void setupConnection(final NodeId nodeId, final Node configNode) {
final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
// remove this node from active connectors?
}
}, MoreExecutors.directExecutor());
-
- return future;
}
protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations);
}
- protected RemoteDeviceHandler createSalFacade(RemoteDeviceId deviceId, boolean lockDatastore) {
+ protected RemoteDeviceHandler createSalFacade(final RemoteDeviceId deviceId, final boolean lockDatastore) {
return new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, lockDatastore, dataBroker);
}
}
throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
}
+
+ /**
+ * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
+ *
+ * @param nodeConfiguration Node configuration container.
+ * @return String representation of node configuration with credentials replaced by asterisks.
+ */
+ @VisibleForTesting
+ public static final String hideCredentials(final Node nodeConfiguration) {
+ final var netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
+ final var nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
+ final var nodeConfigurationString = nodeConfiguration.toString();
+ return nodeConfigurationString.replace(nodeCredentials, "***");
+ }
}
*/
package org.opendaylight.netconf.topology.spi;
+import static java.util.Objects.requireNonNull;
+
import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.netconf.client.NetconfClientSessionListener;
import org.opendaylight.netconf.client.mdsal.NetconfDeviceCommunicator;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
-public final class NetconfConnectorDTO implements AutoCloseable {
- private final List<SchemaSourceRegistration<?>> yanglibRegistrations;
- private final NetconfDeviceCommunicator communicator;
- private final RemoteDeviceHandler facade;
+final class NetconfConnectorDTO implements AutoCloseable {
+ private final @NonNull List<SchemaSourceRegistration<?>> yanglibRegistrations;
+ private final @NonNull NetconfDeviceCommunicator communicator;
+ private final @NonNull RemoteDeviceHandler facade;
- public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler facade,
+ NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler facade,
final List<SchemaSourceRegistration<?>> yanglibRegistrations) {
- this.communicator = communicator;
- this.facade = facade;
- this.yanglibRegistrations = yanglibRegistrations;
+ this.communicator = requireNonNull(communicator);
+ this.facade = requireNonNull(facade);
+ this.yanglibRegistrations = List.copyOf(yanglibRegistrations);
}
- public NetconfDeviceCommunicator getCommunicator() {
+ NetconfDeviceCommunicator getCommunicator() {
return communicator;
}
- public RemoteDeviceHandler getFacade() {
- return facade;
- }
-
- public NetconfClientSessionListener getSessionListener() {
+ NetconfClientSessionListener getSessionListener() {
return communicator;
}