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.Name;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.Specification;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.specification.TlsCase;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
87 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
88 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
89 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
90 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
91 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
92 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
93 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
94 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
95 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
96 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
97 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
98 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
99 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
100 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
101 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
102 import org.slf4j.Logger;
103 import org.slf4j.LoggerFactory;
105 public abstract class AbstractNetconfTopology implements NetconfTopology {
107 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
109 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
110 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
111 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
112 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
113 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
114 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
115 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
116 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
118 // constants related to Schema Cache(s)
120 * Filesystem based caches are stored relative to the cache directory.
122 private static final String CACHE_DIRECTORY = "cache";
125 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
127 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
130 * The qualified schema cache directory <code>cache/schema</code>.
132 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
133 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
136 * The name for the default schema repository.
138 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
141 * The default schema repository in the case that one is not specified.
143 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
144 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
147 * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
149 private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
150 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
151 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
153 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
154 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
157 * The default factory for creating <code>SchemaContext</code> instances.
159 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
160 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
163 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
164 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
165 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
166 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
167 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
168 * synchronization locks.
170 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
172 // Initializes default constant instances for the case when the default schema repository
173 // directory cache/schema is used.
175 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
176 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
177 DEFAULT_SCHEMA_CONTEXT_FACTORY,
178 new NetconfStateSchemasResolverImpl()));
179 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
180 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
181 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
182 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
185 protected final String topologyId;
186 private final NetconfClientDispatcher clientDispatcher;
187 private final EventExecutor eventExecutor;
188 protected final ScheduledThreadPool keepaliveExecutor;
189 protected final ThreadPool processingExecutor;
190 protected final SharedSchemaRepository sharedSchemaRepository;
191 protected final DataBroker dataBroker;
192 protected final DOMMountPointService mountPointService;
193 private final NetconfKeystoreAdapter keystoreAdapter;
194 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
195 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
196 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
197 protected String privateKeyPath;
198 protected String privateKeyPassphrase;
199 protected final AAAEncryptionService encryptionService;
200 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
202 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
203 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
204 final ThreadPool processingExecutor,
205 final SchemaRepositoryProvider schemaRepositoryProvider,
206 final DataBroker dataBroker, final DOMMountPointService mountPointService,
207 final AAAEncryptionService encryptionService) {
208 this.topologyId = topologyId;
209 this.clientDispatcher = clientDispatcher;
210 this.eventExecutor = eventExecutor;
211 this.keepaliveExecutor = keepaliveExecutor;
212 this.processingExecutor = processingExecutor;
213 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
214 this.dataBroker = dataBroker;
215 this.mountPointService = mountPointService;
216 this.encryptionService = encryptionService;
218 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
221 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
222 this.schemaRegistry = schemaRegistry;
225 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
226 this.schemaContextFactory = schemaContextFactory;
230 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
231 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
232 return setupConnection(nodeId, configNode);
236 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
237 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
238 if (!activeConnectors.containsKey(nodeId)) {
239 return Futures.immediateFailedFuture(
240 new IllegalStateException("Unable to disconnect device that is not connected"));
243 // retrieve connection, and disconnect it
244 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
245 connectorDTO.getCommunicator().close();
246 connectorDTO.getFacade().close();
247 return Futures.immediateFuture(null);
250 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
251 final Node configNode) {
252 final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
254 Preconditions.checkNotNull(netconfNode.getHost());
255 Preconditions.checkNotNull(netconfNode.getPort());
256 Preconditions.checkNotNull(netconfNode.isTcpOnly());
258 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
259 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
260 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
261 final NetconfReconnectingClientConfiguration clientConfig =
262 getClientConfig(netconfClientSessionListener, netconfNode);
263 final ListenableFuture<NetconfDeviceCapabilities> future =
264 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
266 activeConnectors.put(nodeId, deviceCommunicatorDTO);
268 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
270 public void onSuccess(final NetconfDeviceCapabilities result) {
271 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
275 public void onFailure(final Throwable throwable) {
276 LOG.error("Connector for : " + nodeId.getValue() + " failed");
277 // remove this node from active connectors?
279 }, MoreExecutors.directExecutor());
284 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
285 final NetconfNode node) {
286 //setup default values since default value is not supported in mdsal
287 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
288 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
289 final long keepaliveDelay = node.getKeepaliveDelay() == null
290 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
291 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
292 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
294 final IpAddress ipAddress = node.getHost().getIpAddress();
295 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
296 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
297 node.getPort().getValue());
298 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
300 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
301 createSalFacade(remoteDeviceId);
303 if (keepaliveDelay > 0) {
304 LOG.warn("Adding keepalive facade, for device {}", nodeId);
305 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
306 keepaliveDelay, defaultRequestTimeoutMillis);
309 // pre register yang library sources as fallback schemas to schema registry
310 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
311 if (node.getYangLibrary() != null) {
312 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
313 final String yangLibUsername = node.getYangLibrary().getUsername();
314 final String yangLigPassword = node.getYangLibrary().getPassword();
316 final LibraryModulesSchemas libraryModulesSchemas;
317 if (yangLibURL != null) {
318 if (yangLibUsername != null && yangLigPassword != null) {
319 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
321 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
324 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
325 : libraryModulesSchemas.getAvailableModels().entrySet()) {
326 registeredYangLibSources
327 .add(schemaRegistry.registerSchemaSource(
328 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
329 libraryModulesSchemas.getAvailableModels()),
330 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
331 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
336 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
337 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
338 if (node.isSchemaless()) {
339 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
341 device = new NetconfDeviceBuilder()
342 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
343 .setSchemaResourcesDTO(schemaResourcesDTO)
344 .setGlobalProcessingExecutor(processingExecutor.getExecutor())
345 .setId(remoteDeviceId)
346 .setSalFacade(salFacade)
350 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
351 final int rpcMessageLimit =
352 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
354 if (rpcMessageLimit < 1) {
355 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
358 return new NetconfConnectorDTO(userCapabilities.isPresent()
359 ? new NetconfDeviceCommunicator(remoteDeviceId, device, userCapabilities.get(), rpcMessageLimit)
360 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade);
363 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
364 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
365 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
366 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
367 // Only checks to ensure the String is not empty or null; further checks related to directory
368 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
369 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
370 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
371 // the SchemaRegistry and SchemaContextFactory remain the default values.
372 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
373 // Multiple modules may be created at once;
374 // synchronize to avoid issues with data consistency among threads.
375 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
376 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
377 // if they already exist
378 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
379 if (schemaResourcesDTO == null) {
380 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
381 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
382 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
383 schemaResourcesDTO.getSchemaRegistry())
385 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
388 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
389 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
392 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
393 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
396 if (schemaResourcesDTO == null) {
397 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
398 schemaContextFactory, new NetconfStateSchemasResolverImpl());
401 return schemaResourcesDTO;
405 * Creates the backing Schema classes for a particular directory.
407 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
408 * @return A DTO containing the Schema classes for the Netconf mount.
410 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
411 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
412 final SchemaContextFactory contextFactory
413 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
414 setSchemaRegistry(repository);
415 setSchemaContextFactory(contextFactory);
416 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
417 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
418 repository.registerSchemaSourceListener(deviceCache);
419 repository.registerSchemaSourceListener(
420 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
421 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
422 new NetconfStateSchemasResolverImpl());
426 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
428 * @param schemaCacheDirectory The custom cache directory relative to "cache"
429 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
431 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
432 final String schemaCacheDirectory) {
433 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
434 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
435 new File(relativeSchemaCacheDirectory));
439 * Sets the private key path from location specified in configuration file using blueprint.
441 public void setPrivateKeyPath(final String privateKeyPath) {
442 this.privateKeyPath = privateKeyPath;
446 * Sets the private key passphrase from location specified in configuration file using blueprint.
448 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
449 this.privateKeyPassphrase = privateKeyPassphrase;
452 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
453 final NetconfNode node) {
455 //setup default values since default value is not supported in mdsal
456 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
457 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
458 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
459 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
460 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
461 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
462 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
464 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
466 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
467 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
468 final ReconnectStrategy strategy = sf.createReconnectStrategy();
470 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder =
471 NetconfReconnectingClientConfigurationBuilder.create();
473 if (node.isTcpOnly() || node.getProtocol() == null || node.getProtocol().getName() == Name.SSH) {
474 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
475 reconnectingClientConfigurationBuilder
476 .withAuthHandler(authHandler)
477 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
478 NetconfClientConfiguration.NetconfClientProtocol.SSH);
479 } else if (node.getProtocol().getName() == Name.TLS) {
480 final SslHandlerFactory sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter,
481 node.getProtocol().getSpecification());
482 reconnectingClientConfigurationBuilder
483 .withSslHandlerFactory(sslHandlerFactory)
484 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
486 throw new IllegalStateException("Unsupported protocol type: " + node.getProtocol().getName().getClass());
489 return reconnectingClientConfigurationBuilder
490 .withAddress(socketAddress)
491 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
492 .withReconnectStrategy(strategy)
493 .withConnectStrategyFactory(sf)
494 .withSessionListener(listener)
498 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
499 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
500 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
501 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
502 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
503 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
504 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
505 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
507 if (credentials instanceof LoginPwUnencrypted) {
508 final LoginPasswordUnencrypted loginPassword =
509 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
510 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
512 if (credentials instanceof LoginPw) {
513 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
514 return new LoginPasswordHandler(loginPassword.getUsername(),
515 encryptionService.decrypt(loginPassword.getPassword()));
517 if (credentials instanceof KeyAuth) {
518 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
519 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
520 keystoreAdapter, encryptionService);
522 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
525 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
527 private InetSocketAddress getSocketAddress(final Host host, final int port) {
528 if (host.getDomainName() != null) {
529 return new InetSocketAddress(host.getDomainName().getValue(), port);
531 final IpAddress ipAddress = host.getIpAddress();
532 final String ip = ipAddress.getIpv4Address() != null
533 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
534 return new InetSocketAddress(ip, port);
538 private Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
539 // if none of yang-module-capabilities or non-module-capabilities is specified
540 // just return absent
541 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
542 return Optional.absent();
545 final List<String> capabilities = new ArrayList<>();
547 boolean overrideYangModuleCaps = false;
548 if (node.getYangModuleCapabilities() != null) {
549 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
550 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
553 //non-module capabilities should not exist in yang module capabilities
554 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
555 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
556 "List yang-module-capabilities/capability should contain only module based capabilities. "
557 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
559 boolean overrideNonModuleCaps = false;
560 if (node.getNonModuleCapabilities() != null) {
561 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
562 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
565 return Optional.of(new UserPreferences(NetconfSessionPreferences
566 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
569 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
570 private final Long connectionAttempts;
571 private final EventExecutor executor;
572 private final double sleepFactor;
573 private final int minSleep;
575 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
576 final int minSleep, final BigDecimal sleepFactor) {
577 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
578 connectionAttempts = maxConnectionAttempts;
580 connectionAttempts = null;
583 this.sleepFactor = sleepFactor.doubleValue();
584 this.executor = executor;
585 this.minSleep = minSleep;
589 public ReconnectStrategy createReconnectStrategy() {
590 return new TimedReconnectStrategy(executor, minSleep,
591 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
595 protected static class NetconfConnectorDTO implements AutoCloseable {
597 private final NetconfDeviceCommunicator communicator;
598 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
600 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
601 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
602 this.communicator = communicator;
603 this.facade = facade;
606 public NetconfDeviceCommunicator getCommunicator() {
610 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
614 public NetconfClientSessionListener getSessionListener() {
619 public void close() {
620 communicator.close();
625 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
626 private final NetconfKeystoreAdapter keystoreAdapter;
627 private final Optional<Specification> specOptional;
629 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
630 this.keystoreAdapter = keystoreAdapter;
631 this.specOptional = Optional.fromNullable(specification);
635 public SslHandler createSslHandler() {
637 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
639 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
640 kmf.init(keyStore, "".toCharArray());
642 final TrustManagerFactory tmf =
643 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
646 final SSLContext sslCtx = SSLContext.getInstance("TLS");
647 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
648 final SSLEngine engine = sslCtx.createSSLEngine();
649 engine.setUseClientMode(true);
651 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
652 if (specOptional.isPresent()) {
653 final Specification specification = specOptional.get();
654 if (!(specification instanceof TlsCase)) {
655 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
657 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
660 engine.setEnabledProtocols(protocols.toArray(new String[0]));
661 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
662 engine.setEnableSessionCreation(true);
664 return new SslHandler(engine);
665 } catch (GeneralSecurityException | IOException exc) {
666 throw new IllegalStateException(exc);