2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netconf.topology;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Strings;
14 import com.google.common.collect.Lists;
15 import com.google.common.collect.Sets;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import com.google.common.util.concurrent.Uninterruptibles;
21 import io.netty.handler.ssl.SslHandler;
22 import io.netty.util.concurrent.EventExecutor;
24 import java.io.IOException;
25 import java.math.BigDecimal;
26 import java.net.InetSocketAddress;
28 import java.security.GeneralSecurityException;
29 import java.security.KeyStore;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
35 import java.util.concurrent.TimeUnit;
36 import javax.net.ssl.KeyManagerFactory;
37 import javax.net.ssl.SSLContext;
38 import javax.net.ssl.SSLEngine;
39 import javax.net.ssl.TrustManagerFactory;
40 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
41 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
42 import org.opendaylight.controller.config.threadpool.ThreadPool;
43 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
44 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
45 import org.opendaylight.netconf.api.NetconfMessage;
46 import org.opendaylight.netconf.client.NetconfClientDispatcher;
47 import org.opendaylight.netconf.client.NetconfClientSessionListener;
48 import org.opendaylight.netconf.client.SslHandlerFactory;
49 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
50 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
51 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
52 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
53 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
54 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
55 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
56 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
57 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
58 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
59 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
60 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
61 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
62 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
63 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
64 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
65 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
66 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
67 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
68 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
69 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
70 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
71 import org.opendaylight.netconf.topology.api.NetconfTopology;
72 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
73 import org.opendaylight.protocol.framework.ReconnectStrategy;
74 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
75 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
76 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
77 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.Specification;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.protocol.specification.TlsCase;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
90 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
91 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
92 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
93 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
94 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
95 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
96 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
97 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
98 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
99 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
100 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
101 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
102 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
103 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
104 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
108 public abstract class AbstractNetconfTopology implements NetconfTopology {
110 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
112 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
113 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
114 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
115 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
116 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
117 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
118 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
119 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
121 // constants related to Schema Cache(s)
123 * Filesystem based caches are stored relative to the cache directory.
125 private static final String CACHE_DIRECTORY = "cache";
128 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
130 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
133 * The qualified schema cache directory <code>cache/schema</code>.
135 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
136 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
139 * The name for the default schema repository.
141 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
144 * The default schema repository in the case that one is not specified.
146 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
147 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
149 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
150 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
153 * The default factory for creating <code>SchemaContext</code> instances.
155 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
156 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
159 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
160 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
161 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
162 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
163 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
164 * synchronization locks.
166 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
168 // Initializes default constant instances for the case when the default schema repository
169 // directory cache/schema is used.
171 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
172 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
173 DEFAULT_SCHEMA_CONTEXT_FACTORY,
174 new NetconfStateSchemasResolverImpl()));
175 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
176 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
177 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
180 * Create the default <code>FilesystemSchemaSourceCache</code>, which stores cached files
181 * in <code>cache/schema</code>. Try up to 3 times - we've seen intermittent failures on jenkins where
182 * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race
183 * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this
184 * scenario, a retry should succeed.
189 FilesystemSchemaSourceCache<YangTextSchemaSource> defaultCache =
190 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
191 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
192 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache);
194 } catch (IllegalArgumentException e) {
196 LOG.error("Error creating default schema cache", e);
199 Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
204 private final NetconfClientDispatcher clientDispatcher;
205 private final EventExecutor eventExecutor;
206 private final DeviceActionFactory deviceActionFactory;
207 private final NetconfKeystoreAdapter keystoreAdapter;
208 protected final ScheduledThreadPool keepaliveExecutor;
209 protected final ThreadPool processingExecutor;
210 protected final SharedSchemaRepository sharedSchemaRepository;
211 protected final DataBroker dataBroker;
212 protected final DOMMountPointService mountPointService;
213 protected final String topologyId;
214 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
215 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
216 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
217 protected String privateKeyPath;
218 protected String privateKeyPassphrase;
219 protected final AAAEncryptionService encryptionService;
220 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
222 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
223 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
224 final ThreadPool processingExecutor,
225 final SchemaRepositoryProvider schemaRepositoryProvider,
226 final DataBroker dataBroker, final DOMMountPointService mountPointService,
227 final AAAEncryptionService encryptionService,
228 final DeviceActionFactory deviceActionFactory) {
229 this.topologyId = topologyId;
230 this.clientDispatcher = clientDispatcher;
231 this.eventExecutor = eventExecutor;
232 this.keepaliveExecutor = keepaliveExecutor;
233 this.processingExecutor = processingExecutor;
234 this.deviceActionFactory = deviceActionFactory;
235 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
236 this.dataBroker = dataBroker;
237 this.mountPointService = mountPointService;
238 this.encryptionService = encryptionService;
240 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
243 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
244 this.schemaRegistry = schemaRegistry;
247 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
248 this.schemaContextFactory = schemaContextFactory;
252 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
253 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
254 return setupConnection(nodeId, configNode);
258 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
259 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
260 if (!activeConnectors.containsKey(nodeId)) {
261 return Futures.immediateFailedFuture(
262 new IllegalStateException("Unable to disconnect device that is not connected"));
265 // retrieve connection, and disconnect it
266 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
267 connectorDTO.getCommunicator().close();
268 connectorDTO.getFacade().close();
269 return Futures.immediateFuture(null);
272 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
273 final Node configNode) {
274 final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
276 Preconditions.checkNotNull(netconfNode.getHost());
277 Preconditions.checkNotNull(netconfNode.getPort());
278 Preconditions.checkNotNull(netconfNode.isTcpOnly());
280 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
281 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
282 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
283 final NetconfReconnectingClientConfiguration clientConfig =
284 getClientConfig(netconfClientSessionListener, netconfNode);
285 final ListenableFuture<NetconfDeviceCapabilities> future =
286 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
288 activeConnectors.put(nodeId, deviceCommunicatorDTO);
290 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
292 public void onSuccess(final NetconfDeviceCapabilities result) {
293 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
297 public void onFailure(final Throwable throwable) {
298 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
299 // remove this node from active connectors?
301 }, MoreExecutors.directExecutor());
306 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
307 //setup default values since default value is not supported in mdsal
308 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
309 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
310 final long keepaliveDelay = node.getKeepaliveDelay() == null
311 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
312 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
313 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
315 final IpAddress ipAddress = node.getHost().getIpAddress();
316 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
317 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
318 node.getPort().getValue());
319 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
321 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
322 createSalFacade(remoteDeviceId);
324 if (keepaliveDelay > 0) {
325 LOG.warn("Adding keepalive facade, for device {}", nodeId);
326 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
327 keepaliveDelay, defaultRequestTimeoutMillis);
330 // pre register yang library sources as fallback schemas to schema registry
331 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
332 if (node.getYangLibrary() != null) {
333 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
334 final String yangLibUsername = node.getYangLibrary().getUsername();
335 final String yangLigPassword = node.getYangLibrary().getPassword();
337 final LibraryModulesSchemas libraryModulesSchemas;
338 if (yangLibURL != null) {
339 if (yangLibUsername != null && yangLigPassword != null) {
340 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
342 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
345 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
346 : libraryModulesSchemas.getAvailableModels().entrySet()) {
347 registeredYangLibSources
348 .add(schemaRegistry.registerSchemaSource(
349 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
350 libraryModulesSchemas.getAvailableModels()),
351 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
352 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
357 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
358 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
359 if (node.isSchemaless()) {
360 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
362 NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
363 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
364 .setSchemaResourcesDTO(schemaResourcesDTO)
365 .setGlobalProcessingExecutor(this.processingExecutor.getExecutor())
366 .setId(remoteDeviceId)
367 .setSalFacade(salFacade);
368 if (this.deviceActionFactory != null) {
369 netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
371 device = netconfDeviceBuilder.build();
374 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
375 final int rpcMessageLimit =
376 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
378 if (rpcMessageLimit < 1) {
379 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
382 NetconfDeviceCommunicator netconfDeviceCommunicator =
383 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
384 userCapabilities.get(), rpcMessageLimit)
385 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
387 if (salFacade instanceof KeepaliveSalFacade) {
388 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
390 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
393 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
394 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
395 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
396 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
397 // Only checks to ensure the String is not empty or null; further checks related to directory
398 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
399 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
400 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
401 // the SchemaRegistry and SchemaContextFactory remain the default values.
402 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
403 // Multiple modules may be created at once;
404 // synchronize to avoid issues with data consistency among threads.
405 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
406 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
407 // if they already exist
408 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
409 if (schemaResourcesDTO == null) {
410 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
411 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
412 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
413 schemaResourcesDTO.getSchemaRegistry())
415 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
418 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
419 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
422 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
423 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
426 if (schemaResourcesDTO == null) {
427 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
428 schemaContextFactory, new NetconfStateSchemasResolverImpl());
431 return schemaResourcesDTO;
435 * Creates the backing Schema classes for a particular directory.
437 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
438 * @return A DTO containing the Schema classes for the Netconf mount.
440 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
441 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
442 final SchemaContextFactory contextFactory
443 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
444 setSchemaRegistry(repository);
445 setSchemaContextFactory(contextFactory);
446 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
447 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
448 repository.registerSchemaSourceListener(deviceCache);
449 repository.registerSchemaSourceListener(
450 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
451 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
452 new NetconfStateSchemasResolverImpl());
456 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
458 * @param schemaCacheDirectory The custom cache directory relative to "cache"
459 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
461 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
462 final String schemaCacheDirectory) {
463 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
464 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
465 new File(relativeSchemaCacheDirectory));
469 * Sets the private key path from location specified in configuration file using blueprint.
471 public void setPrivateKeyPath(final String privateKeyPath) {
472 this.privateKeyPath = privateKeyPath;
476 * Sets the private key passphrase from location specified in configuration file using blueprint.
478 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
479 this.privateKeyPassphrase = privateKeyPassphrase;
482 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
483 final NetconfNode node) {
485 //setup default values since default value is not supported in mdsal
486 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
487 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
488 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
489 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
490 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
491 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
492 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
494 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
496 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
497 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
498 final ReconnectStrategy strategy = sf.createReconnectStrategy();
500 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder =
501 NetconfReconnectingClientConfigurationBuilder.create();
503 if (node.isTcpOnly() || node.getProtocol() == null || node.getProtocol().getName() == Name.SSH) {
504 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
505 reconnectingClientConfigurationBuilder
506 .withAuthHandler(authHandler)
507 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
508 NetconfClientConfiguration.NetconfClientProtocol.SSH);
509 } else if (node.getProtocol().getName() == Name.TLS) {
510 final SslHandlerFactory sslHandlerFactory = new SslHandlerFactoryImpl(keystoreAdapter,
511 node.getProtocol().getSpecification());
512 reconnectingClientConfigurationBuilder
513 .withSslHandlerFactory(sslHandlerFactory)
514 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
516 throw new IllegalStateException("Unsupported protocol type: " + node.getProtocol().getName().getClass());
519 return reconnectingClientConfigurationBuilder
520 .withAddress(socketAddress)
521 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
522 .withReconnectStrategy(strategy)
523 .withConnectStrategyFactory(sf)
524 .withSessionListener(listener)
528 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
529 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
530 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
531 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
532 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
533 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
534 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
535 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
537 if (credentials instanceof LoginPwUnencrypted) {
538 final LoginPasswordUnencrypted loginPassword =
539 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
540 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
542 if (credentials instanceof LoginPw) {
543 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
544 return new LoginPasswordHandler(loginPassword.getUsername(),
545 encryptionService.decrypt(loginPassword.getPassword()));
547 if (credentials instanceof KeyAuth) {
548 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
549 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
550 keystoreAdapter, encryptionService);
552 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
555 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
557 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
558 if (host.getDomainName() != null) {
559 return new InetSocketAddress(host.getDomainName().getValue(), port);
562 final IpAddress ipAddress = host.getIpAddress();
563 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
564 : ipAddress.getIpv6Address().getValue();
565 return new InetSocketAddress(ip, port);
568 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
569 // if none of yang-module-capabilities or non-module-capabilities is specified
570 // just return absent
571 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
572 return Optional.absent();
575 final List<String> capabilities = new ArrayList<>();
577 boolean overrideYangModuleCaps = false;
578 if (node.getYangModuleCapabilities() != null) {
579 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
580 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
583 //non-module capabilities should not exist in yang module capabilities
584 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
585 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
586 "List yang-module-capabilities/capability should contain only module based capabilities. "
587 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
589 boolean overrideNonModuleCaps = false;
590 if (node.getNonModuleCapabilities() != null) {
591 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
592 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
595 return Optional.of(new UserPreferences(NetconfSessionPreferences
596 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
599 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
600 private final Long connectionAttempts;
601 private final EventExecutor executor;
602 private final double sleepFactor;
603 private final int minSleep;
605 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
606 final int minSleep, final BigDecimal sleepFactor) {
607 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
608 connectionAttempts = maxConnectionAttempts;
610 connectionAttempts = null;
613 this.sleepFactor = sleepFactor.doubleValue();
614 this.executor = executor;
615 this.minSleep = minSleep;
619 public ReconnectStrategy createReconnectStrategy() {
620 return new TimedReconnectStrategy(executor, minSleep,
621 minSleep, sleepFactor, null /*maxSleep*/, connectionAttempts, null /*deadline*/);
625 protected static class NetconfConnectorDTO implements AutoCloseable {
627 private final NetconfDeviceCommunicator communicator;
628 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
630 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
631 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
632 this.communicator = communicator;
633 this.facade = facade;
636 public NetconfDeviceCommunicator getCommunicator() {
640 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
644 public NetconfClientSessionListener getSessionListener() {
649 public void close() {
650 communicator.close();
655 private static final class SslHandlerFactoryImpl implements SslHandlerFactory {
656 private final NetconfKeystoreAdapter keystoreAdapter;
657 private final Optional<Specification> specOptional;
659 SslHandlerFactoryImpl(final NetconfKeystoreAdapter keystoreAdapter, final Specification specification) {
660 this.keystoreAdapter = keystoreAdapter;
661 this.specOptional = Optional.fromNullable(specification);
665 public SslHandler createSslHandler() {
667 final KeyStore keyStore = keystoreAdapter.getJavaKeyStore();
669 final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
670 kmf.init(keyStore, "".toCharArray());
672 final TrustManagerFactory tmf =
673 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
676 final SSLContext sslCtx = SSLContext.getInstance("TLS");
677 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
678 final SSLEngine engine = sslCtx.createSSLEngine();
679 engine.setUseClientMode(true);
681 final Set<String> protocols = Sets.newHashSet(engine.getSupportedProtocols());
682 if (specOptional.isPresent()) {
683 final Specification specification = specOptional.get();
684 if (!(specification instanceof TlsCase)) {
685 throw new IllegalArgumentException("Cannot get TLS specification from: " + specification);
687 protocols.removeAll(((TlsCase)specification).getTls().getExcludedVersions());
690 engine.setEnabledProtocols(protocols.toArray(new String[0]));
691 engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
692 engine.setEnableSessionCreation(true);
694 return new SslHandler(engine);
695 } catch (GeneralSecurityException | IOException exc) {
696 throw new IllegalStateException(exc);