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.parser.repo.SharedSchemaRepository;
99 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
100 import org.slf4j.Logger;
101 import org.slf4j.LoggerFactory;
103 public abstract class AbstractNetconfTopology implements NetconfTopology {
105 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
107 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
108 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
109 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
110 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
111 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
112 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
113 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
114 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
116 // constants related to Schema Cache(s)
118 * Filesystem based caches are stored relative to the cache directory.
120 private static final String CACHE_DIRECTORY = "cache";
123 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
125 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
128 * The qualified schema cache directory <code>cache/schema</code>.
130 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
131 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
134 * The name for the default schema repository.
136 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
139 * The default schema repository in the case that one is not specified.
141 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
142 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
145 * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
147 private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
148 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
149 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
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_CACHE);
175 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
176 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
179 protected final String topologyId;
180 private final NetconfClientDispatcher clientDispatcher;
181 private final EventExecutor eventExecutor;
182 protected final ScheduledThreadPool keepaliveExecutor;
183 protected final ThreadPool processingExecutor;
184 protected final SharedSchemaRepository sharedSchemaRepository;
185 protected final DataBroker dataBroker;
186 protected final DOMMountPointService mountPointService;
187 private final NetconfKeystoreAdapter keystoreAdapter;
188 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
189 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
190 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
191 protected String privateKeyPath;
192 protected String privateKeyPassphrase;
193 protected final AAAEncryptionService encryptionService;
194 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
196 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
197 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
198 final ThreadPool processingExecutor,
199 final SchemaRepositoryProvider schemaRepositoryProvider,
200 final DataBroker dataBroker, final DOMMountPointService mountPointService,
201 final AAAEncryptionService encryptionService) {
202 this.topologyId = topologyId;
203 this.clientDispatcher = clientDispatcher;
204 this.eventExecutor = eventExecutor;
205 this.keepaliveExecutor = keepaliveExecutor;
206 this.processingExecutor = processingExecutor;
207 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
208 this.dataBroker = dataBroker;
209 this.mountPointService = mountPointService;
210 this.encryptionService = encryptionService;
212 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
215 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
216 this.schemaRegistry = schemaRegistry;
219 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
220 this.schemaContextFactory = schemaContextFactory;
224 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
225 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
226 return setupConnection(nodeId, configNode);
230 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
231 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
232 if (!activeConnectors.containsKey(nodeId)) {
233 return Futures.immediateFailedFuture(
234 new IllegalStateException("Unable to disconnect device that is not connected"));
237 // retrieve connection, and disconnect it
238 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
239 connectorDTO.getCommunicator().close();
240 connectorDTO.getFacade().close();
241 return Futures.immediateFuture(null);
244 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
245 final Node configNode) {
246 final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
248 Preconditions.checkNotNull(netconfNode.getHost());
249 Preconditions.checkNotNull(netconfNode.getPort());
250 Preconditions.checkNotNull(netconfNode.isTcpOnly());
252 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
253 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
254 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
255 final NetconfReconnectingClientConfiguration clientConfig =
256 getClientConfig(netconfClientSessionListener, netconfNode);
257 final ListenableFuture<NetconfDeviceCapabilities> future =
258 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
260 activeConnectors.put(nodeId, deviceCommunicatorDTO);
262 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
264 public void onSuccess(final NetconfDeviceCapabilities result) {
265 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
269 public void onFailure(final Throwable throwable) {
270 LOG.error("Connector for : " + nodeId.getValue() + " failed");
271 // remove this node from active connectors?
273 }, MoreExecutors.directExecutor());
278 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
279 final NetconfNode node) {
280 //setup default values since default value is not supported in mdsal
281 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
282 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
283 final long keepaliveDelay = node.getKeepaliveDelay() == null
284 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
285 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
286 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
288 final IpAddress ipAddress = node.getHost().getIpAddress();
289 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
290 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
291 node.getPort().getValue());
292 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
294 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
295 createSalFacade(remoteDeviceId);
297 if (keepaliveDelay > 0) {
298 LOG.warn("Adding keepalive facade, for device {}", nodeId);
299 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
300 keepaliveDelay, defaultRequestTimeoutMillis);
303 // pre register yang library sources as fallback schemas to schema registry
304 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
305 if (node.getYangLibrary() != null) {
306 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
307 final String yangLibUsername = node.getYangLibrary().getUsername();
308 final String yangLigPassword = node.getYangLibrary().getPassword();
310 final LibraryModulesSchemas libraryModulesSchemas;
311 if (yangLibURL != null) {
312 if (yangLibUsername != null && yangLigPassword != null) {
313 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
315 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
318 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
319 : libraryModulesSchemas.getAvailableModels().entrySet()) {
320 registeredYangLibSources
321 .add(schemaRegistry.registerSchemaSource(
322 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
323 libraryModulesSchemas.getAvailableModels()),
324 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
325 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
330 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
331 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
332 if (node.isSchemaless()) {
333 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
335 device = new NetconfDeviceBuilder()
336 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
337 .setSchemaResourcesDTO(schemaResourcesDTO)
338 .setGlobalProcessingExecutor(processingExecutor.getExecutor())
339 .setId(remoteDeviceId)
340 .setSalFacade(salFacade)
344 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
345 final int rpcMessageLimit =
346 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
348 if (rpcMessageLimit < 1) {
349 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
352 return new NetconfConnectorDTO(userCapabilities.isPresent()
353 ? new NetconfDeviceCommunicator(remoteDeviceId, device, userCapabilities.get(), rpcMessageLimit)
354 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade);
357 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
358 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
359 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
360 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
361 // Only checks to ensure the String is not empty or null; further checks related to directory
362 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
363 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
364 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
365 // the SchemaRegistry and SchemaContextFactory remain the default values.
366 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
367 // Multiple modules may be created at once;
368 // synchronize to avoid issues with data consistency among threads.
369 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
370 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
371 // if they already exist
372 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
373 if (schemaResourcesDTO == null) {
374 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
375 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
376 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
377 schemaResourcesDTO.getSchemaRegistry())
379 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
382 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
383 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
386 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
387 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
390 if (schemaResourcesDTO == null) {
391 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
392 schemaContextFactory, new NetconfStateSchemasResolverImpl());
395 return schemaResourcesDTO;
399 * Creates the backing Schema classes for a particular directory.
401 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
402 * @return A DTO containing the Schema classes for the Netconf mount.
404 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
405 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
406 final SchemaContextFactory contextFactory
407 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
408 setSchemaRegistry(repository);
409 setSchemaContextFactory(contextFactory);
410 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
411 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
412 repository.registerSchemaSourceListener(deviceCache);
413 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
414 new NetconfStateSchemasResolverImpl());
418 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
420 * @param schemaCacheDirectory The custom cache directory relative to "cache"
421 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
423 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
424 final String schemaCacheDirectory) {
425 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
426 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
427 new File(relativeSchemaCacheDirectory));
431 * Sets the private key path from location specified in configuration file using blueprint.
433 public void setPrivateKeyPath(final String privateKeyPath) {
434 this.privateKeyPath = privateKeyPath;
438 * Sets the private key passphrase from location specified in configuration file using blueprint.
440 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
441 this.privateKeyPassphrase = privateKeyPassphrase;
444 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
445 final NetconfNode node) {
447 //setup default values since default value is not supported in mdsal
448 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
449 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
450 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
451 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
452 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
453 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
454 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
456 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
458 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
459 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
460 final ReconnectStrategy strategy = sf.createReconnectStrategy();
462 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder =
463 NetconfReconnectingClientConfigurationBuilder.create();
465 if (node.isTcpOnly() || node.getProtocol() == null || node.getProtocol().getName() == Name.SSH) {
466 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
467 reconnectingClientConfigurationBuilder
468 .withAuthHandler(authHandler)
469 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
470 NetconfClientConfiguration.NetconfClientProtocol.SSH);
471 } else if (node.getProtocol().getName() == Name.TLS) {
472 final SslHandlerFactory sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter,
473 node.getProtocol().getSpecification());
474 reconnectingClientConfigurationBuilder
475 .withSslHandlerFactory(sslHandlerFactory)
476 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
478 throw new IllegalStateException("Unsupported protocol type: " + node.getProtocol().getName().getClass());
481 return reconnectingClientConfigurationBuilder
482 .withAddress(socketAddress)
483 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
484 .withReconnectStrategy(strategy)
485 .withConnectStrategyFactory(sf)
486 .withSessionListener(listener)
490 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
491 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
492 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
493 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
494 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
495 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
496 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
497 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
499 if (credentials instanceof LoginPwUnencrypted) {
500 final LoginPasswordUnencrypted loginPassword =
501 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
502 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
504 if (credentials instanceof LoginPw) {
505 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
506 return new LoginPasswordHandler(loginPassword.getUsername(),
507 encryptionService.decrypt(loginPassword.getPassword()));
509 if (credentials instanceof KeyAuth) {
510 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
511 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
512 keystoreAdapter, encryptionService);
514 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
517 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
519 private InetSocketAddress getSocketAddress(final Host host, final int port) {
520 if (host.getDomainName() != null) {
521 return new InetSocketAddress(host.getDomainName().getValue(), port);
523 final IpAddress ipAddress = host.getIpAddress();
524 final String ip = ipAddress.getIpv4Address() != null
525 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
526 return new InetSocketAddress(ip, port);
530 private Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
531 // if none of yang-module-capabilities or non-module-capabilities is specified
532 // just return absent
533 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
534 return Optional.absent();
537 final List<String> capabilities = new ArrayList<>();
539 boolean overrideYangModuleCaps = false;
540 if (node.getYangModuleCapabilities() != null) {
541 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
542 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
545 //non-module capabilities should not exist in yang module capabilities
546 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
547 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
548 "List yang-module-capabilities/capability should contain only module based capabilities. "
549 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
551 boolean overrideNonModuleCaps = false;
552 if (node.getNonModuleCapabilities() != null) {
553 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
554 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
557 return Optional.of(new UserPreferences(NetconfSessionPreferences
558 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
561 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
562 private final Long connectionAttempts;
563 private final EventExecutor executor;
564 private final double sleepFactor;
565 private final int minSleep;
567 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
568 final int minSleep, final BigDecimal sleepFactor) {
569 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
570 connectionAttempts = maxConnectionAttempts;
572 connectionAttempts = null;
575 this.sleepFactor = sleepFactor.doubleValue();
576 this.executor = executor;
577 this.minSleep = minSleep;
581 public ReconnectStrategy createReconnectStrategy() {
582 return new TimedReconnectStrategy(executor, minSleep,
583 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
587 protected static class NetconfConnectorDTO implements AutoCloseable {
589 private final NetconfDeviceCommunicator communicator;
590 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
592 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
593 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
594 this.communicator = communicator;
595 this.facade = facade;
598 public NetconfDeviceCommunicator getCommunicator() {
602 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
606 public NetconfClientSessionListener getSessionListener() {
611 public void close() {
612 communicator.close();
617 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
618 private final NetconfKeystoreAdapter keystoreAdapter;
619 private final Optional<Specification> specOptional;
621 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
622 this.keystoreAdapter = keystoreAdapter;
623 this.specOptional = Optional.fromNullable(specification);
627 public SslHandler createSslHandler() {
629 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
631 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
632 kmf.init(keyStore, "".toCharArray());
634 final TrustManagerFactory tmf =
635 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
638 final SSLContext sslCtx = SSLContext.getInstance("TLS");
639 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
640 final SSLEngine engine = sslCtx.createSSLEngine();
641 engine.setUseClientMode(true);
643 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
644 if (specOptional.isPresent()) {
645 final Specification specification = specOptional.get();
646 if (!(specification instanceof TlsCase)) {
647 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
649 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
652 engine.setEnabledProtocols(protocols.toArray(new String[0]));
653 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
654 engine.setEnableSessionCreation(true);
656 return new SslHandler(engine);
657 } catch (GeneralSecurityException | IOException exc) {
658 throw new IllegalStateException(exc);