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().toJava();
324 final long keepaliveDelay = node.getKeepaliveDelay() == null
325 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay().toJava();
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().toJava());
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 .setDeviceActionFactory(deviceActionFactory);
386 device = netconfDeviceBuilder.build();
389 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
390 final int rpcMessageLimit = node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT
391 : node.getConcurrentRpcLimit().toJava();
393 if (rpcMessageLimit < 1) {
394 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
397 NetconfDeviceCommunicator netconfDeviceCommunicator =
398 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
399 userCapabilities.get(), rpcMessageLimit)
400 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
402 if (salFacade instanceof KeepaliveSalFacade) {
403 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
405 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
408 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
409 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
410 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
411 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
412 // Only checks to ensure the String is not empty or null; further checks related to directory
413 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
414 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
415 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
416 // the SchemaRegistry and SchemaContextFactory remain the default values.
417 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
418 // Multiple modules may be created at once;
419 // synchronize to avoid issues with data consistency among threads.
420 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
421 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
422 // if they already exist
423 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
424 if (schemaResourcesDTO == null) {
425 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
426 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
427 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
428 schemaResourcesDTO.getSchemaRegistry())
430 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
433 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
434 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
437 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
438 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
441 if (schemaResourcesDTO == null) {
442 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
443 schemaContextFactory, new NetconfStateSchemasResolverImpl());
446 return schemaResourcesDTO;
450 * Creates the backing Schema classes for a particular directory.
452 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
453 * @return A DTO containing the Schema classes for the Netconf mount.
455 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
456 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
457 final EffectiveModelContextFactory contextFactory
458 = repository.createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault());
459 setSchemaRegistry(repository);
460 setSchemaContextFactory(contextFactory);
461 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
462 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
463 repository.registerSchemaSourceListener(deviceCache);
464 repository.registerSchemaSourceListener(
465 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
466 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
467 new NetconfStateSchemasResolverImpl());
471 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
473 * @param schemaCacheDirectory The custom cache directory relative to "cache"
474 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
476 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
477 final String schemaCacheDirectory) {
478 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
479 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
480 new File(relativeSchemaCacheDirectory));
484 * Sets the private key path from location specified in configuration file using blueprint.
486 public void setPrivateKeyPath(final String privateKeyPath) {
487 this.privateKeyPath = privateKeyPath;
491 * Sets the private key passphrase from location specified in configuration file using blueprint.
493 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
494 this.privateKeyPassphrase = privateKeyPassphrase;
497 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
498 final NetconfNode node) {
500 //setup default values since default value is not supported in mdsal
501 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
502 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis().toJava();
503 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
504 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts().toJava();
505 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
506 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis().toJava();
507 final boolean useTcp = node.isTcpOnly() == null ? DEFAULT_IS_TCP_ONLY : node.isTcpOnly();
508 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
510 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue().toJava());
512 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
513 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
515 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
516 final Protocol protocol = node.getProtocol();
518 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
519 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
520 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
521 } else if (protocol == null || protocol.getName() == Name.SSH) {
522 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
523 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
524 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
525 } else if (protocol.getName() == Name.TLS) {
526 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
527 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
528 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
530 throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
533 if (node.getOdlHelloMessageCapabilities() != null) {
534 reconnectingClientConfigurationBuilder
535 .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
538 return reconnectingClientConfigurationBuilder
539 .withAddress(socketAddress)
540 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
541 .withReconnectStrategy(sf.createReconnectStrategy())
542 .withConnectStrategyFactory(sf)
543 .withSessionListener(listener)
547 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
548 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
549 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
550 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
551 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
552 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
553 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
554 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
556 if (credentials instanceof LoginPwUnencrypted) {
557 final LoginPasswordUnencrypted loginPassword =
558 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
559 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
561 if (credentials instanceof LoginPw) {
562 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
563 return new LoginPasswordHandler(loginPassword.getUsername(),
564 encryptionService.decrypt(loginPassword.getPassword()));
566 if (credentials instanceof KeyAuth) {
567 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
568 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
569 keystoreAdapter, encryptionService);
571 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
574 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
576 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
577 if (host.getDomainName() != null) {
578 return new InetSocketAddress(host.getDomainName().getValue(), port);
581 final IpAddress ipAddress = host.getIpAddress();
582 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
583 : ipAddress.getIpv6Address().getValue();
584 return new InetSocketAddress(ip, port);
587 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
588 // if none of yang-module-capabilities or non-module-capabilities is specified
589 // just return absent
590 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
591 return Optional.empty();
594 final List<String> capabilities = new ArrayList<>();
596 boolean overrideYangModuleCaps = false;
597 if (node.getYangModuleCapabilities() != null) {
598 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
599 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
602 //non-module capabilities should not exist in yang module capabilities
603 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
604 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
605 "List yang-module-capabilities/capability should contain only module based capabilities. "
606 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
608 boolean overrideNonModuleCaps = false;
609 if (node.getNonModuleCapabilities() != null) {
610 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
611 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
614 return Optional.of(new UserPreferences(NetconfSessionPreferences
615 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
618 protected static class NetconfConnectorDTO implements AutoCloseable {
620 private final NetconfDeviceCommunicator communicator;
621 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
623 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
624 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
625 this.communicator = communicator;
626 this.facade = facade;
629 public NetconfDeviceCommunicator getCommunicator() {
633 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
637 public NetconfClientSessionListener getSessionListener() {
642 public void close() {
643 communicator.close();