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.sal.connect.util.SslHandlerFactoryImpl;
63 import org.opendaylight.netconf.topology.api.NetconfTopology;
64 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
79 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
80 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
81 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
82 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
83 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
84 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
85 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
86 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
87 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
88 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
89 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
90 import org.opendaylight.yangtools.yang.model.repo.util.InMemorySchemaSourceCache;
91 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
92 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.ASTSchemaSource;
93 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
97 public abstract class AbstractNetconfTopology implements NetconfTopology {
99 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
101 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
102 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
103 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
104 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
105 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
106 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
107 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
108 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
110 // constants related to Schema Cache(s)
112 * Filesystem based caches are stored relative to the cache directory.
114 private static final String CACHE_DIRECTORY = "cache";
117 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
119 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
122 * The qualified schema cache directory <code>cache/schema</code>.
124 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
125 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
128 * The name for the default schema repository.
130 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
133 * The default schema repository in the case that one is not specified.
135 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
136 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
138 public static final InMemorySchemaSourceCache<ASTSchemaSource> DEFAULT_AST_CACHE =
139 InMemorySchemaSourceCache.createSoftCache(DEFAULT_SCHEMA_REPOSITORY, ASTSchemaSource.class);
142 * The default factory for creating <code>SchemaContext</code> instances.
144 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
145 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
148 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
149 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
150 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
151 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
152 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
153 * synchronization locks.
155 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
157 // Initializes default constant instances for the case when the default schema repository
158 // directory cache/schema is used.
160 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
161 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
162 DEFAULT_SCHEMA_CONTEXT_FACTORY,
163 new NetconfStateSchemasResolverImpl()));
164 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_AST_CACHE);
165 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
166 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
169 * Create the default <code>FilesystemSchemaSourceCache</code>, which stores cached files
170 * in <code>cache/schema</code>. Try up to 3 times - we've seen intermittent failures on jenkins where
171 * FilesystemSchemaSourceCache throws an IAE due to mkdirs failure. The theory is that there's a race
172 * creating the dir and it already exists when mkdirs is called (mkdirs returns false in this case). In this
173 * scenario, a retry should succeed.
178 FilesystemSchemaSourceCache<YangTextSchemaSource> defaultCache =
179 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
180 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
181 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(defaultCache);
183 } catch (IllegalArgumentException e) {
185 LOG.error("Error creating default schema cache", e);
188 Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
193 private final NetconfClientDispatcher clientDispatcher;
194 private final EventExecutor eventExecutor;
195 private final DeviceActionFactory deviceActionFactory;
196 private final NetconfKeystoreAdapter keystoreAdapter;
197 protected final ScheduledThreadPool keepaliveExecutor;
198 protected final ListeningExecutorService processingExecutor;
199 protected final SharedSchemaRepository sharedSchemaRepository;
200 protected final DataBroker dataBroker;
201 protected final DOMMountPointService mountPointService;
202 protected final String topologyId;
203 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
204 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
205 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
206 protected String privateKeyPath;
207 protected String privateKeyPassphrase;
208 protected final AAAEncryptionService encryptionService;
209 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
211 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
212 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
213 final ThreadPool processingExecutor,
214 final SchemaRepositoryProvider schemaRepositoryProvider,
215 final DataBroker dataBroker, final DOMMountPointService mountPointService,
216 final AAAEncryptionService encryptionService,
217 final DeviceActionFactory deviceActionFactory) {
218 this.topologyId = topologyId;
219 this.clientDispatcher = clientDispatcher;
220 this.eventExecutor = eventExecutor;
221 this.keepaliveExecutor = keepaliveExecutor;
222 this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
223 this.deviceActionFactory = deviceActionFactory;
224 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
225 this.dataBroker = dataBroker;
226 this.mountPointService = mountPointService;
227 this.encryptionService = encryptionService;
229 this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
232 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
233 this.schemaRegistry = schemaRegistry;
236 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
237 this.schemaContextFactory = schemaContextFactory;
241 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
242 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
243 return setupConnection(nodeId, configNode);
247 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
248 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
249 if (!activeConnectors.containsKey(nodeId)) {
250 return Futures.immediateFailedFuture(
251 new IllegalStateException("Unable to disconnect device that is not connected"));
254 // retrieve connection, and disconnect it
255 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
256 connectorDTO.getCommunicator().close();
257 connectorDTO.getFacade().close();
258 return Futures.immediateFuture(null);
261 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
262 final Node configNode) {
263 final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
264 final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
266 Preconditions.checkNotNull(netconfNode.getHost());
267 Preconditions.checkNotNull(netconfNode.getPort());
268 Preconditions.checkNotNull(netconfNode.isTcpOnly());
270 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional);
271 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
272 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
273 final NetconfReconnectingClientConfiguration clientConfig =
274 getClientConfig(netconfClientSessionListener, netconfNode);
275 final ListenableFuture<NetconfDeviceCapabilities> future =
276 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
278 activeConnectors.put(nodeId, deviceCommunicatorDTO);
280 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
282 public void onSuccess(final NetconfDeviceCapabilities result) {
283 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
287 public void onFailure(final Throwable throwable) {
288 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
289 // remove this node from active connectors?
291 }, MoreExecutors.directExecutor());
296 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
297 return createDeviceCommunicator(nodeId, node, null);
300 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
301 final NetconfNodeAugmentedOptional nodeOptional) {
302 //setup default values since default value is not supported in mdsal
303 final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
304 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
305 final long keepaliveDelay = node.getKeepaliveDelay() == null
306 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
307 final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
308 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
310 final IpAddress ipAddress = node.getHost().getIpAddress();
311 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
312 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
313 node.getPort().getValue());
314 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
316 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
317 createSalFacade(remoteDeviceId);
319 if (keepaliveDelay > 0) {
320 LOG.warn("Adding keepalive facade, for device {}", nodeId);
321 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
322 keepaliveDelay, defaultRequestTimeoutMillis);
325 // pre register yang library sources as fallback schemas to schema registry
326 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
327 if (node.getYangLibrary() != null) {
328 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
329 final String yangLibUsername = node.getYangLibrary().getUsername();
330 final String yangLigPassword = node.getYangLibrary().getPassword();
332 final LibraryModulesSchemas libraryModulesSchemas;
333 if (yangLibURL != null) {
334 if (yangLibUsername != null && yangLigPassword != null) {
335 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
337 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
340 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
341 : libraryModulesSchemas.getAvailableModels().entrySet()) {
342 registeredYangLibSources
343 .add(schemaRegistry.registerSchemaSource(
344 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
345 libraryModulesSchemas.getAvailableModels()),
346 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
347 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
352 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
353 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
354 if (node.isSchemaless()) {
355 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
357 NetconfDeviceBuilder netconfDeviceBuilder = new NetconfDeviceBuilder()
358 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
359 .setSchemaResourcesDTO(schemaResourcesDTO)
360 .setGlobalProcessingExecutor(this.processingExecutor)
361 .setId(remoteDeviceId)
362 .setSalFacade(salFacade)
364 .setEventExecutor(eventExecutor)
365 .setNodeOptional(nodeOptional);
366 if (this.deviceActionFactory != null) {
367 netconfDeviceBuilder.setDeviceActionFactory(this.deviceActionFactory);
369 device = netconfDeviceBuilder.build();
372 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
373 final int rpcMessageLimit =
374 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
376 if (rpcMessageLimit < 1) {
377 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
380 NetconfDeviceCommunicator netconfDeviceCommunicator =
381 userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
382 userCapabilities.get(), rpcMessageLimit)
383 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
385 if (salFacade instanceof KeepaliveSalFacade) {
386 ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
388 return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
391 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
392 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
393 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
394 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
395 // Only checks to ensure the String is not empty or null; further checks related to directory
396 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
397 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
398 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
399 // the SchemaRegistry and SchemaContextFactory remain the default values.
400 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
401 // Multiple modules may be created at once;
402 // synchronize to avoid issues with data consistency among threads.
403 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
404 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
405 // if they already exist
406 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
407 if (schemaResourcesDTO == null) {
408 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
409 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
410 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
411 schemaResourcesDTO.getSchemaRegistry())
413 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
416 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
417 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
420 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
421 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
424 if (schemaResourcesDTO == null) {
425 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
426 schemaContextFactory, new NetconfStateSchemasResolverImpl());
429 return schemaResourcesDTO;
433 * Creates the backing Schema classes for a particular directory.
435 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
436 * @return A DTO containing the Schema classes for the Netconf mount.
438 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
439 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
440 final SchemaContextFactory contextFactory
441 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
442 setSchemaRegistry(repository);
443 setSchemaContextFactory(contextFactory);
444 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
445 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
446 repository.registerSchemaSourceListener(deviceCache);
447 repository.registerSchemaSourceListener(
448 InMemorySchemaSourceCache.createSoftCache(repository, ASTSchemaSource.class));
449 return new NetconfDevice.SchemaResourcesDTO(repository, repository, contextFactory,
450 new NetconfStateSchemasResolverImpl());
454 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
456 * @param schemaCacheDirectory The custom cache directory relative to "cache"
457 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
459 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
460 final String schemaCacheDirectory) {
461 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
462 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
463 new File(relativeSchemaCacheDirectory));
467 * Sets the private key path from location specified in configuration file using blueprint.
469 public void setPrivateKeyPath(final String privateKeyPath) {
470 this.privateKeyPath = privateKeyPath;
474 * Sets the private key passphrase from location specified in configuration file using blueprint.
476 public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
477 this.privateKeyPassphrase = privateKeyPassphrase;
480 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
481 final NetconfNode node) {
483 //setup default values since default value is not supported in mdsal
484 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
485 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
486 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
487 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
488 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
489 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
490 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
492 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
494 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
495 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
497 final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
498 final Protocol protocol = node.getProtocol();
499 if (node.isTcpOnly()) {
500 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
501 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
502 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
503 } else if (protocol == null || protocol.getName() == Name.SSH) {
504 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
505 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
506 .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
507 } else if (protocol.getName() == Name.TLS) {
508 reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
509 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
510 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
512 throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
515 if (node.getOdlHelloMessageCapabilities() != null) {
516 reconnectingClientConfigurationBuilder
517 .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
520 return reconnectingClientConfigurationBuilder
521 .withAddress(socketAddress)
522 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
523 .withReconnectStrategy(sf.createReconnectStrategy())
524 .withConnectStrategyFactory(sf)
525 .withSessionListener(listener)
529 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
530 if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
531 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
532 final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
533 .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
534 = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
535 .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
536 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
538 if (credentials instanceof LoginPwUnencrypted) {
539 final LoginPasswordUnencrypted loginPassword =
540 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
541 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
543 if (credentials instanceof LoginPw) {
544 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
545 return new LoginPasswordHandler(loginPassword.getUsername(),
546 encryptionService.decrypt(loginPassword.getPassword()));
548 if (credentials instanceof KeyAuth) {
549 final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
550 return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
551 keystoreAdapter, encryptionService);
553 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
556 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
558 private static InetSocketAddress getSocketAddress(final Host host, final int port) {
559 if (host.getDomainName() != null) {
560 return new InetSocketAddress(host.getDomainName().getValue(), port);
563 final IpAddress ipAddress = host.getIpAddress();
564 final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
565 : ipAddress.getIpv6Address().getValue();
566 return new InetSocketAddress(ip, port);
569 private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
570 // if none of yang-module-capabilities or non-module-capabilities is specified
571 // just return absent
572 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
573 return Optional.absent();
576 final List<String> capabilities = new ArrayList<>();
578 boolean overrideYangModuleCaps = false;
579 if (node.getYangModuleCapabilities() != null) {
580 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
581 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
584 //non-module capabilities should not exist in yang module capabilities
585 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
586 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
587 "List yang-module-capabilities/capability should contain only module based capabilities. "
588 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
590 boolean overrideNonModuleCaps = false;
591 if (node.getNonModuleCapabilities() != null) {
592 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
593 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
596 return Optional.of(new UserPreferences(NetconfSessionPreferences
597 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
600 protected static class NetconfConnectorDTO implements AutoCloseable {
602 private final NetconfDeviceCommunicator communicator;
603 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
605 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
606 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
607 this.communicator = communicator;
608 this.facade = facade;
611 public NetconfDeviceCommunicator getCommunicator() {
615 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
619 public NetconfClientSessionListener getSessionListener() {
624 public void close() {
625 communicator.close();