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 io.netty.handler.ssl.SslHandler;
21 import io.netty.util.concurrent.EventExecutor;
23 import java.io.IOException;
24 import java.math.BigDecimal;
25 import java.net.InetSocketAddress;
27 import java.security.GeneralSecurityException;
28 import java.security.KeyStore;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
34 import javax.net.ssl.KeyManagerFactory;
35 import javax.net.ssl.SSLContext;
36 import javax.net.ssl.SSLEngine;
37 import javax.net.ssl.TrustManagerFactory;
38 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
39 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
40 import org.opendaylight.controller.config.threadpool.ThreadPool;
41 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
42 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
43 import org.opendaylight.netconf.api.NetconfMessage;
44 import org.opendaylight.netconf.client.NetconfClientDispatcher;
45 import org.opendaylight.netconf.client.NetconfClientSessionListener;
46 import org.opendaylight.netconf.client.SslHandlerFactory;
47 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
48 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
49 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
50 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
51 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
52 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
53 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
54 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
55 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
56 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
57 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
58 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
59 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
60 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
61 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
62 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
63 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
64 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
65 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
66 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
67 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
68 import org.opendaylight.netconf.topology.api.NetconfTopology;
69 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
70 import org.opendaylight.protocol.framework.ReconnectStrategy;
71 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
72 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.Specification;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.specification.TlsCase;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
86 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
87 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
88 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
89 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
90 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
91 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
92 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
93 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
94 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
95 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
96 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
97 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
98 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
102 public abstract class AbstractNetconfTopology implements NetconfTopology {
104 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
106 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
107 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
108 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
109 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
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);
144 * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
146 private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
147 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
148 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
151 * The default factory for creating <code>SchemaContext</code> instances.
153 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
154 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
157 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
158 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
159 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
160 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
161 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
162 * synchronization locks.
164 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
166 // Initializes default constant instances for the case when the default schema repository
167 // directory cache/schema is used.
169 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
170 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
171 DEFAULT_SCHEMA_CONTEXT_FACTORY,
172 new NetconfStateSchemasResolverImpl()));
173 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
174 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
175 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
178 protected final String topologyId;
179 private final NetconfClientDispatcher clientDispatcher;
180 private final EventExecutor eventExecutor;
181 protected final ScheduledThreadPool keepaliveExecutor;
182 protected final ThreadPool processingExecutor;
183 protected final SharedSchemaRepository sharedSchemaRepository;
184 protected final DataBroker dataBroker;
185 protected final DOMMountPointService mountPointService;
186 private final NetconfKeystoreAdapter keystoreAdapter;
187 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
188 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
189 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
190 protected String privateKeyPath;
191 protected String privateKeyPassphrase;
192 protected final AAAEncryptionService encryptionService;
193 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
195 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
196 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
197 final ThreadPool processingExecutor,
198 final SchemaRepositoryProvider schemaRepositoryProvider,
199 final DataBroker dataBroker, final DOMMountPointService mountPointService,
200 final AAAEncryptionService encryptionService) {
201 this.topologyId = topologyId;
202 this.clientDispatcher = clientDispatcher;
203 this.eventExecutor = eventExecutor;
204 this.keepaliveExecutor = keepaliveExecutor;
205 this.processingExecutor = processingExecutor;
206 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
207 this.dataBroker = dataBroker;
208 this.mountPointService = mountPointService;
209 this.encryptionService = encryptionService;
211 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
214 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
215 this.schemaRegistry = schemaRegistry;
218 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
219 this.schemaContextFactory = schemaContextFactory;
223 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
224 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
225 return setupConnection(nodeId, configNode);
229 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
230 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
231 if (!activeConnectors.containsKey(nodeId)) {
232 return Futures.immediateFailedFuture(
233 new IllegalStateException("Unable to disconnect device that is not connected"));
236 // retrieve connection, and disconnect it
237 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
238 connectorDTO.getCommunicator().close();
239 connectorDTO.getFacade().close();
240 return Futures.immediateFuture(null);
243 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
244 final Node configNode) {
245 final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
247 Preconditions.checkNotNull(netconfNode.getHost());
248 Preconditions.checkNotNull(netconfNode.getPort());
249 Preconditions.checkNotNull(netconfNode.isTcpOnly());
251 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
252 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
253 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
254 final NetconfReconnectingClientConfiguration clientConfig =
255 getClientConfig(netconfClientSessionListener, netconfNode);
256 final ListenableFuture<NetconfDeviceCapabilities> future =
257 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
259 activeConnectors.put(nodeId, deviceCommunicatorDTO);
261 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
263 public void onSuccess(final NetconfDeviceCapabilities result) {
264 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
268 public void onFailure(final Throwable throwable) {
269 LOG.error("Connector for : " + nodeId.getValue() + " failed");
270 // remove this node from active connectors?
272 }, MoreExecutors.directExecutor());
277 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
278 final NetconfNode node) {
279 //setup default values since default value is not supported in mdsal
280 final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
281 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
282 final Long keepaliveDelay = node.getKeepaliveDelay() == null
283 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
284 final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
285 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
287 final IpAddress ipAddress = node.getHost().getIpAddress();
288 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
289 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
290 node.getPort().getValue());
291 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
293 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
294 createSalFacade(remoteDeviceId);
296 if (keepaliveDelay > 0) {
297 LOG.warn("Adding keepalive facade, for device {}", nodeId);
298 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
299 keepaliveDelay, defaultRequestTimeoutMillis);
302 // pre register yang library sources as fallback schemas to schema registry
303 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
304 if (node.getYangLibrary() != null) {
305 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
306 final String yangLibUsername = node.getYangLibrary().getUsername();
307 final String yangLigPassword = node.getYangLibrary().getPassword();
309 final LibraryModulesSchemas libraryModulesSchemas;
310 if (yangLibURL != null) {
311 if (yangLibUsername != null && yangLigPassword != null) {
312 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
314 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
317 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
318 : libraryModulesSchemas.getAvailableModels().entrySet()) {
319 registeredYangLibSources
320 .add(schemaRegistry.registerSchemaSource(
321 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
322 libraryModulesSchemas.getAvailableModels()),
323 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
324 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
329 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
330 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
331 if (node.isSchemaless()) {
332 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
334 device = new NetconfDeviceBuilder()
335 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
336 .setSchemaResourcesDTO(schemaResourcesDTO)
337 .setGlobalProcessingExecutor(processingExecutor.getExecutor())
338 .setId(remoteDeviceId)
339 .setSalFacade(salFacade)
343 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
344 final int rpcMessageLimit =
345 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
347 if (rpcMessageLimit < 1) {
348 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
351 return new NetconfConnectorDTO(userCapabilities.isPresent()
352 ? new NetconfDeviceCommunicator(remoteDeviceId, device, userCapabilities.get(), rpcMessageLimit)
353 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade);
356 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
357 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
358 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
359 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
360 // Only checks to ensure the String is not empty or null; further checks related to directory
361 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
362 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
363 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
364 // the SchemaRegistry and SchemaContextFactory remain the default values.
365 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
366 // Multiple modules may be created at once;
367 // synchronize to avoid issues with data consistency among threads.
368 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
369 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
370 // if they already exist
371 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
372 if (schemaResourcesDTO == null) {
373 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
374 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
375 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
376 schemaResourcesDTO.getSchemaRegistry())
378 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
381 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
382 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
385 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
386 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
389 if (schemaResourcesDTO == null) {
390 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
391 schemaContextFactory, new NetconfStateSchemasResolverImpl());
394 return schemaResourcesDTO;
398 * Creates the backing Schema classes for a particular directory.
400 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
401 * @return A DTO containing the Schema classes for the Netconf mount.
403 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
404 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
405 final SchemaContextFactory contextFactory
406 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
407 setSchemaRegistry(repository);
408 setSchemaContextFactory(contextFactory);
409 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
410 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
411 repository.registerSchemaSourceListener(deviceCache);
412 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
413 new NetconfStateSchemasResolverImpl());
417 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
419 * @param schemaCacheDirectory The custom cache directory relative to "cache"
420 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
422 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
423 final String schemaCacheDirectory) {
424 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
425 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
426 new File(relativeSchemaCacheDirectory));
430 * Sets the private key path from location specified in configuration file using blueprint.
432 public void setPrivateKeyPath(final String privateKeyPath) {
433 this.privateKeyPath = privateKeyPath;
437 * Sets the private key passphrase from location specified in configuration file using blueprint.
439 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
440 this.privateKeyPassphrase = privateKeyPassphrase;
443 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
444 final NetconfNode node) {
446 //setup default values since default value is not supported in mdsal
447 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
448 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
449 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
450 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
451 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
452 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
453 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
455 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
457 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
458 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
459 final ReconnectStrategy strategy = sf.createReconnectStrategy();
461 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
463 return NetconfReconnectingClientConfigurationBuilder.create()
464 .withAddress(socketAddress)
465 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
466 .withReconnectStrategy(strategy)
467 .withAuthHandler(authHandler)
468 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
469 NetconfClientConfiguration.NetconfClientProtocol.SSH)
470 .withConnectStrategyFactory(sf)
471 .withSessionListener(listener)
475 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
476 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
477 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
478 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
479 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
480 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
481 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
482 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
484 if (credentials instanceof LoginPwUnencrypted) {
485 final LoginPasswordUnencrypted loginPassword =
486 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
487 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
489 if (credentials instanceof LoginPw) {
490 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
491 return new LoginPasswordHandler(loginPassword.getUsername(),
492 encryptionService.decrypt(loginPassword.getPassword()));
494 if (credentials instanceof KeyAuth) {
495 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
496 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
497 keystoreAdapter, encryptionService);
499 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
502 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
504 private InetSocketAddress getSocketAddress(final Host host, final int port) {
505 if (host.getDomainName() != null) {
506 return new InetSocketAddress(host.getDomainName().getValue(), port);
508 final IpAddress ipAddress = host.getIpAddress();
509 final String ip = ipAddress.getIpv4Address() != null
510 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
511 return new InetSocketAddress(ip, port);
515 private Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
516 // if none of yang-module-capabilities or non-module-capabilities is specified
517 // just return absent
518 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
519 return Optional.absent();
522 final List<String> capabilities = new ArrayList<>();
524 boolean overrideYangModuleCaps = false;
525 if (node.getYangModuleCapabilities() != null) {
526 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
527 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
530 //non-module capabilities should not exist in yang module capabilities
531 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
532 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
533 "List yang-module-capabilities/capability should contain only module based capabilities. "
534 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
536 boolean overrideNonModuleCaps = false;
537 if (node.getNonModuleCapabilities() != null) {
538 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
539 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
542 return Optional.of(new UserPreferences(NetconfSessionPreferences
543 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
546 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
547 private final Long connectionAttempts;
548 private final EventExecutor executor;
549 private final double sleepFactor;
550 private final int minSleep;
552 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
553 final int minSleep, final BigDecimal sleepFactor) {
554 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
555 connectionAttempts = maxConnectionAttempts;
557 connectionAttempts = null;
560 this.sleepFactor = sleepFactor.doubleValue();
561 this.executor = executor;
562 this.minSleep = minSleep;
566 public ReconnectStrategy createReconnectStrategy() {
567 final Long maxSleep = null;
568 final Long deadline = null;
570 return new TimedReconnectStrategy(executor, minSleep,
571 minSleep, sleepFactor, maxSleep, connectionAttempts, deadline);
575 protected static class NetconfConnectorDTO implements AutoCloseable {
577 private final NetconfDeviceCommunicator communicator;
578 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
580 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
581 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
582 this.communicator = communicator;
583 this.facade = facade;
586 public NetconfDeviceCommunicator getCommunicator() {
590 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
594 public NetconfClientSessionListener getSessionListener() {
599 public void close() {
600 communicator.close();
605 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
606 private final NetconfKeystoreAdapter keystoreAdapter;
607 private final Optional<Specification> specOptional;
609 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
610 this.keystoreAdapter = keystoreAdapter;
611 this.specOptional = Optional.fromNullable(specification);
615 public SslHandler createSslHandler() {
617 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
619 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
620 kmf.init(keyStore, "".toCharArray());
622 final TrustManagerFactory tmf =
623 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
626 final SSLContext sslCtx = SSLContext.getInstance("TLS");
627 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
628 final SSLEngine engine = sslCtx.createSSLEngine();
629 engine.setUseClientMode(true);
631 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
632 if (specOptional.isPresent()) {
633 final Specification specification = specOptional.get();
634 if (!(specification instanceof TlsCase)) {
635 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
637 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
640 engine.setEnabledProtocols(protocols.toArray(new String[0]));
641 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
642 engine.setEnableSessionCreation(true);
644 return new SslHandler(engine);
645 } catch (GeneralSecurityException | IOException exc) {
646 throw new IllegalStateException(exc);