2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.topology;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.Lists;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import com.google.common.util.concurrent.ListeningExecutorService;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import com.google.common.util.concurrent.Uninterruptibles;
22 import io.netty.util.concurrent.EventExecutor;
24 import java.math.BigDecimal;
25 import java.net.InetSocketAddress;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
31 import java.util.Optional;
32 import java.util.concurrent.TimeUnit;
33 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
34 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
35 import org.opendaylight.controller.config.threadpool.ThreadPool;
36 import org.opendaylight.mdsal.binding.api.DataBroker;
37 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
38 import org.opendaylight.netconf.api.NetconfMessage;
39 import org.opendaylight.netconf.client.NetconfClientDispatcher;
40 import org.opendaylight.netconf.client.NetconfClientSessionListener;
41 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
42 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
43 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
44 import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
45 import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
46 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
47 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
48 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
49 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
50 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
51 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
52 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
53 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
54 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
55 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
56 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
57 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
58 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
59 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
60 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
61 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
62 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
63 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
64 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
65 import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl;
66 import org.opendaylight.netconf.topology.api.NetconfTopology;
67 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
82 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
83 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
84 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
85 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
86 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
87 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
88 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
89 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
90 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
91 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
92 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
93 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
94 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
95 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
96 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
97 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
101 public abstract class AbstractNetconfTopology implements NetconfTopology {
103 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
105 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
106 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
107 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
108 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
109 private static final boolean DEFAULT_IS_TCP_ONLY = false;
110 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
111 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
112 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
113 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
115 // constants related to Schema Cache(s)
117 * Filesystem based caches are stored relative to the cache directory.
119 private static final String CACHE_DIRECTORY = "cache";
122 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
124 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
127 * The qualified schema cache directory <code>cache/schema</code>.
129 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
130 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
133 * The name for the default schema repository.
135 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
138 * The default schema repository in the case that one is not specified.
140 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
141 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
143 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
144 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
147 * The default factory for creating <code>SchemaContext</code> instances.
149 private static final EffectiveModelContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
150 DEFAULT_SCHEMA_REPOSITORY.createEffectiveModelContextFactory(
151 SchemaContextFactoryConfiguration.getDefault());
154 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
155 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
156 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
157 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
158 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
159 * synchronization locks.
161 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
163 // Initializes default constant instances for the case when the default schema repository
164 // directory cache/schema is used.
166 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
167 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
168 DEFAULT_SCHEMA_CONTEXT_FACTORY,
169 new NetconfStateSchemasResolverImpl()));
170 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
171 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
172 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
175 * Create the default <code>FilesystemSchemaSourceCache</code>, which stores cached files
176 * in <code>cache/schema</code>. Try up to 3 times - we've seen intermittent failures on jenkins where
177 * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race
178 * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this
179 * scenario, a retry should succeed.
184 FilesystemSchemaSourceCache<YangTextSchemaSource> defaultCache =
185 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
186 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
187 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache);
189 } catch (IllegalArgumentException e) {
191 LOG.error("Error creating default schema cache", e);
194 Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
199 private final NetconfClientDispatcher clientDispatcher;
200 private final EventExecutor eventExecutor;
201 private final DeviceActionFactory deviceActionFactory;
202 private final NetconfKeystoreAdapter keystoreAdapter;
203 protected final ScheduledThreadPool keepaliveExecutor;
204 protected final ListeningExecutorService processingExecutor;
205 protected final SharedSchemaRepository sharedSchemaRepository;
206 protected final DataBroker dataBroker;
207 protected final DOMMountPointService mountPointService;
208 protected final String topologyId;
209 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
210 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
211 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
212 protected String privateKeyPath;
213 protected String privateKeyPassphrase;
214 protected final AAAEncryptionService encryptionService;
215 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
217 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
218 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
219 final ThreadPool processingExecutor,
220 final SchemaRepositoryProvider schemaRepositoryProvider,
221 final DataBroker dataBroker, final DOMMountPointService mountPointService,
222 final AAAEncryptionService encryptionService,
223 final DeviceActionFactory deviceActionFactory) {
224 this.topologyId = topologyId;
225 this.clientDispatcher = clientDispatcher;
226 this.eventExecutor = eventExecutor;
227 this.keepaliveExecutor = keepaliveExecutor;
228 this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
229 this.deviceActionFactory = deviceActionFactory;
230 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
231 this.dataBroker = dataBroker;
232 this.mountPointService = mountPointService;
233 this.encryptionService = encryptionService;
235 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
238 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
239 this.schemaRegistry = schemaRegistry;
242 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
243 this.schemaContextFactory = schemaContextFactory;
247 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
248 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode));
249 return setupConnection(nodeId, configNode);
253 * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
255 * @param nodeConfiguration Node configuration container.
256 * @return String representation of node configuration with credentials replaced by asterisks.
259 public static String hideCredentials(final Node nodeConfiguration) {
260 final NetconfNode netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
261 final String nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
262 final String nodeConfigurationString = nodeConfiguration.toString();
263 return nodeConfigurationString.replace(nodeCredentials, "***");
267 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
268 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
269 if (!activeConnectors.containsKey(nodeId)) {
270 return Futures.immediateFailedFuture(
271 new IllegalStateException("Unable to disconnect device that is not connected"));
274 // retrieve connection, and disconnect it
275 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
276 connectorDTO.getCommunicator().close();
277 connectorDTO.getFacade().close();
278 return Futures.immediateFuture(null);
281 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
282 final Node configNode) {
283 final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
284 final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
286 requireNonNull(netconfNode.getHost());
287 requireNonNull(netconfNode.getPort());
289 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional);
290 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
291 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
292 final NetconfReconnectingClientConfiguration clientConfig =
293 getClientConfig(netconfClientSessionListener, netconfNode);
294 final ListenableFuture<NetconfDeviceCapabilities> future =
295 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
297 activeConnectors.put(nodeId, deviceCommunicatorDTO);
299 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
301 public void onSuccess(final NetconfDeviceCapabilities result) {
302 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
306 public void onFailure(final Throwable throwable) {
307 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
308 // remove this node from active connectors?
310 }, MoreExecutors.directExecutor());
315 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
316 return createDeviceCommunicator(nodeId, node, null);
319 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
320 final NetconfNodeAugmentedOptional nodeOptional) {
321 //setup default values since default value is not supported in mdsal
322 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
323 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
324 final long keepaliveDelay = node.getKeepaliveDelay() == null
325 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
326 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
327 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
329 final IpAddress ipAddress = node.getHost().getIpAddress();
330 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
331 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
332 node.getPort().getValue());
333 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
335 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
336 createSalFacade(remoteDeviceId);
338 if (keepaliveDelay > 0) {
339 LOG.warn("Adding keepalive facade, for device {}", nodeId);
340 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
341 keepaliveDelay, defaultRequestTimeoutMillis);
344 // pre register yang library sources as fallback schemas to schema registry
345 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
346 if (node.getYangLibrary() != null) {
347 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
348 final String yangLibUsername = node.getYangLibrary().getUsername();
349 final String yangLigPassword = node.getYangLibrary().getPassword();
351 final LibraryModulesSchemas libraryModulesSchemas;
352 if (yangLibURL != null) {
353 if (yangLibUsername != null && yangLigPassword != null) {
354 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
356 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
359 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
360 : libraryModulesSchemas.getAvailableModels().entrySet()) {
361 registeredYangLibSources
362 .add(schemaRegistry.registerSchemaSource(
363 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
364 libraryModulesSchemas.getAvailableModels()),
365 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
366 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
371 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
372 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
373 if (node.isSchemaless()) {
374 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
376 NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
377 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
378 .setSchemaResourcesDTO(schemaResourcesDTO)
379 .setGlobalProcessingExecutor(this.processingExecutor)
380 .setId(remoteDeviceId)
381 .setSalFacade(salFacade)
383 .setEventExecutor(eventExecutor)
384 .setNodeOptional(nodeOptional);
385 if (this.deviceActionFactory != null) {
386 netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
388 device = netconfDeviceBuilder.build();
391 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
392 final int rpcMessageLimit =
393 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
395 if (rpcMessageLimit < 1) {
396 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
399 NetconfDeviceCommunicator netconfDeviceCommunicator =
400 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
401 userCapabilities.get(), rpcMessageLimit)
402 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
404 if (salFacade instanceof KeepaliveSalFacade) {
405 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
407 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
410 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
411 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
412 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
413 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
414 // Only checks to ensure the String is not empty or null; further checks related to directory
415 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
416 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
417 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
418 // the SchemaRegistry and SchemaContextFactory remain the default values.
419 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
420 // Multiple modules may be created at once;
421 // synchronize to avoid issues with data consistency among threads.
422 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
423 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
424 // if they already exist
425 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
426 if (schemaResourcesDTO == null) {
427 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
428 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
429 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
430 schemaResourcesDTO.getSchemaRegistry())
432 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
435 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
436 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
439 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
440 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
443 if (schemaResourcesDTO == null) {
444 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
445 schemaContextFactory, new NetconfStateSchemasResolverImpl());
448 return schemaResourcesDTO;
452 * Creates the backing Schema classes for a particular directory.
454 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
455 * @return A DTO containing the Schema classes for the Netconf mount.
457 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
458 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
459 final EffectiveModelContextFactory contextFactory
460 = repository.createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault());
461 setSchemaRegistry(repository);
462 setSchemaContextFactory(contextFactory);
463 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
464 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
465 repository.registerSchemaSourceListener(deviceCache);
466 repository.registerSchemaSourceListener(
467 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
468 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
469 new NetconfStateSchemasResolverImpl());
473 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
475 * @param schemaCacheDirectory The custom cache directory relative to "cache"
476 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
478 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
479 final String schemaCacheDirectory) {
480 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
481 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
482 new File(relativeSchemaCacheDirectory));
486 * Sets the private key path from location specified in configuration file using blueprint.
488 public void setPrivateKeyPath(final String privateKeyPath) {
489 this.privateKeyPath = privateKeyPath;
493 * Sets the private key passphrase from location specified in configuration file using blueprint.
495 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
496 this.privateKeyPassphrase = privateKeyPassphrase;
499 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
500 final NetconfNode node) {
502 //setup default values since default value is not supported in mdsal
503 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
504 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
505 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
506 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
507 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
508 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
509 final boolean useTcp = node.isTcpOnly() == null ? DEFAULT_IS_TCP_ONLY : node.isTcpOnly();
510 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
512 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
514 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
515 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
517 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
518 final Protocol protocol = node.getProtocol();
520 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
521 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
522 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
523 } else if (protocol == null || protocol.getName() == Name.SSH) {
524 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
525 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
526 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
527 } else if (protocol.getName() == Name.TLS) {
528 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
529 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
530 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
532 throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
535 if (node.getOdlHelloMessageCapabilities() != null) {
536 reconnectingClientConfigurationBuilder
537 .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
540 return reconnectingClientConfigurationBuilder
541 .withAddress(socketAddress)
542 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
543 .withReconnectStrategy(sf.createReconnectStrategy())
544 .withConnectStrategyFactory(sf)
545 .withSessionListener(listener)
549 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
550 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
551 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
552 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
553 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
554 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
555 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
556 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
558 if (credentials instanceof LoginPwUnencrypted) {
559 final LoginPasswordUnencrypted loginPassword =
560 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
561 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
563 if (credentials instanceof LoginPw) {
564 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
565 return new LoginPasswordHandler(loginPassword.getUsername(),
566 encryptionService.decrypt(loginPassword.getPassword()));
568 if (credentials instanceof KeyAuth) {
569 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
570 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
571 keystoreAdapter, encryptionService);
573 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
576 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
578 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
579 if (host.getDomainName() != null) {
580 return new InetSocketAddress(host.getDomainName().getValue(), port);
583 final IpAddress ipAddress = host.getIpAddress();
584 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
585 : ipAddress.getIpv6Address().getValue();
586 return new InetSocketAddress(ip, port);
589 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
590 // if none of yang-module-capabilities or non-module-capabilities is specified
591 // just return absent
592 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
593 return Optional.empty();
596 final List<String> capabilities = new ArrayList<>();
598 boolean overrideYangModuleCaps = false;
599 if (node.getYangModuleCapabilities() != null) {
600 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
601 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
604 //non-module capabilities should not exist in yang module capabilities
605 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
606 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
607 "List yang-module-capabilities/capability should contain only module based capabilities. "
608 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
610 boolean overrideNonModuleCaps = false;
611 if (node.getNonModuleCapabilities() != null) {
612 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
613 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
616 return Optional.of(new UserPreferences(NetconfSessionPreferences
617 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
620 protected static class NetconfConnectorDTO implements AutoCloseable {
622 private final NetconfDeviceCommunicator communicator;
623 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
625 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
626 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
627 this.communicator = communicator;
628 this.facade = facade;
631 public NetconfDeviceCommunicator getCommunicator() {
635 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
639 public NetconfClientSessionListener getSessionListener() {
644 public void close() {
645 communicator.close();