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
8 package org.opendaylight.netconf.topology;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Strings;
13 import com.google.common.collect.Lists;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.ListeningExecutorService;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import com.google.common.util.concurrent.Uninterruptibles;
20 import io.netty.util.concurrent.EventExecutor;
22 import java.math.BigDecimal;
23 import java.net.InetSocketAddress;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
29 import java.util.concurrent.TimeUnit;
30 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
31 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
32 import org.opendaylight.controller.config.threadpool.ThreadPool;
33 import org.opendaylight.mdsal.binding.api.DataBroker;
34 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
35 import org.opendaylight.netconf.api.NetconfMessage;
36 import org.opendaylight.netconf.client.NetconfClientDispatcher;
37 import org.opendaylight.netconf.client.NetconfClientSessionListener;
38 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
39 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
40 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
41 import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
42 import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
43 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
44 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
45 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
46 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
47 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
48 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
49 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
50 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
51 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
52 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
53 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
54 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
55 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
56 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
57 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
58 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
59 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
60 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
61 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
62 import org.opendaylight.netconf.topology.api.NetconfTopology;
63 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
77 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
78 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
79 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
80 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
81 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
82 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
83 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
84 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
85 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
86 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
87 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
88 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
89 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
90 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
91 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
92 import org.slf4j.Logger;
93 import org.slf4j.LoggerFactory;
95 public abstract class AbstractNetconfTopology implements NetconfTopology {
97 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
99 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
100 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
101 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
102 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
103 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
104 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
105 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
106 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
108 // constants related to Schema Cache(s)
110 * Filesystem based caches are stored relative to the cache directory.
112 private static final String CACHE_DIRECTORY = "cache";
115 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
117 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
120 * The qualified schema cache directory <code>cache/schema</code>.
122 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
123 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
126 * The name for the default schema repository.
128 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
131 * The default schema repository in the case that one is not specified.
133 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
134 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
136 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
137 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
140 * The default factory for creating <code>SchemaContext</code> instances.
142 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
143 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
146 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
147 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
148 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
149 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
150 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
151 * synchronization locks.
153 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
155 // Initializes default constant instances for the case when the default schema repository
156 // directory cache/schema is used.
158 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
159 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
160 DEFAULT_SCHEMA_CONTEXT_FACTORY,
161 new NetconfStateSchemasResolverImpl()));
162 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
163 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
164 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
167 * Create the default <code>FilesystemSchemaSourceCache</code>, which stores cached files
168 * in <code>cache/schema</code>. Try up to 3 times - we've seen intermittent failures on jenkins where
169 * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race
170 * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this
171 * scenario, a retry should succeed.
176 FilesystemSchemaSourceCache<YangTextSchemaSource> defaultCache =
177 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
178 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
179 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache);
181 } catch (IllegalArgumentException e) {
183 LOG.error("Error creating default schema cache", e);
186 Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
191 private final NetconfClientDispatcher clientDispatcher;
192 private final EventExecutor eventExecutor;
193 private final DeviceActionFactory deviceActionFactory;
194 private final NetconfKeystoreAdapter keystoreAdapter;
195 protected final ScheduledThreadPool keepaliveExecutor;
196 protected final ListeningExecutorService processingExecutor;
197 protected final SharedSchemaRepository sharedSchemaRepository;
198 protected final DataBroker dataBroker;
199 protected final DOMMountPointService mountPointService;
200 protected final String topologyId;
201 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
202 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
203 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
204 protected String privateKeyPath;
205 protected String privateKeyPassphrase;
206 protected final AAAEncryptionService encryptionService;
207 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
209 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
210 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
211 final ThreadPool processingExecutor,
212 final SchemaRepositoryProvider schemaRepositoryProvider,
213 final DataBroker dataBroker, final DOMMountPointService mountPointService,
214 final AAAEncryptionService encryptionService,
215 final DeviceActionFactory deviceActionFactory) {
216 this.topologyId = topologyId;
217 this.clientDispatcher = clientDispatcher;
218 this.eventExecutor = eventExecutor;
219 this.keepaliveExecutor = keepaliveExecutor;
220 this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
221 this.deviceActionFactory = deviceActionFactory;
222 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
223 this.dataBroker = dataBroker;
224 this.mountPointService = mountPointService;
225 this.encryptionService = encryptionService;
227 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
230 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
231 this.schemaRegistry = schemaRegistry;
234 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
235 this.schemaContextFactory = schemaContextFactory;
239 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
240 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
241 return setupConnection(nodeId, configNode);
245 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
246 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
247 if (!activeConnectors.containsKey(nodeId)) {
248 return Futures.immediateFailedFuture(
249 new IllegalStateException("Unable to disconnect device that is not connected"));
252 // retrieve connection, and disconnect it
253 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
254 connectorDTO.getCommunicator().close();
255 connectorDTO.getFacade().close();
256 return Futures.immediateFuture(null);
259 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
260 final Node configNode) {
261 final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
263 Preconditions.checkNotNull(netconfNode.getHost());
264 Preconditions.checkNotNull(netconfNode.getPort());
265 Preconditions.checkNotNull(netconfNode.isTcpOnly());
267 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
268 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
269 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
270 final NetconfReconnectingClientConfiguration clientConfig =
271 getClientConfig(netconfClientSessionListener, netconfNode);
272 final ListenableFuture<NetconfDeviceCapabilities> future =
273 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
275 activeConnectors.put(nodeId, deviceCommunicatorDTO);
277 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
279 public void onSuccess(final NetconfDeviceCapabilities result) {
280 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
284 public void onFailure(final Throwable throwable) {
285 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
286 // remove this node from active connectors?
288 }, MoreExecutors.directExecutor());
293 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
294 //setup default values since default value is not supported in mdsal
295 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
296 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
297 final long keepaliveDelay = node.getKeepaliveDelay() == null
298 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
299 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
300 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
302 final IpAddress ipAddress = node.getHost().getIpAddress();
303 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
304 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
305 node.getPort().getValue());
306 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
308 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
309 createSalFacade(remoteDeviceId);
311 if (keepaliveDelay > 0) {
312 LOG.warn("Adding keepalive facade, for device {}", nodeId);
313 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
314 keepaliveDelay, defaultRequestTimeoutMillis);
317 // pre register yang library sources as fallback schemas to schema registry
318 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
319 if (node.getYangLibrary() != null) {
320 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
321 final String yangLibUsername = node.getYangLibrary().getUsername();
322 final String yangLigPassword = node.getYangLibrary().getPassword();
324 final LibraryModulesSchemas libraryModulesSchemas;
325 if (yangLibURL != null) {
326 if (yangLibUsername != null && yangLigPassword != null) {
327 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
329 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
332 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
333 : libraryModulesSchemas.getAvailableModels().entrySet()) {
334 registeredYangLibSources
335 .add(schemaRegistry.registerSchemaSource(
336 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
337 libraryModulesSchemas.getAvailableModels()),
338 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
339 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
344 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
345 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
346 if (node.isSchemaless()) {
347 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
349 NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
350 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
351 .setSchemaResourcesDTO(schemaResourcesDTO)
352 .setGlobalProcessingExecutor(this.processingExecutor)
353 .setId(remoteDeviceId)
354 .setSalFacade(salFacade);
355 if (this.deviceActionFactory != null) {
356 netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
358 device = netconfDeviceBuilder.build();
361 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
362 final int rpcMessageLimit =
363 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
365 if (rpcMessageLimit < 1) {
366 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
369 NetconfDeviceCommunicator netconfDeviceCommunicator =
370 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
371 userCapabilities.get(), rpcMessageLimit)
372 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
374 if (salFacade instanceof KeepaliveSalFacade) {
375 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
377 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
380 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
381 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
382 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
383 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
384 // Only checks to ensure the String is not empty or null; further checks related to directory
385 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
386 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
387 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
388 // the SchemaRegistry and SchemaContextFactory remain the default values.
389 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
390 // Multiple modules may be created at once;
391 // synchronize to avoid issues with data consistency among threads.
392 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
393 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
394 // if they already exist
395 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
396 if (schemaResourcesDTO == null) {
397 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
398 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
399 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
400 schemaResourcesDTO.getSchemaRegistry())
402 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
405 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
406 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
409 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
410 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
413 if (schemaResourcesDTO == null) {
414 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
415 schemaContextFactory, new NetconfStateSchemasResolverImpl());
418 return schemaResourcesDTO;
422 * Creates the backing Schema classes for a particular directory.
424 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
425 * @return A DTO containing the Schema classes for the Netconf mount.
427 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
428 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
429 final SchemaContextFactory contextFactory
430 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
431 setSchemaRegistry(repository);
432 setSchemaContextFactory(contextFactory);
433 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
434 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
435 repository.registerSchemaSourceListener(deviceCache);
436 repository.registerSchemaSourceListener(
437 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
438 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
439 new NetconfStateSchemasResolverImpl());
443 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
445 * @param schemaCacheDirectory The custom cache directory relative to "cache"
446 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
448 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
449 final String schemaCacheDirectory) {
450 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
451 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
452 new File(relativeSchemaCacheDirectory));
456 * Sets the private key path from location specified in configuration file using blueprint.
458 public void setPrivateKeyPath(final String privateKeyPath) {
459 this.privateKeyPath = privateKeyPath;
463 * Sets the private key passphrase from location specified in configuration file using blueprint.
465 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
466 this.privateKeyPassphrase = privateKeyPassphrase;
469 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
470 final NetconfNode node) {
472 //setup default values since default value is not supported in mdsal
473 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
474 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
475 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
476 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
477 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
478 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
479 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
481 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
483 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
484 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
486 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
487 final Protocol protocol = node.getProtocol();
488 if (node.isTcpOnly()) {
489 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
490 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
491 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
492 } else if (protocol == null || protocol.getName() == Name.SSH) {
493 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
494 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
495 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
496 } else if (protocol.getName() == Name.TLS) {
497 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
498 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
499 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
501 throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
504 if (node.getOdlHelloMessageCapabilities() != null) {
505 reconnectingClientConfigurationBuilder
506 .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
509 return reconnectingClientConfigurationBuilder
510 .withAddress(socketAddress)
511 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
512 .withReconnectStrategy(sf.createReconnectStrategy())
513 .withConnectStrategyFactory(sf)
514 .withSessionListener(listener)
518 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
519 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
520 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
521 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
522 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
523 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
524 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
525 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
527 if (credentials instanceof LoginPwUnencrypted) {
528 final LoginPasswordUnencrypted loginPassword =
529 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
530 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
532 if (credentials instanceof LoginPw) {
533 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
534 return new LoginPasswordHandler(loginPassword.getUsername(),
535 encryptionService.decrypt(loginPassword.getPassword()));
537 if (credentials instanceof KeyAuth) {
538 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
539 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
540 keystoreAdapter, encryptionService);
542 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
545 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
547 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
548 if (host.getDomainName() != null) {
549 return new InetSocketAddress(host.getDomainName().getValue(), port);
552 final IpAddress ipAddress = host.getIpAddress();
553 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
554 : ipAddress.getIpv6Address().getValue();
555 return new InetSocketAddress(ip, port);
558 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
559 // if none of yang-module-capabilities or non-module-capabilities is specified
560 // just return absent
561 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
562 return Optional.absent();
565 final List<String> capabilities = new ArrayList<>();
567 boolean overrideYangModuleCaps = false;
568 if (node.getYangModuleCapabilities() != null) {
569 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
570 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
573 //non-module capabilities should not exist in yang module capabilities
574 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
575 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
576 "List yang-module-capabilities/capability should contain only module based capabilities. "
577 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
579 boolean overrideNonModuleCaps = false;
580 if (node.getNonModuleCapabilities() != null) {
581 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
582 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
585 return Optional.of(new UserPreferences(NetconfSessionPreferences
586 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
589 protected static class NetconfConnectorDTO implements AutoCloseable {
591 private final NetconfDeviceCommunicator communicator;
592 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
594 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
595 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
596 this.communicator = communicator;
597 this.facade = facade;
600 public NetconfDeviceCommunicator getCommunicator() {
604 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
608 public NetconfClientSessionListener getSessionListener() {
613 public void close() {
614 communicator.close();