X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fnetconf-topology%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Ftopology%2Fspi%2FAbstractNetconfTopology.java;h=ed4d598b3c0c7802f07292f36925c28250bdcb5e;hb=1dba5b2651d7fc60af0263cc0640d5ebeda8e454;hp=c64c4380d4fc0f2cb42b3fb95fbd939ea02088bd;hpb=6983e9f19823b47214b64d33c28b2ff0197eecf8;p=netconf.git diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java index c64c4380d4..ed4d598b3c 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/spi/AbstractNetconfTopology.java @@ -10,7 +10,6 @@ package org.opendaylight.netconf.topology.spi; import static java.util.Objects.requireNonNull; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -23,11 +22,12 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.concurrent.ExecutionException; 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.common.api.LogicalDatastoreType; import org.opendaylight.mdsal.dom.api.DOMMountPointService; import org.opendaylight.netconf.client.NetconfClientDispatcher; import org.opendaylight.netconf.client.NetconfClientSessionListener; @@ -41,39 +41,37 @@ import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswo import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceId; import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth; -import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; -import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; -import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade; import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter; import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseNetconfSchemas; -import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl; 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.node.optional.rev190614.NetconfNodeAugmentedOptional; -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.netconf.node.connection.parameters.Protocol; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.schema.storage.YangLibrary; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.connection.parameters.Protocol.Name; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.credentials.KeyAuth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.credentials.LoginPw; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225.credentials.credentials.LoginPwUnencrypted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev221225.NetconfNodeAugmentedOptional; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev221225.NetconfNode; +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.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.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.Empty; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; @@ -109,23 +107,39 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { final AAAEncryptionService encryptionService, final DeviceActionFactory deviceActionFactory, final BaseNetconfSchemas baseSchemas) { - this.topologyId = topologyId; + this.topologyId = requireNonNull(topologyId); this.clientDispatcher = clientDispatcher; this.eventExecutor = eventExecutor; this.keepaliveExecutor = keepaliveExecutor; this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor()); this.schemaManager = requireNonNull(schemaManager); this.deviceActionFactory = deviceActionFactory; - this.dataBroker = dataBroker; + this.dataBroker = requireNonNull(dataBroker); this.mountPointService = mountPointService; this.encryptionService = encryptionService; this.baseSchemas = requireNonNull(baseSchemas); keystoreAdapter = new NetconfKeystoreAdapter(dataBroker); + + // FIXME: this should be a put(), as we are initializing and will be re-populating the datastore with all the + // devices. Whatever has been there before should be nuked to properly re-align lifecycle. + final var wtx = dataBroker.newWriteOnlyTransaction(); + wtx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class) + .child(Topology.class, new TopologyKey(new TopologyId(topologyId))) + .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build()); + final var future = wtx.commit(); + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("Unable to initialize topology {}", topologyId, e); + throw new IllegalStateException(e); + } + + LOG.debug("Topology {} initialized", topologyId); } @Override - public ListenableFuture connectNode(final NodeId nodeId, final Node configNode) { + public ListenableFuture connectNode(final NodeId nodeId, final Node configNode) { LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode)); return setupConnection(nodeId, configNode); } @@ -145,21 +159,21 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { } @Override - public ListenableFuture disconnectNode(final NodeId nodeId) { - LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue()); + public ListenableFuture disconnectNode(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("Unable to disconnect device that is not connected")); + new IllegalStateException("Cannot disconnect " + nodeName + " as it is not connected")); } connectorDTO.close(); - return Futures.immediateFuture(null); + return Futures.immediateFuture(Empty.value()); } - protected ListenableFuture setupConnection(final NodeId nodeId, - final Node configNode) { + protected ListenableFuture setupConnection(final NodeId nodeId, final Node configNode) { final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class); final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class); @@ -171,14 +185,14 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener(); final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(netconfClientSessionListener, netconfNode, nodeId); - final ListenableFuture future = + final ListenableFuture future = deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig); activeConnectors.put(nodeId, deviceCommunicatorDTO); - Futures.addCallback(future, new FutureCallback() { + Futures.addCallback(future, new FutureCallback<>() { @Override - public void onSuccess(final NetconfDeviceCapabilities result) { + public void onSuccess(final Empty result) { LOG.debug("Connector for {} started succesfully", nodeId.getValue()); } @@ -198,53 +212,58 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node, final NetconfNodeAugmentedOptional nodeOptional) { - final RemoteDeviceId remoteDeviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, node); - + final var deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, node); final long keepaliveDelay = node.requireKeepaliveDelay().toJava(); - RemoteDeviceHandler salFacade = createSalFacade(remoteDeviceId); + + final var deviceSalFacade = new NetconfTopologyDeviceSalFacade(deviceId, mountPointService, + node.requireLockDatastore(), dataBroker); + // The facade we are going it present to NetconfDevice + RemoteDeviceHandler salFacade; + final KeepaliveSalFacade keepAliveFacade; if (keepaliveDelay > 0) { LOG.info("Adding keepalive facade, for device {}", nodeId); - salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), - keepaliveDelay, node.requireDefaultRequestTimeoutMillis().toJava()); + salFacade = keepAliveFacade = new KeepaliveSalFacade(deviceId, deviceSalFacade, + keepaliveExecutor.getExecutor(), keepaliveDelay, node.requireDefaultRequestTimeoutMillis().toJava()); + } else { + salFacade = deviceSalFacade; + keepAliveFacade = null; + } + + // Setup reconnection on empty context, if so configured + if (nodeOptional != null && nodeOptional.getIgnoreMissingSchemaSources().getAllowed()) { + LOG.warn("Ignoring missing schema sources is not currently implemented for {}", deviceId); } - final RemoteDevice device; + final RemoteDevice device; final List> yanglibRegistrations; if (node.requireSchemaless()) { - device = new SchemalessNetconfDevice(baseSchemas, remoteDeviceId, salFacade); + device = new SchemalessNetconfDevice(baseSchemas, deviceId, salFacade); yanglibRegistrations = List.of(); } else { - final boolean reconnectOnChangedSchema = node.requireReconnectOnChangedSchema(); final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node.getSchemaCacheDirectory(), nodeId.getValue()); device = new NetconfDeviceBuilder() - .setReconnectOnSchemasChange(reconnectOnChangedSchema) + .setReconnectOnSchemasChange(node.requireReconnectOnChangedSchema()) .setSchemaResourcesDTO(resources) .setGlobalProcessingExecutor(processingExecutor) - .setId(remoteDeviceId) + .setId(deviceId) .setSalFacade(salFacade) - .setNode(node) - .setEventExecutor(eventExecutor) - .setNodeOptional(nodeOptional) .setDeviceActionFactory(deviceActionFactory) .setBaseSchemas(baseSchemas) .build(); - yanglibRegistrations = registerDeviceSchemaSources(remoteDeviceId, node, resources); + yanglibRegistrations = registerDeviceSchemaSources(deviceId, node, resources); } - final Optional userCapabilities = getUserCapabilities(node); final int rpcMessageLimit = node.requireConcurrentRpcLimit().toJava(); if (rpcMessageLimit < 1) { - LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId); + LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", deviceId); } - final NetconfDeviceCommunicator netconfDeviceCommunicator = - userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device, - userCapabilities.get(), rpcMessageLimit) - : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit); + final var netconfDeviceCommunicator = new NetconfDeviceCommunicator(deviceId, device, rpcMessageLimit, + NetconfNodeUtils.extractUserCapabilities(node)); - if (salFacade instanceof KeepaliveSalFacade) { - ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator); + if (keepAliveFacade != null) { + keepAliveFacade.setListener(netconfDeviceCommunicator); } return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations); @@ -252,7 +271,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { private static List> registerDeviceSchemaSources(final RemoteDeviceId remoteDeviceId, final NetconfNode node, final SchemaResourcesDTO resources) { - final YangLibrary yangLibrary = node.getYangLibrary(); + final var yangLibrary = node.getYangLibrary(); if (yangLibrary != null) { final Uri uri = yangLibrary.getYangLibraryUrl(); if (uri != null) { @@ -303,7 +322,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { node.requireMaxConnectionAttempts().toJava(), node.requireBetweenAttemptsTimeoutMillis().toJava(), node.requireSleepFactor().decimalValue()); final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder; - final Protocol protocol = node.getProtocol(); + final var protocol = node.getProtocol(); if (node.requireTcpOnly()) { reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create() .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP) @@ -337,58 +356,24 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) { if (credentials - instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology - .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword) { + instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev221225 + .credentials.credentials.LoginPassword loginPassword) { return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword()); } - if (credentials instanceof LoginPwUnencrypted) { - final LoginPasswordUnencrypted loginPassword = - ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted(); + if (credentials instanceof LoginPwUnencrypted unencrypted) { + final var loginPassword = unencrypted.getLoginPasswordUnencrypted(); return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword()); } - if (credentials instanceof LoginPw) { - final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword(); + if (credentials instanceof LoginPw loginPw) { + final var loginPassword = loginPw.getLoginPassword(); return new LoginPasswordHandler(loginPassword.getUsername(), encryptionService.decrypt(loginPassword.getPassword())); } - if (credentials instanceof KeyAuth) { - final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased(); + if (credentials instanceof KeyAuth keyAuth) { + final var keyPair = keyAuth.getKeyBased(); return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(), keystoreAdapter, encryptionService); } throw new IllegalStateException("Unsupported credential type: " + credentials.getClass()); } - - protected abstract RemoteDeviceHandler createSalFacade(RemoteDeviceId id); - - private static Optional getUserCapabilities(final NetconfNode node) { - // if none of yang-module-capabilities or non-module-capabilities is specified - // just return absent - if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) { - return Optional.empty(); - } - - final List capabilities = new ArrayList<>(); - - boolean overrideYangModuleCaps = false; - if (node.getYangModuleCapabilities() != null) { - capabilities.addAll(node.getYangModuleCapabilities().getCapability()); - overrideYangModuleCaps = node.getYangModuleCapabilities().getOverride(); - } - - //non-module capabilities should not exist in yang module capabilities - final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities); - Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(), - "List yang-module-capabilities/capability should contain only module based capabilities. " - + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps()); - - boolean overrideNonModuleCaps = false; - if (node.getNonModuleCapabilities() != null) { - capabilities.addAll(node.getNonModuleCapabilities().getCapability()); - overrideNonModuleCaps = node.getNonModuleCapabilities().getOverride(); - } - - return Optional.of(new UserPreferences(NetconfSessionPreferences - .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps)); - } }