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
9 package org.opendaylight.netconf.topology;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Strings;
14 import com.google.common.collect.Lists;
15 import com.google.common.collect.Sets;
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.MoreExecutors;
20 import com.google.common.util.concurrent.Uninterruptibles;
21 import io.netty.handler.ssl.SslHandler;
22 import io.netty.util.concurrent.EventExecutor;
24 import java.io.IOException;
25 import java.math.BigDecimal;
26 import java.net.InetSocketAddress;
28 import java.security.GeneralSecurityException;
29 import java.security.KeyStore;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
35 import java.util.concurrent.TimeUnit;
36 import javax.net.ssl.KeyManagerFactory;
37 import javax.net.ssl.SSLContext;
38 import javax.net.ssl.SSLEngine;
39 import javax.net.ssl.TrustManagerFactory;
40 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
41 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
42 import org.opendaylight.controller.config.threadpool.ThreadPool;
43 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
44 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
45 import org.opendaylight.netconf.api.NetconfMessage;
46 import org.opendaylight.netconf.client.NetconfClientDispatcher;
47 import org.opendaylight.netconf.client.NetconfClientSessionListener;
48 import org.opendaylight.netconf.client.SslHandlerFactory;
49 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
50 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
51 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
52 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
53 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
54 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
55 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
56 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
57 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
58 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
59 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
60 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
61 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
62 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
63 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
64 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
65 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
66 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
67 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
68 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
69 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
70 import org.opendaylight.netconf.topology.api.NetconfTopology;
71 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
72 import org.opendaylight.protocol.framework.ReconnectStrategy;
73 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
74 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
75 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
76 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.Specification;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.specification.TlsCase;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
89 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
90 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
91 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
92 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
93 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
94 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
95 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
96 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
97 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
98 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
99 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
100 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
101 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
102 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
103 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
104 import org.slf4j.Logger;
105 import org.slf4j.LoggerFactory;
107 public abstract class AbstractNetconfTopology implements NetconfTopology {
109 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
111 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
112 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
113 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
114 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
115 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
116 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
117 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
118 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
120 // constants related to Schema Cache(s)
122 * Filesystem based caches are stored relative to the cache directory.
124 private static final String CACHE_DIRECTORY = "cache";
127 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
129 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
132 * The qualified schema cache directory <code>cache/schema</code>.
134 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
135 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
138 * The name for the default schema repository.
140 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
143 * The default schema repository in the case that one is not specified.
145 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
146 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
148 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
149 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
152 * The default factory for creating <code>SchemaContext</code> instances.
154 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
155 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
158 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
159 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
160 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
161 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
162 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
163 * synchronization locks.
165 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
167 // Initializes default constant instances for the case when the default schema repository
168 // directory cache/schema is used.
170 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
171 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
172 DEFAULT_SCHEMA_CONTEXT_FACTORY,
173 new NetconfStateSchemasResolverImpl()));
174 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
175 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
176 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
179 * Create the default <code>FilesystemSchemaSourceCache</code>, which stores cached files
180 * in <code>cache/schema</code>. Try up to 3 times - we've seen intermittent failures on jenkins where
181 * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race
182 * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this
183 * scenario, a retry should succeed.
188 FilesystemSchemaSourceCache<YangTextSchemaSource> defaultCache =
189 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
190 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
191 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache);
193 } catch (IllegalArgumentException e) {
195 LOG.error("Error creating default schema cache", e);
198 Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
203 protected final String topologyId;
204 private final NetconfClientDispatcher clientDispatcher;
205 private final EventExecutor eventExecutor;
206 protected final ScheduledThreadPool keepaliveExecutor;
207 protected final ThreadPool processingExecutor;
208 protected final SharedSchemaRepository sharedSchemaRepository;
209 protected final DataBroker dataBroker;
210 protected final DOMMountPointService mountPointService;
211 private final NetconfKeystoreAdapter keystoreAdapter;
212 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
213 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
214 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
215 protected String privateKeyPath;
216 protected String privateKeyPassphrase;
217 protected final AAAEncryptionService encryptionService;
218 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
220 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
221 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
222 final ThreadPool processingExecutor,
223 final SchemaRepositoryProvider schemaRepositoryProvider,
224 final DataBroker dataBroker, final DOMMountPointService mountPointService,
225 final AAAEncryptionService encryptionService) {
226 this.topologyId = topologyId;
227 this.clientDispatcher = clientDispatcher;
228 this.eventExecutor = eventExecutor;
229 this.keepaliveExecutor = keepaliveExecutor;
230 this.processingExecutor = processingExecutor;
231 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
232 this.dataBroker = dataBroker;
233 this.mountPointService = mountPointService;
234 this.encryptionService = encryptionService;
236 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
239 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
240 this.schemaRegistry = schemaRegistry;
243 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
244 this.schemaContextFactory = schemaContextFactory;
248 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
249 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
250 return setupConnection(nodeId, configNode);
254 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
255 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
256 if (!activeConnectors.containsKey(nodeId)) {
257 return Futures.immediateFailedFuture(
258 new IllegalStateException("Unable to disconnect device that is not connected"));
261 // retrieve connection, and disconnect it
262 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
263 connectorDTO.getCommunicator().close();
264 connectorDTO.getFacade().close();
265 return Futures.immediateFuture(null);
268 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
269 final Node configNode) {
270 final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
272 Preconditions.checkNotNull(netconfNode.getHost());
273 Preconditions.checkNotNull(netconfNode.getPort());
274 Preconditions.checkNotNull(netconfNode.isTcpOnly());
276 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
277 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
278 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
279 final NetconfReconnectingClientConfiguration clientConfig =
280 getClientConfig(netconfClientSessionListener, netconfNode);
281 final ListenableFuture<NetconfDeviceCapabilities> future =
282 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
284 activeConnectors.put(nodeId, deviceCommunicatorDTO);
286 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
288 public void onSuccess(final NetconfDeviceCapabilities result) {
289 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
293 public void onFailure(final Throwable throwable) {
294 LOG.error("Connector for : " + nodeId.getValue() + " failed");
295 // remove this node from active connectors?
297 }, MoreExecutors.directExecutor());
302 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
303 final NetconfNode node) {
304 //setup default values since default value is not supported in mdsal
305 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
306 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
307 final long keepaliveDelay = node.getKeepaliveDelay() == null
308 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
309 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
310 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
312 final IpAddress ipAddress = node.getHost().getIpAddress();
313 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
314 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
315 node.getPort().getValue());
316 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
318 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
319 createSalFacade(remoteDeviceId);
321 if (keepaliveDelay > 0) {
322 LOG.warn("Adding keepalive facade, for device {}", nodeId);
323 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
324 keepaliveDelay, defaultRequestTimeoutMillis);
327 // pre register yang library sources as fallback schemas to schema registry
328 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
329 if (node.getYangLibrary() != null) {
330 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
331 final String yangLibUsername = node.getYangLibrary().getUsername();
332 final String yangLigPassword = node.getYangLibrary().getPassword();
334 final LibraryModulesSchemas libraryModulesSchemas;
335 if (yangLibURL != null) {
336 if (yangLibUsername != null && yangLigPassword != null) {
337 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
339 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
342 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
343 : libraryModulesSchemas.getAvailableModels().entrySet()) {
344 registeredYangLibSources
345 .add(schemaRegistry.registerSchemaSource(
346 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
347 libraryModulesSchemas.getAvailableModels()),
348 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
349 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
354 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
355 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
356 if (node.isSchemaless()) {
357 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
359 device = new NetconfDeviceBuilder()
360 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
361 .setSchemaResourcesDTO(schemaResourcesDTO)
362 .setGlobalProcessingExecutor(processingExecutor.getExecutor())
363 .setId(remoteDeviceId)
364 .setSalFacade(salFacade)
368 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
369 final int rpcMessageLimit =
370 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
372 if (rpcMessageLimit < 1) {
373 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
376 NetconfDeviceCommunicator netconfDeviceCommunicator =
377 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
378 userCapabilities.get(), rpcMessageLimit)
379 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
381 if (salFacade instanceof KeepaliveSalFacade) {
382 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
384 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
387 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
388 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
389 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
390 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
391 // Only checks to ensure the String is not empty or null; further checks related to directory
392 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
393 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
394 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
395 // the SchemaRegistry and SchemaContextFactory remain the default values.
396 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
397 // Multiple modules may be created at once;
398 // synchronize to avoid issues with data consistency among threads.
399 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
400 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
401 // if they already exist
402 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
403 if (schemaResourcesDTO == null) {
404 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
405 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
406 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
407 schemaResourcesDTO.getSchemaRegistry())
409 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
412 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
413 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
416 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
417 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
420 if (schemaResourcesDTO == null) {
421 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
422 schemaContextFactory, new NetconfStateSchemasResolverImpl());
425 return schemaResourcesDTO;
429 * Creates the backing Schema classes for a particular directory.
431 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
432 * @return A DTO containing the Schema classes for the Netconf mount.
434 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
435 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
436 final SchemaContextFactory contextFactory
437 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
438 setSchemaRegistry(repository);
439 setSchemaContextFactory(contextFactory);
440 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
441 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
442 repository.registerSchemaSourceListener(deviceCache);
443 repository.registerSchemaSourceListener(
444 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
445 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
446 new NetconfStateSchemasResolverImpl());
450 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
452 * @param schemaCacheDirectory The custom cache directory relative to "cache"
453 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
455 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
456 final String schemaCacheDirectory) {
457 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
458 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
459 new File(relativeSchemaCacheDirectory));
463 * Sets the private key path from location specified in configuration file using blueprint.
465 public void setPrivateKeyPath(final String privateKeyPath) {
466 this.privateKeyPath = privateKeyPath;
470 * Sets the private key passphrase from location specified in configuration file using blueprint.
472 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
473 this.privateKeyPassphrase = privateKeyPassphrase;
476 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
477 final NetconfNode node) {
479 //setup default values since default value is not supported in mdsal
480 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
481 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
482 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
483 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
484 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
485 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
486 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
488 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
490 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
491 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
492 final ReconnectStrategy strategy = sf.createReconnectStrategy();
494 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder =
495 NetconfReconnectingClientConfigurationBuilder.create();
497 if (node.isTcpOnly() || node.getProtocol() == null || node.getProtocol().getName() == Name.SSH) {
498 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
499 reconnectingClientConfigurationBuilder
500 .withAuthHandler(authHandler)
501 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
502 NetconfClientConfiguration.NetconfClientProtocol.SSH);
503 } else if (node.getProtocol().getName() == Name.TLS) {
504 final SslHandlerFactory sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter,
505 node.getProtocol().getSpecification());
506 reconnectingClientConfigurationBuilder
507 .withSslHandlerFactory(sslHandlerFactory)
508 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
510 throw new IllegalStateException("Unsupported protocol type: " + node.getProtocol().getName().getClass());
513 return reconnectingClientConfigurationBuilder
514 .withAddress(socketAddress)
515 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
516 .withReconnectStrategy(strategy)
517 .withConnectStrategyFactory(sf)
518 .withSessionListener(listener)
522 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
523 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
524 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
525 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
526 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
527 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
528 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
529 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
531 if (credentials instanceof LoginPwUnencrypted) {
532 final LoginPasswordUnencrypted loginPassword =
533 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
534 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
536 if (credentials instanceof LoginPw) {
537 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
538 return new LoginPasswordHandler(loginPassword.getUsername(),
539 encryptionService.decrypt(loginPassword.getPassword()));
541 if (credentials instanceof KeyAuth) {
542 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
543 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
544 keystoreAdapter, encryptionService);
546 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
549 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
551 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
552 if (host.getDomainName() != null) {
553 return new InetSocketAddress(host.getDomainName().getValue(), port);
556 final IpAddress ipAddress = host.getIpAddress();
557 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
558 : ipAddress.getIpv6Address().getValue();
559 return new InetSocketAddress(ip, port);
562 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
563 // if none of yang-module-capabilities or non-module-capabilities is specified
564 // just return absent
565 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
566 return Optional.absent();
569 final List<String> capabilities = new ArrayList<>();
571 boolean overrideYangModuleCaps = false;
572 if (node.getYangModuleCapabilities() != null) {
573 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
574 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
577 //non-module capabilities should not exist in yang module capabilities
578 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
579 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
580 "List yang-module-capabilities/capability should contain only module based capabilities. "
581 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
583 boolean overrideNonModuleCaps = false;
584 if (node.getNonModuleCapabilities() != null) {
585 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
586 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
589 return Optional.of(new UserPreferences(NetconfSessionPreferences
590 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
593 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
594 private final Long connectionAttempts;
595 private final EventExecutor executor;
596 private final double sleepFactor;
597 private final int minSleep;
599 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
600 final int minSleep, final BigDecimal sleepFactor) {
601 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
602 connectionAttempts = maxConnectionAttempts;
604 connectionAttempts = null;
607 this.sleepFactor = sleepFactor.doubleValue();
608 this.executor = executor;
609 this.minSleep = minSleep;
613 public ReconnectStrategy createReconnectStrategy() {
614 return new TimedReconnectStrategy(executor, minSleep,
615 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
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();
649 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
650 private final NetconfKeystoreAdapter keystoreAdapter;
651 private final Optional<Specification> specOptional;
653 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
654 this.keystoreAdapter = keystoreAdapter;
655 this.specOptional = Optional.fromNullable(specification);
659 public SslHandler createSslHandler() {
661 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
663 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
664 kmf.init(keyStore, "".toCharArray());
666 final TrustManagerFactory tmf =
667 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
670 final SSLContext sslCtx = SSLContext.getInstance("TLS");
671 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
672 final SSLEngine engine = sslCtx.createSSLEngine();
673 engine.setUseClientMode(true);
675 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
676 if (specOptional.isPresent()) {
677 final Specification specification = specOptional.get();
678 if (!(specification instanceof TlsCase)) {
679 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
681 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
684 engine.setEnabledProtocols(protocols.toArray(new String[0]));
685 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
686 engine.setEnableSessionCreation(true);
688 return new SslHandler(engine);
689 } catch (GeneralSecurityException | IOException exc) {
690 throw new IllegalStateException(exc);