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());
520 return reconnectingClientConfigurationBuilder
521 .withAddress(socketAddress)
522 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
523 .withReconnectStrategy(strategy)
524 .withConnectStrategyFactory(sf)
525 .withSessionListener(listener)
529 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
530 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
531 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
532 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
533 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
534 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
535 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
536 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
538 if (credentials instanceof LoginPwUnencrypted) {
539 final LoginPasswordUnencrypted loginPassword =
540 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
541 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
543 if (credentials instanceof LoginPw) {
544 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
545 return new LoginPasswordHandler(loginPassword.getUsername(),
546 encryptionService.decrypt(loginPassword.getPassword()));
548 if (credentials instanceof KeyAuth) {
549 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
550 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
551 keystoreAdapter, encryptionService);
553 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
556 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
558 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
559 if (host.getDomainName() != null) {
560 return new InetSocketAddress(host.getDomainName().getValue(), port);
563 final IpAddress ipAddress = host.getIpAddress();
564 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
565 : ipAddress.getIpv6Address().getValue();
566 return new InetSocketAddress(ip, port);
569 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
570 // if none of yang-module-capabilities or non-module-capabilities is specified
571 // just return absent
572 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
573 return Optional.absent();
576 final List<String> capabilities = new ArrayList<>();
578 boolean overrideYangModuleCaps = false;
579 if (node.getYangModuleCapabilities() != null) {
580 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
581 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
584 //non-module capabilities should not exist in yang module capabilities
585 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
586 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
587 "List yang-module-capabilities/capability should contain only module based capabilities. "
588 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
590 boolean overrideNonModuleCaps = false;
591 if (node.getNonModuleCapabilities() != null) {
592 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
593 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
596 return Optional.of(new UserPreferences(NetconfSessionPreferences
597 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
600 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
601 private final Long connectionAttempts;
602 private final EventExecutor executor;
603 private final double sleepFactor;
604 private final int minSleep;
606 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
607 final int minSleep, final BigDecimal sleepFactor) {
608 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
609 connectionAttempts = maxConnectionAttempts;
611 connectionAttempts = null;
614 this.sleepFactor = sleepFactor.doubleValue();
615 this.executor = executor;
616 this.minSleep = minSleep;
620 public ReconnectStrategy createReconnectStrategy() {
621 return new TimedReconnectStrategy(executor, minSleep,
622 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
626 protected static class NetconfConnectorDTO implements AutoCloseable {
628 private final NetconfDeviceCommunicator communicator;
629 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
631 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
632 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
633 this.communicator = communicator;
634 this.facade = facade;
637 public NetconfDeviceCommunicator getCommunicator() {
641 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
645 public NetconfClientSessionListener getSessionListener() {
650 public void close() {
651 communicator.close();
656 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
657 private final NetconfKeystoreAdapter keystoreAdapter;
658 private final Optional<Specification> specOptional;
660 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
661 this.keystoreAdapter = keystoreAdapter;
662 this.specOptional = Optional.fromNullable(specification);
666 public SslHandler createSslHandler() {
668 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
670 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
671 kmf.init(keyStore, "".toCharArray());
673 final TrustManagerFactory tmf =
674 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
677 final SSLContext sslCtx = SSLContext.getInstance("TLS");
678 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
679 final SSLEngine engine = sslCtx.createSSLEngine();
680 engine.setUseClientMode(true);
682 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
683 if (specOptional.isPresent()) {
684 final Specification specification = specOptional.get();
685 if (!(specification instanceof TlsCase)) {
686 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
688 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
691 engine.setEnabledProtocols(protocols.toArray(new String[0]));
692 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
693 engine.setEnableSessionCreation(true);
695 return new SslHandler(engine);
696 } catch (GeneralSecurityException | IOException exc) {
697 throw new IllegalStateException(exc);