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.ListeningExecutorService;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import com.google.common.util.concurrent.Uninterruptibles;
22 import io.netty.handler.ssl.SslHandler;
23 import io.netty.util.concurrent.EventExecutor;
25 import java.io.IOException;
26 import java.math.BigDecimal;
27 import java.net.InetSocketAddress;
29 import java.security.GeneralSecurityException;
30 import java.security.KeyStore;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
36 import java.util.concurrent.TimeUnit;
37 import javax.net.ssl.KeyManagerFactory;
38 import javax.net.ssl.SSLContext;
39 import javax.net.ssl.SSLEngine;
40 import javax.net.ssl.TrustManagerFactory;
41 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
42 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
43 import org.opendaylight.controller.config.threadpool.ThreadPool;
44 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
45 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
46 import org.opendaylight.netconf.api.NetconfMessage;
47 import org.opendaylight.netconf.client.NetconfClientDispatcher;
48 import org.opendaylight.netconf.client.NetconfClientSessionListener;
49 import org.opendaylight.netconf.client.SslHandlerFactory;
50 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
51 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
52 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
53 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
54 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
55 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
56 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
57 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
58 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
59 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
60 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
61 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
62 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
63 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
64 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
65 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
66 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
67 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
68 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
69 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
70 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
71 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
72 import org.opendaylight.netconf.topology.api.NetconfTopology;
73 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
74 import org.opendaylight.protocol.framework.ReconnectStrategy;
75 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
76 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
77 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
78 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.Specification;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.specification.TlsCase;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
91 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
92 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
93 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
94 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
95 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
96 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
97 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
98 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
99 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
100 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
101 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
102 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
103 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
104 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
105 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
106 import org.slf4j.Logger;
107 import org.slf4j.LoggerFactory;
109 public abstract class AbstractNetconfTopology implements NetconfTopology {
111 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
113 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
114 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
115 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
116 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
117 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
118 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
119 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
120 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
122 // constants related to Schema Cache(s)
124 * Filesystem based caches are stored relative to the cache directory.
126 private static final String CACHE_DIRECTORY = "cache";
129 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
131 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
134 * The qualified schema cache directory <code>cache/schema</code>.
136 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
137 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
140 * The name for the default schema repository.
142 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
145 * The default schema repository in the case that one is not specified.
147 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
148 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
150 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
151 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
154 * The default factory for creating <code>SchemaContext</code> instances.
156 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
157 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
160 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
161 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
162 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
163 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
164 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
165 * synchronization locks.
167 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
169 // Initializes default constant instances for the case when the default schema repository
170 // directory cache/schema is used.
172 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
173 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
174 DEFAULT_SCHEMA_CONTEXT_FACTORY,
175 new NetconfStateSchemasResolverImpl()));
176 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
177 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
178 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
181 * Create the default <code>FilesystemSchemaSourceCache</code>, which stores cached files
182 * in <code>cache/schema</code>. Try up to 3 times - we've seen intermittent failures on jenkins where
183 * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race
184 * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this
185 * scenario, a retry should succeed.
190 FilesystemSchemaSourceCache<YangTextSchemaSource> defaultCache =
191 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
192 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
193 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache);
195 } catch (IllegalArgumentException e) {
197 LOG.error("Error creating default schema cache", e);
200 Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
205 private final NetconfClientDispatcher clientDispatcher;
206 private final EventExecutor eventExecutor;
207 private final DeviceActionFactory deviceActionFactory;
208 private final NetconfKeystoreAdapter keystoreAdapter;
209 protected final ScheduledThreadPool keepaliveExecutor;
210 protected final ListeningExecutorService processingExecutor;
211 protected final SharedSchemaRepository sharedSchemaRepository;
212 protected final DataBroker dataBroker;
213 protected final DOMMountPointService mountPointService;
214 protected final String topologyId;
215 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
216 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
217 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
218 protected String privateKeyPath;
219 protected String privateKeyPassphrase;
220 protected final AAAEncryptionService encryptionService;
221 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
223 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
224 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
225 final ThreadPool processingExecutor,
226 final SchemaRepositoryProvider schemaRepositoryProvider,
227 final DataBroker dataBroker, final DOMMountPointService mountPointService,
228 final AAAEncryptionService encryptionService,
229 final DeviceActionFactory deviceActionFactory) {
230 this.topologyId = topologyId;
231 this.clientDispatcher = clientDispatcher;
232 this.eventExecutor = eventExecutor;
233 this.keepaliveExecutor = keepaliveExecutor;
234 this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
235 this.deviceActionFactory = deviceActionFactory;
236 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
237 this.dataBroker = dataBroker;
238 this.mountPointService = mountPointService;
239 this.encryptionService = encryptionService;
241 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
244 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
245 this.schemaRegistry = schemaRegistry;
248 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
249 this.schemaContextFactory = schemaContextFactory;
253 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
254 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
255 return setupConnection(nodeId, configNode);
259 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
260 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
261 if (!activeConnectors.containsKey(nodeId)) {
262 return Futures.immediateFailedFuture(
263 new IllegalStateException("Unable to disconnect device that is not connected"));
266 // retrieve connection, and disconnect it
267 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
268 connectorDTO.getCommunicator().close();
269 connectorDTO.getFacade().close();
270 return Futures.immediateFuture(null);
273 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
274 final Node configNode) {
275 final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
277 Preconditions.checkNotNull(netconfNode.getHost());
278 Preconditions.checkNotNull(netconfNode.getPort());
279 Preconditions.checkNotNull(netconfNode.isTcpOnly());
281 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
282 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
283 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
284 final NetconfReconnectingClientConfiguration clientConfig =
285 getClientConfig(netconfClientSessionListener, netconfNode);
286 final ListenableFuture<NetconfDeviceCapabilities> future =
287 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
289 activeConnectors.put(nodeId, deviceCommunicatorDTO);
291 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
293 public void onSuccess(final NetconfDeviceCapabilities result) {
294 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
298 public void onFailure(final Throwable throwable) {
299 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
300 // remove this node from active connectors?
302 }, MoreExecutors.directExecutor());
307 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
308 //setup default values since default value is not supported in mdsal
309 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
310 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
311 final long keepaliveDelay = node.getKeepaliveDelay() == null
312 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
313 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
314 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
316 final IpAddress ipAddress = node.getHost().getIpAddress();
317 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
318 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
319 node.getPort().getValue());
320 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
322 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
323 createSalFacade(remoteDeviceId);
325 if (keepaliveDelay > 0) {
326 LOG.warn("Adding keepalive facade, for device {}", nodeId);
327 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
328 keepaliveDelay, defaultRequestTimeoutMillis);
331 // pre register yang library sources as fallback schemas to schema registry
332 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
333 if (node.getYangLibrary() != null) {
334 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
335 final String yangLibUsername = node.getYangLibrary().getUsername();
336 final String yangLigPassword = node.getYangLibrary().getPassword();
338 final LibraryModulesSchemas libraryModulesSchemas;
339 if (yangLibURL != null) {
340 if (yangLibUsername != null && yangLigPassword != null) {
341 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
343 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
346 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
347 : libraryModulesSchemas.getAvailableModels().entrySet()) {
348 registeredYangLibSources
349 .add(schemaRegistry.registerSchemaSource(
350 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
351 libraryModulesSchemas.getAvailableModels()),
352 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
353 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
358 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
359 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
360 if (node.isSchemaless()) {
361 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
363 NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
364 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
365 .setSchemaResourcesDTO(schemaResourcesDTO)
366 .setGlobalProcessingExecutor(this.processingExecutor)
367 .setId(remoteDeviceId)
368 .setSalFacade(salFacade);
369 if (this.deviceActionFactory != null) {
370 netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
372 device = netconfDeviceBuilder.build();
375 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
376 final int rpcMessageLimit =
377 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
379 if (rpcMessageLimit < 1) {
380 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
383 NetconfDeviceCommunicator netconfDeviceCommunicator =
384 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
385 userCapabilities.get(), rpcMessageLimit)
386 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
388 if (salFacade instanceof KeepaliveSalFacade) {
389 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
391 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
394 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
395 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
396 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
397 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
398 // Only checks to ensure the String is not empty or null; further checks related to directory
399 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
400 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
401 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
402 // the SchemaRegistry and SchemaContextFactory remain the default values.
403 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
404 // Multiple modules may be created at once;
405 // synchronize to avoid issues with data consistency among threads.
406 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
407 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
408 // if they already exist
409 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
410 if (schemaResourcesDTO == null) {
411 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
412 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
413 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
414 schemaResourcesDTO.getSchemaRegistry())
416 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
419 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
420 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
423 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
424 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
427 if (schemaResourcesDTO == null) {
428 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
429 schemaContextFactory, new NetconfStateSchemasResolverImpl());
432 return schemaResourcesDTO;
436 * Creates the backing Schema classes for a particular directory.
438 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
439 * @return A DTO containing the Schema classes for the Netconf mount.
441 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
442 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
443 final SchemaContextFactory contextFactory
444 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
445 setSchemaRegistry(repository);
446 setSchemaContextFactory(contextFactory);
447 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
448 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
449 repository.registerSchemaSourceListener(deviceCache);
450 repository.registerSchemaSourceListener(
451 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
452 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
453 new NetconfStateSchemasResolverImpl());
457 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
459 * @param schemaCacheDirectory The custom cache directory relative to "cache"
460 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
462 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
463 final String schemaCacheDirectory) {
464 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
465 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
466 new File(relativeSchemaCacheDirectory));
470 * Sets the private key path from location specified in configuration file using blueprint.
472 public void setPrivateKeyPath(final String privateKeyPath) {
473 this.privateKeyPath = privateKeyPath;
477 * Sets the private key passphrase from location specified in configuration file using blueprint.
479 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
480 this.privateKeyPassphrase = privateKeyPassphrase;
483 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
484 final NetconfNode node) {
486 //setup default values since default value is not supported in mdsal
487 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
488 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
489 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
490 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
491 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
492 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
493 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
495 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
497 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
498 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
499 final ReconnectStrategy strategy = sf.createReconnectStrategy();
501 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder =
502 NetconfReconnectingClientConfigurationBuilder.create();
504 if (node.isTcpOnly() || node.getProtocol() == null || node.getProtocol().getName() == Name.SSH) {
505 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
506 reconnectingClientConfigurationBuilder
507 .withAuthHandler(authHandler)
508 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
509 NetconfClientConfiguration.NetconfClientProtocol.SSH);
510 } else if (node.getProtocol().getName() == Name.TLS) {
511 final SslHandlerFactory sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter,
512 node.getProtocol().getSpecification());
513 reconnectingClientConfigurationBuilder
514 .withSslHandlerFactory(sslHandlerFactory)
515 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
517 throw new IllegalStateException("Unsupported protocol type: " + node.getProtocol().getName().getClass());
519 if (node.getOdlHelloMessageCapabilities() != null) {
520 reconnectingClientConfigurationBuilder
521 .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
524 return reconnectingClientConfigurationBuilder
525 .withAddress(socketAddress)
526 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
527 .withReconnectStrategy(strategy)
528 .withConnectStrategyFactory(sf)
529 .withSessionListener(listener)
533 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
534 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
535 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
536 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
537 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
538 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
539 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
540 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
542 if (credentials instanceof LoginPwUnencrypted) {
543 final LoginPasswordUnencrypted loginPassword =
544 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
545 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
547 if (credentials instanceof LoginPw) {
548 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
549 return new LoginPasswordHandler(loginPassword.getUsername(),
550 encryptionService.decrypt(loginPassword.getPassword()));
552 if (credentials instanceof KeyAuth) {
553 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
554 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
555 keystoreAdapter, encryptionService);
557 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
560 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
562 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
563 if (host.getDomainName() != null) {
564 return new InetSocketAddress(host.getDomainName().getValue(), port);
567 final IpAddress ipAddress = host.getIpAddress();
568 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
569 : ipAddress.getIpv6Address().getValue();
570 return new InetSocketAddress(ip, port);
573 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
574 // if none of yang-module-capabilities or non-module-capabilities is specified
575 // just return absent
576 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
577 return Optional.absent();
580 final List<String> capabilities = new ArrayList<>();
582 boolean overrideYangModuleCaps = false;
583 if (node.getYangModuleCapabilities() != null) {
584 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
585 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
588 //non-module capabilities should not exist in yang module capabilities
589 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
590 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
591 "List yang-module-capabilities/capability should contain only module based capabilities. "
592 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
594 boolean overrideNonModuleCaps = false;
595 if (node.getNonModuleCapabilities() != null) {
596 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
597 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
600 return Optional.of(new UserPreferences(NetconfSessionPreferences
601 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
604 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
605 private final Long connectionAttempts;
606 private final EventExecutor executor;
607 private final double sleepFactor;
608 private final int minSleep;
610 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
611 final int minSleep, final BigDecimal sleepFactor) {
612 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
613 connectionAttempts = maxConnectionAttempts;
615 connectionAttempts = null;
618 this.sleepFactor = sleepFactor.doubleValue();
619 this.executor = executor;
620 this.minSleep = minSleep;
624 public ReconnectStrategy createReconnectStrategy() {
625 return new TimedReconnectStrategy(executor, minSleep,
626 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
630 protected static class NetconfConnectorDTO implements AutoCloseable {
632 private final NetconfDeviceCommunicator communicator;
633 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
635 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
636 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
637 this.communicator = communicator;
638 this.facade = facade;
641 public NetconfDeviceCommunicator getCommunicator() {
645 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
649 public NetconfClientSessionListener getSessionListener() {
654 public void close() {
655 communicator.close();
660 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
661 private final NetconfKeystoreAdapter keystoreAdapter;
662 private final Optional<Specification> specOptional;
664 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
665 this.keystoreAdapter = keystoreAdapter;
666 this.specOptional = Optional.fromNullable(specification);
670 public SslHandler createSslHandler() {
672 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
674 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
675 kmf.init(keyStore, "".toCharArray());
677 final TrustManagerFactory tmf =
678 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
681 final SSLContext sslCtx = SSLContext.getInstance("TLS");
682 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
683 final SSLEngine engine = sslCtx.createSSLEngine();
684 engine.setUseClientMode(true);
686 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
687 if (specOptional.isPresent()) {
688 final Specification specification = specOptional.get();
689 if (!(specification instanceof TlsCase)) {
690 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
692 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
695 engine.setEnabledProtocols(protocols.toArray(new String[0]));
696 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
697 engine.setEnableSessionCreation(true);
699 return new SslHandler(engine);
700 } catch (GeneralSecurityException | IOException exc) {
701 throw new IllegalStateException(exc);