X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fnetconf-topology-singleton%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Ftopology%2Fsingleton%2Fimpl%2FRemoteDeviceConnectorImpl.java;h=acf3854a83c7289b3b9bd91ae56b1b2217043d16;hb=202b4d062c1064fc061009d5725b8a102fcc13c4;hp=0525789d153eac9f575ee8b05423e075345c2d43;hpb=77eb72cb594acdcf302a9a49aeb9ced8281bab8c;p=netconf.git diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java index 0525789d15..acf3854a83 100644 --- a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java @@ -9,36 +9,37 @@ package org.opendaylight.netconf.topology.singleton.impl; import akka.actor.ActorRef; +import akka.util.Timeout; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import io.netty.util.concurrent.EventExecutor; -import java.io.File; import java.math.BigDecimal; import java.net.InetSocketAddress; import java.net.URL; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; import org.opendaylight.netconf.api.NetconfMessage; 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.conf.NetconfReconnectingClientConfigurationBuilder; import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; -import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; @@ -57,19 +58,13 @@ import org.opendaylight.protocol.framework.TimedReconnectStrategy; 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.opendaylight.netconf.node.topology.rev150114.NetconfNode; +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.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; -import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; 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; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; -import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; -import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; -import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; -import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,44 +72,29 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { private static final Logger LOG = LoggerFactory.getLogger(RemoteDeviceConnectorImpl.class); - /** - * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name - * of the schema cache directory, and the value is a corresponding SchemaResourcesDTO. The - * SchemaResourcesDTO is essentially a container that allows for the extraction of the - * SchemaRegistry and SchemaContextFactory which should be used for a particular - * Netconf mount. Access to schemaResourcesDTOs should be surrounded by appropriate - * synchronization locks. - */ - private static final Map schemaResourcesDTOs = new HashMap<>(); - - private SchemaSourceRegistry schemaRegistry = NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; - private SchemaRepository schemaRepository = NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + // Initializes default constant instances for the case when the default schema repository + // directory cache/schema is used. private final NetconfTopologySetup netconfTopologyDeviceSetup; private final RemoteDeviceId remoteDeviceId; - - private SchemaContextFactory schemaContextFactory = NetconfTopologyUtils.DEFAULT_SCHEMA_CONTEXT_FACTORY; + private final DOMMountPointService mountService; + private final Timeout actorResponseWaitTime; + private final String privateKeyPath; + private final String privateKeyPassphrase; + private final AAAEncryptionService encryptionService; private NetconfConnectorDTO deviceCommunicatorDTO; - // Initializes default constant instances for the case when the default schema repository - // directory cache/schema is used. - static { - schemaResourcesDTOs.put(NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY, - new NetconfDevice.SchemaResourcesDTO(NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY, - NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY, - NetconfTopologyUtils.DEFAULT_SCHEMA_CONTEXT_FACTORY, - new NetconfStateSchemasResolverImpl())); - NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(NetconfTopologyUtils.DEFAULT_CACHE); - NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener( - TextToASTTransformer.create(NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY, - NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY)); - } - public RemoteDeviceConnectorImpl(final NetconfTopologySetup netconfTopologyDeviceSetup, - final RemoteDeviceId remoteDeviceId) { + final RemoteDeviceId remoteDeviceId, final Timeout actorResponseWaitTime, + final DOMMountPointService mountService) { this.netconfTopologyDeviceSetup = Preconditions.checkNotNull(netconfTopologyDeviceSetup); this.remoteDeviceId = remoteDeviceId; + this.actorResponseWaitTime = actorResponseWaitTime; + this.mountService = mountService; + this.privateKeyPath = netconfTopologyDeviceSetup.getPrivateKeyPath(); + this.privateKeyPassphrase = netconfTopologyDeviceSetup.getPrivateKeyPassphrase(); + this.encryptionService = netconfTopologyDeviceSetup.getEncryptionService(); } @Override @@ -136,30 +116,31 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { Futures.addCallback(future, new FutureCallback() { @Override - public void onSuccess(NetconfDeviceCapabilities result) { + public void onSuccess(final NetconfDeviceCapabilities result) { LOG.debug("{}: Connector started successfully", remoteDeviceId); } @Override - public void onFailure(@Nullable Throwable throwable) { + public void onFailure(@Nullable final Throwable throwable) { LOG.error("{}: Connector failed, {}", remoteDeviceId, throwable); } }); } + @SuppressWarnings("checkstyle:IllegalCatch") @Override public void stopRemoteDeviceConnection() { Preconditions.checkNotNull(deviceCommunicatorDTO, remoteDeviceId + ": Device communicator was not created."); try { deviceCommunicatorDTO.close(); - } catch (Exception e) { + } catch (final Exception e) { LOG.error("{}: Error at closing device communicator.", remoteDeviceId, e); } } @VisibleForTesting NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node, - final ActorRef deviceContextActorRef) { + final ActorRef deviceContextActorRef) { //setup default values since default value is not supported in mdsal final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? NetconfTopologyUtils.DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis(); @@ -168,9 +149,9 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null ? NetconfTopologyUtils.DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema(); - RemoteDeviceHandler salFacade = new MasterSalFacade(remoteDeviceId, - netconfTopologyDeviceSetup.getDomBroker(), netconfTopologyDeviceSetup.getBindingAwareBroker(), - netconfTopologyDeviceSetup.getActorSystem(), deviceContextActorRef); + RemoteDeviceHandler salFacade = new MasterSalFacade(remoteDeviceId, + netconfTopologyDeviceSetup.getActorSystem(), deviceContextActorRef, actorResponseWaitTime, + mountService, netconfTopologyDeviceSetup.getDataBroker()); if (keepaliveDelay > 0) { LOG.info("{}: Adding keepalive facade.", remoteDeviceId); salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, @@ -178,14 +159,17 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { defaultRequestTimeoutMillis); } + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = netconfTopologyDeviceSetup.getSchemaResourcesDTO(); + + // pre register yang library sources as fallback schemas to schema registry - List> registeredYangLibSources = Lists.newArrayList(); + final List> registeredYangLibSources = Lists.newArrayList(); if (node.getYangLibrary() != null) { final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue(); final String yangLibUsername = node.getYangLibrary().getUsername(); final String yangLigPassword = node.getYangLibrary().getPassword(); - LibraryModulesSchemas libraryModulesSchemas; + final LibraryModulesSchemas libraryModulesSchemas; if (yangLibURL != null) { if (yangLibUsername != null && yangLigPassword != null) { libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword); @@ -193,10 +177,10 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL); } - for (Map.Entry sourceIdentifierURLEntry : + for (final Map.Entry sourceIdentifierURLEntry : libraryModulesSchemas.getAvailableModels().entrySet()) { registeredYangLibSources - .add(schemaRegistry.registerSchemaSource( + .add(schemaResourcesDTO.getSchemaRegistry().registerSchemaSource( new YangLibrarySchemaYangSourceProvider(remoteDeviceId, libraryModulesSchemas.getAvailableModels()), PotentialSchemaSource @@ -206,7 +190,6 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { } } - final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node); final RemoteDevice device; if (node.isSchemaless()) { device = new SchemalessNetconfDevice(remoteDeviceId, salFacade); @@ -230,111 +213,40 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { } return new NetconfConnectorDTO( - userCapabilities.isPresent() - ? new NetconfDeviceCommunicator( - remoteDeviceId, device, new UserPreferences(userCapabilities.get(), - node.getYangModuleCapabilities().isOverride()), rpcMessageLimit) : - new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade); + userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device, + new UserPreferences(userCapabilities.get(), + Objects.isNull(node.getYangModuleCapabilities()) + ? false : node.getYangModuleCapabilities().isOverride(), + Objects.isNull(node.getNonModuleCapabilities()) + ? false : node.getNonModuleCapabilities().isOverride()), rpcMessageLimit) + : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade); } private Optional getUserCapabilities(final NetconfNode node) { - if (node.getYangModuleCapabilities() == null) { + if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) { return Optional.empty(); } + final List capabilities = new ArrayList<>(); - final List capabilities = node.getYangModuleCapabilities().getCapability(); - if (capabilities == null || capabilities.isEmpty()) { - return Optional.empty(); + if (node.getYangModuleCapabilities() != null) { + capabilities.addAll(node.getYangModuleCapabilities().getCapability()); } - final NetconfSessionPreferences parsedOverrideCapabilities = - NetconfSessionPreferences.fromStrings(capabilities); - Preconditions.checkState(parsedOverrideCapabilities.getNonModuleCaps().isEmpty(), remoteDeviceId + - ": Capabilities to override can only contain module based capabilities, non-module capabilities " - + "will be retrieved from the device, configured non-module capabilities: " - + parsedOverrideCapabilities.getNonModuleCaps()); - - return Optional.of(parsedOverrideCapabilities); - } - - private NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) { - // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc. - NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null; - final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory(); - // Only checks to ensure the String is not empty or null; further checks related to directory accessibility - // and file permissions are handled during the FilesystemSchemaSourceCache initialization. - if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) { - // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry - // and SchemaContextFactory remain the default values. - if (!moduleSchemaCacheDirectory.equals(NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY)) { - // Multiple modules may be created at once; synchronize to avoid issues with data consistency among - // threads. - synchronized (schemaResourcesDTOs) { - // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if - // they already exist - schemaResourcesDTO = schemaResourcesDTOs.get(moduleSchemaCacheDirectory); - if (schemaResourcesDTO == null) { - schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory); - schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener( - TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(), - schemaResourcesDTO.getSchemaRegistry()) - ); - schemaResourcesDTOs.put(moduleSchemaCacheDirectory, schemaResourcesDTO); - } - } - LOG.info("{} : netconf connector will use schema cache directory {} instead of {}", - remoteDeviceId, moduleSchemaCacheDirectory, NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY); - } - } else { - LOG.info("{} : using the default directory {}", - remoteDeviceId, NetconfTopologyUtils.QUALIFIED_DEFAULT_CACHE_DIRECTORY); - } + //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()); - if (schemaResourcesDTO == null) { - schemaResourcesDTO = - new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaContextFactory, - new NetconfStateSchemasResolverImpl()); + if (node.getNonModuleCapabilities() != null) { + capabilities.addAll(node.getNonModuleCapabilities().getCapability()); } - return schemaResourcesDTO; - } - - /** - * Creates the backing Schema classes for a particular directory. - * - * @param moduleSchemaCacheDirectory The string directory relative to "cache" - * @return A DTO containing the Schema classes for the Netconf mount. - */ - private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) { - final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory); - final SchemaContextFactory schemaContextFactory - = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT); - this.schemaRegistry = repository; - this.schemaContextFactory = schemaContextFactory; - - final FilesystemSchemaSourceCache deviceCache = - createDeviceFilesystemCache(moduleSchemaCacheDirectory); - repository.registerSchemaSourceListener(deviceCache); - return new NetconfDevice.SchemaResourcesDTO(repository, repository, schemaContextFactory, - new NetconfStateSchemasResolverImpl()); - } - - /** - * Creates a FilesystemSchemaSourceCache for the custom schema cache directory. - * - * @param schemaCacheDirectory The custom cache directory relative to "cache" - * @return A FilesystemSchemaSourceCache for the custom schema cache directory - */ - private FilesystemSchemaSourceCache createDeviceFilesystemCache( - final String schemaCacheDirectory) { - final String relativeSchemaCacheDirectory = - NetconfTopologyUtils.CACHE_DIRECTORY + File.separator + schemaCacheDirectory; - return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, - new File(relativeSchemaCacheDirectory)); + return Optional.of(NetconfSessionPreferences.fromStrings(capabilities, CapabilityOrigin.UserDefined)); } //TODO: duplicate code - private InetSocketAddress getSocketAddress(final Host host, int port) { + private InetSocketAddress getSocketAddress(final Host host, final int port) { if (host.getDomainName() != null) { return new InetSocketAddress(host.getDomainName().getValue(), port); } else { @@ -345,38 +257,9 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { } } - private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory { - private final Long connectionAttempts; - private final EventExecutor executor; - private final double sleepFactor; - private final int minSleep; - - TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, - final int minSleep, final BigDecimal sleepFactor) { - if (maxConnectionAttempts != null && maxConnectionAttempts > 0) { - connectionAttempts = maxConnectionAttempts; - } else { - connectionAttempts = null; - } - - this.sleepFactor = sleepFactor.doubleValue(); - this.executor = executor; - this.minSleep = minSleep; - } - - @Override - public ReconnectStrategy createReconnectStrategy() { - final Long maxSleep = null; - final Long deadline = null; - - return new TimedReconnectStrategy(executor, minSleep, - minSleep, sleepFactor, maxSleep, connectionAttempts, deadline); - } - } - @VisibleForTesting NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener, - final NetconfNode node) { + final NetconfNode node) { //setup default values since default value is not supported in mdsal final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null @@ -397,10 +280,15 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { final AuthenticationHandler authHandler; final Credentials credentials = node.getCredentials(); - if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) { - authHandler = new LoginPassword( - ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(), - ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getPassword()); + if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf + .node.credentials.credentials.LoginPassword) { + authHandler = new PublicKeyAuth( + ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf + .node.credentials.credentials.LoginPassword) credentials).getUsername(), + ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf + .node.credentials.credentials.LoginPassword) credentials).getPassword(), + this.privateKeyPath, this.privateKeyPassphrase, encryptionService); + } else { throw new IllegalStateException(remoteDeviceId + ": Only login/password authentication is supported"); } @@ -418,8 +306,32 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { .build(); } - @VisibleForTesting - Map getSchemaResourcesDTOs() { - return schemaResourcesDTOs; + private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory { + private final Long connectionAttempts; + private final EventExecutor executor; + private final double sleepFactor; + private final int minSleep; + + TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, + final int minSleep, final BigDecimal sleepFactor) { + if (maxConnectionAttempts != null && maxConnectionAttempts > 0) { + connectionAttempts = maxConnectionAttempts; + } else { + connectionAttempts = null; + } + + this.sleepFactor = sleepFactor.doubleValue(); + this.executor = executor; + this.minSleep = minSleep; + } + + @Override + public ReconnectStrategy createReconnectStrategy() { + final Long maxSleep = null; + final Long deadline = null; + + return new TimedReconnectStrategy(executor, minSleep, + minSleep, sleepFactor, maxSleep, connectionAttempts, deadline); + } } }