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