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 : " + nodeId.getValue() + " started succesfully");
298 public void onFailure(final Throwable throwable) {
299 LOG.error("Connector for : " + nodeId.getValue() + " failed");
300 // remove this node from active connectors?
302 }, MoreExecutors.directExecutor());
307 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
308 final NetconfNode node) {
309 //setup default values since default value is not supported in mdsal
310 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
311 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
312 final long keepaliveDelay = node.getKeepaliveDelay() == null
313 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
314 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
315 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
317 final IpAddress ipAddress = node.getHost().getIpAddress();
318 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
319 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
320 node.getPort().getValue());
321 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
323 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
324 createSalFacade(remoteDeviceId);
326 if (keepaliveDelay > 0) {
327 LOG.warn("Adding keepalive facade, for device {}", nodeId);
328 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
329 keepaliveDelay, defaultRequestTimeoutMillis);
332 // pre register yang library sources as fallback schemas to schema registry
333 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
334 if (node.getYangLibrary() != null) {
335 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
336 final String yangLibUsername = node.getYangLibrary().getUsername();
337 final String yangLigPassword = node.getYangLibrary().getPassword();
339 final LibraryModulesSchemas libraryModulesSchemas;
340 if (yangLibURL != null) {
341 if (yangLibUsername != null && yangLigPassword != null) {
342 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
344 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
347 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
348 : libraryModulesSchemas.getAvailableModels().entrySet()) {
349 registeredYangLibSources
350 .add(schemaRegistry.registerSchemaSource(
351 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
352 libraryModulesSchemas.getAvailableModels()),
353 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
354 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
359 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
360 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
361 if (node.isSchemaless()) {
362 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
364 NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
365 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
366 .setSchemaResourcesDTO(schemaResourcesDTO)
367 .setGlobalProcessingExecutor(this.processingExecutor)
368 .setId(remoteDeviceId)
369 .setSalFacade(salFacade);
370 if (this.deviceActionFactory != null) {
371 netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
373 device = netconfDeviceBuilder.build();
376 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
377 final int rpcMessageLimit =
378 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
380 if (rpcMessageLimit < 1) {
381 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
384 NetconfDeviceCommunicator netconfDeviceCommunicator =
385 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
386 userCapabilities.get(), rpcMessageLimit)
387 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
389 if (salFacade instanceof KeepaliveSalFacade) {
390 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
392 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
395 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
396 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
397 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
398 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
399 // Only checks to ensure the String is not empty or null; further checks related to directory
400 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
401 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
402 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
403 // the SchemaRegistry and SchemaContextFactory remain the default values.
404 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
405 // Multiple modules may be created at once;
406 // synchronize to avoid issues with data consistency among threads.
407 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
408 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
409 // if they already exist
410 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
411 if (schemaResourcesDTO == null) {
412 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
413 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
414 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
415 schemaResourcesDTO.getSchemaRegistry())
417 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
420 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
421 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
424 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
425 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
428 if (schemaResourcesDTO == null) {
429 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
430 schemaContextFactory, new NetconfStateSchemasResolverImpl());
433 return schemaResourcesDTO;
437 * Creates the backing Schema classes for a particular directory.
439 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
440 * @return A DTO containing the Schema classes for the Netconf mount.
442 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
443 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
444 final SchemaContextFactory contextFactory
445 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
446 setSchemaRegistry(repository);
447 setSchemaContextFactory(contextFactory);
448 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
449 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
450 repository.registerSchemaSourceListener(deviceCache);
451 repository.registerSchemaSourceListener(
452 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
453 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
454 new NetconfStateSchemasResolverImpl());
458 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
460 * @param schemaCacheDirectory The custom cache directory relative to "cache"
461 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
463 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
464 final String schemaCacheDirectory) {
465 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
466 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
467 new File(relativeSchemaCacheDirectory));
471 * Sets the private key path from location specified in configuration file using blueprint.
473 public void setPrivateKeyPath(final String privateKeyPath) {
474 this.privateKeyPath = privateKeyPath;
478 * Sets the private key passphrase from location specified in configuration file using blueprint.
480 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
481 this.privateKeyPassphrase = privateKeyPassphrase;
484 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
485 final NetconfNode node) {
487 //setup default values since default value is not supported in mdsal
488 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
489 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
490 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
491 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
492 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
493 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
494 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
496 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
498 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
499 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
500 final ReconnectStrategy strategy = sf.createReconnectStrategy();
502 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder =
503 NetconfReconnectingClientConfigurationBuilder.create();
505 if (node.isTcpOnly() || node.getProtocol() == null || node.getProtocol().getName() == Name.SSH) {
506 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
507 reconnectingClientConfigurationBuilder
508 .withAuthHandler(authHandler)
509 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
510 NetconfClientConfiguration.NetconfClientProtocol.SSH);
511 } else if (node.getProtocol().getName() == Name.TLS) {
512 final SslHandlerFactory sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter,
513 node.getProtocol().getSpecification());
514 reconnectingClientConfigurationBuilder
515 .withSslHandlerFactory(sslHandlerFactory)
516 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
518 throw new IllegalStateException("Unsupported protocol type: " + node.getProtocol().getName().getClass());
521 return reconnectingClientConfigurationBuilder
522 .withAddress(socketAddress)
523 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
524 .withReconnectStrategy(strategy)
525 .withConnectStrategyFactory(sf)
526 .withSessionListener(listener)
530 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
531 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
532 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
533 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
534 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
535 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
536 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
537 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
539 if (credentials instanceof LoginPwUnencrypted) {
540 final LoginPasswordUnencrypted loginPassword =
541 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
542 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
544 if (credentials instanceof LoginPw) {
545 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
546 return new LoginPasswordHandler(loginPassword.getUsername(),
547 encryptionService.decrypt(loginPassword.getPassword()));
549 if (credentials instanceof KeyAuth) {
550 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
551 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
552 keystoreAdapter, encryptionService);
554 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
557 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
559 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
560 if (host.getDomainName() != null) {
561 return new InetSocketAddress(host.getDomainName().getValue(), port);
564 final IpAddress ipAddress = host.getIpAddress();
565 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
566 : ipAddress.getIpv6Address().getValue();
567 return new InetSocketAddress(ip, port);
570 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
571 // if none of yang-module-capabilities or non-module-capabilities is specified
572 // just return absent
573 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
574 return Optional.absent();
577 final List<String> capabilities = new ArrayList<>();
579 boolean overrideYangModuleCaps = false;
580 if (node.getYangModuleCapabilities() != null) {
581 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
582 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
585 //non-module capabilities should not exist in yang module capabilities
586 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
587 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
588 "List yang-module-capabilities/capability should contain only module based capabilities. "
589 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
591 boolean overrideNonModuleCaps = false;
592 if (node.getNonModuleCapabilities() != null) {
593 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
594 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
597 return Optional.of(new UserPreferences(NetconfSessionPreferences
598 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
601 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
602 private final Long connectionAttempts;
603 private final EventExecutor executor;
604 private final double sleepFactor;
605 private final int minSleep;
607 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
608 final int minSleep, final BigDecimal sleepFactor) {
609 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
610 connectionAttempts = maxConnectionAttempts;
612 connectionAttempts = null;
615 this.sleepFactor = sleepFactor.doubleValue();
616 this.executor = executor;
617 this.minSleep = minSleep;
621 public ReconnectStrategy createReconnectStrategy() {
622 return new TimedReconnectStrategy(executor, minSleep,
623 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
627 protected static class NetconfConnectorDTO implements AutoCloseable {
629 private final NetconfDeviceCommunicator communicator;
630 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
632 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
633 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
634 this.communicator = communicator;
635 this.facade = facade;
638 public NetconfDeviceCommunicator getCommunicator() {
642 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
646 public NetconfClientSessionListener getSessionListener() {
651 public void close() {
652 communicator.close();
657 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
658 private final NetconfKeystoreAdapter keystoreAdapter;
659 private final Optional<Specification> specOptional;
661 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
662 this.keystoreAdapter = keystoreAdapter;
663 this.specOptional = Optional.fromNullable(specification);
667 public SslHandler createSslHandler() {
669 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
671 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
672 kmf.init(keyStore, "".toCharArray());
674 final TrustManagerFactory tmf =
675 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
678 final SSLContext sslCtx = SSLContext.getInstance("TLS");
679 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
680 final SSLEngine engine = sslCtx.createSSLEngine();
681 engine.setUseClientMode(true);
683 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
684 if (specOptional.isPresent()) {
685 final Specification specification = specOptional.get();
686 if (!(specification instanceof TlsCase)) {
687 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
689 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
692 engine.setEnabledProtocols(protocols.toArray(new String[0]));
693 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
694 engine.setEnableSessionCreation(true);
696 return new SslHandler(engine);
697 } catch (GeneralSecurityException | IOException exc) {
698 throw new IllegalStateException(exc);