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.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.Futures;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import io.netty.util.concurrent.EventExecutor;
21 import java.math.BigDecimal;
22 import java.net.InetSocketAddress;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
29 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
30 import org.opendaylight.controller.config.threadpool.ThreadPool;
31 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
32 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
33 import org.opendaylight.netconf.api.NetconfMessage;
34 import org.opendaylight.netconf.client.NetconfClientDispatcher;
35 import org.opendaylight.netconf.client.NetconfClientSessionListener;
36 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
37 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
38 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
39 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
40 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
41 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
42 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
43 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
44 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
45 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
46 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
47 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
48 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
49 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
50 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
51 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
52 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
53 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
54 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
55 import org.opendaylight.netconf.topology.api.NetconfTopology;
56 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
57 import org.opendaylight.protocol.framework.ReconnectStrategy;
58 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
59 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
61 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecated;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.based.KeyPair;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
71 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
72 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
73 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
74 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
75 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
76 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
77 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
78 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
79 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
80 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
81 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
82 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
83 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
84 import org.slf4j.Logger;
85 import org.slf4j.LoggerFactory;
87 public abstract class AbstractNetconfTopology implements NetconfTopology {
89 private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
91 protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
92 protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
93 protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
94 protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
95 private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
96 private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
97 private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
98 private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
100 // constants related to Schema Cache(s)
102 * Filesystem based caches are stored relative to the cache directory.
104 private static final String CACHE_DIRECTORY = "cache";
107 * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
109 private static final String DEFAULT_CACHE_DIRECTORY = "schema";
112 * The qualified schema cache directory <code>cache/schema</code>.
114 private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
115 CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
118 * The name for the default schema repository.
120 private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
123 * The default schema repository in the case that one is not specified.
125 private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
126 new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
129 * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
131 private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
132 new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
133 new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
136 * The default factory for creating <code>SchemaContext</code> instances.
138 private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
139 DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
142 * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name
143 * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>. The
144 * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
145 * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
146 * Netconf mount. Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
147 * synchronization locks.
149 private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
151 // Initializes default constant instances for the case when the default schema repository
152 // directory cache/schema is used.
154 SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
155 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
156 DEFAULT_SCHEMA_CONTEXT_FACTORY,
157 new NetconfStateSchemasResolverImpl()));
158 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
159 DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
160 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
163 protected final String topologyId;
164 private final NetconfClientDispatcher clientDispatcher;
165 private final EventExecutor eventExecutor;
166 protected final ScheduledThreadPool keepaliveExecutor;
167 protected final ThreadPool processingExecutor;
168 protected final SharedSchemaRepository sharedSchemaRepository;
169 protected final DataBroker dataBroker;
170 protected final DOMMountPointService mountPointService;
171 protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
172 protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
173 protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
174 protected String privateKeyPath;
175 protected String privateKeyPassphrase;
176 protected final AAAEncryptionService encryptionService;
177 protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
179 protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
180 final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
181 final ThreadPool processingExecutor,
182 final SchemaRepositoryProvider schemaRepositoryProvider,
183 final DataBroker dataBroker, final DOMMountPointService mountPointService,
184 final AAAEncryptionService encryptionService) {
185 this.topologyId = topologyId;
186 this.clientDispatcher = clientDispatcher;
187 this.eventExecutor = eventExecutor;
188 this.keepaliveExecutor = keepaliveExecutor;
189 this.processingExecutor = processingExecutor;
190 this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
191 this.dataBroker = dataBroker;
192 this.mountPointService = mountPointService;
193 this.encryptionService = encryptionService;
196 public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
197 this.schemaRegistry = schemaRegistry;
200 public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
201 this.schemaContextFactory = schemaContextFactory;
205 public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
206 LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
207 return setupConnection(nodeId, configNode);
211 public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
212 LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
213 if (!activeConnectors.containsKey(nodeId)) {
214 return Futures.immediateFailedFuture(
215 new IllegalStateException("Unable to disconnect device that is not connected"));
218 // retrieve connection, and disconnect it
219 final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
220 connectorDTO.getCommunicator().close();
221 connectorDTO.getFacade().close();
222 return Futures.immediateFuture(null);
225 protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
226 final Node configNode) {
227 final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
229 Preconditions.checkNotNull(netconfNode.getHost());
230 Preconditions.checkNotNull(netconfNode.getPort());
231 Preconditions.checkNotNull(netconfNode.isTcpOnly());
233 final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
234 final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
235 final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
236 final NetconfReconnectingClientConfiguration clientConfig =
237 getClientConfig(netconfClientSessionListener, netconfNode);
238 final ListenableFuture<NetconfDeviceCapabilities> future =
239 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
241 activeConnectors.put(nodeId, deviceCommunicatorDTO);
243 Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
245 public void onSuccess(final NetconfDeviceCapabilities result) {
246 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
250 public void onFailure(final Throwable throwable) {
251 LOG.error("Connector for : " + nodeId.getValue() + " failed");
252 // remove this node from active connectors?
254 }, MoreExecutors.directExecutor());
259 protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
260 final NetconfNode node) {
261 //setup default values since default value is not supported in mdsal
262 final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
263 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
264 final Long keepaliveDelay = node.getKeepaliveDelay() == null
265 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
266 final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
267 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
269 final IpAddress ipAddress = node.getHost().getIpAddress();
270 final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
271 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
272 node.getPort().getValue());
273 final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
275 RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
276 createSalFacade(remoteDeviceId);
278 if (keepaliveDelay > 0) {
279 LOG.warn("Adding keepalive facade, for device {}", nodeId);
280 salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
281 keepaliveDelay, defaultRequestTimeoutMillis);
284 // pre register yang library sources as fallback schemas to schema registry
285 final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
286 if (node.getYangLibrary() != null) {
287 final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
288 final String yangLibUsername = node.getYangLibrary().getUsername();
289 final String yangLigPassword = node.getYangLibrary().getPassword();
291 final LibraryModulesSchemas libraryModulesSchemas;
292 if (yangLibURL != null) {
293 if (yangLibUsername != null && yangLigPassword != null) {
294 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
296 libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
299 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
300 : libraryModulesSchemas.getAvailableModels().entrySet()) {
301 registeredYangLibSources
302 .add(schemaRegistry.registerSchemaSource(
303 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
304 libraryModulesSchemas.getAvailableModels()),
305 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
306 YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
311 final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
312 final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
313 if (node.isSchemaless()) {
314 device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
316 device = new NetconfDeviceBuilder()
317 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
318 .setSchemaResourcesDTO(schemaResourcesDTO)
319 .setGlobalProcessingExecutor(processingExecutor.getExecutor())
320 .setId(remoteDeviceId)
321 .setSalFacade(salFacade)
325 final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
326 final int rpcMessageLimit =
327 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
329 if (rpcMessageLimit < 1) {
330 LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
333 return new NetconfConnectorDTO(userCapabilities.isPresent()
334 ? new NetconfDeviceCommunicator(remoteDeviceId, device, userCapabilities.get(), rpcMessageLimit)
335 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade);
338 protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
339 // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
340 NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
341 final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
342 // Only checks to ensure the String is not empty or null; further checks related to directory
343 // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
344 if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
345 // If a custom schema cache directory is specified, create the backing DTO; otherwise,
346 // the SchemaRegistry and SchemaContextFactory remain the default values.
347 if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
348 // Multiple modules may be created at once;
349 // synchronize to avoid issues with data consistency among threads.
350 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
351 // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
352 // if they already exist
353 schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
354 if (schemaResourcesDTO == null) {
355 schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
356 schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
357 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
358 schemaResourcesDTO.getSchemaRegistry())
360 SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
363 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
364 nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
367 LOG.warn("schema-cache-directory for {} is null or empty; using the default {}",
368 nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
371 if (schemaResourcesDTO == null) {
372 schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
373 schemaContextFactory, new NetconfStateSchemasResolverImpl());
376 return schemaResourcesDTO;
380 * Creates the backing Schema classes for a particular directory.
382 * @param moduleSchemaCacheDirectory The string directory relative to "cache"
383 * @return A DTO containing the Schema classes for the Netconf mount.
385 private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
386 final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
387 final SchemaContextFactory schemaContextFactory
388 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
389 setSchemaRegistry(repository);
390 setSchemaContextFactory(schemaContextFactory);
391 final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
392 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
393 repository.registerSchemaSourceListener(deviceCache);
394 return new NetconfDevice.SchemaResourcesDTO(repository, repository, schemaContextFactory,
395 new NetconfStateSchemasResolverImpl());
399 * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
401 * @param schemaCacheDirectory The custom cache directory relative to "cache"
402 * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
404 private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
405 final String schemaCacheDirectory) {
406 final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
407 return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
408 new File(relativeSchemaCacheDirectory));
412 * Sets the private key path from location specified in configuration file using blueprint.
414 public void setPrivateKeyPath(String privateKeyPath) {
415 this.privateKeyPath = privateKeyPath;
419 * Sets the private key passphrase from location specified in configuration file using blueprint.
421 public void setPrivateKeyPassphrase(String privateKeyPassphrase) {
422 this.privateKeyPassphrase = privateKeyPassphrase;
425 public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
426 final NetconfNode node) {
428 //setup default values since default value is not supported in mdsal
429 final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
430 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
431 final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
432 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
433 final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
434 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
435 final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
437 final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
439 final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
440 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
441 final ReconnectStrategy strategy = sf.createReconnectStrategy();
443 final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials());
445 return NetconfReconnectingClientConfigurationBuilder.create()
446 .withAddress(socketAddress)
447 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
448 .withReconnectStrategy(strategy)
449 .withAuthHandler(authHandler)
450 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
451 NetconfClientConfiguration.NetconfClientProtocol.SSH)
452 .withConnectStrategyFactory(sf)
453 .withSessionListener(listener)
457 private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
458 if (credentials instanceof LoginPasswordDeprecated) {
459 final LoginPasswordDeprecated loginPassword = (LoginPasswordDeprecated) credentials;
460 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
462 if (credentials instanceof LoginPwUnencrypted) {
463 final LoginPasswordUnencrypted loginPassword =
464 ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
465 return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
467 if (credentials instanceof LoginPw) {
468 final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
469 return new LoginPasswordHandler(loginPassword.getUsername(),
470 encryptionService.decrypt(loginPassword.getPassword()));
472 if (credentials instanceof KeyPair) {
473 throw new UnsupportedOperationException("Not implemented yet");
475 throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
478 protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
480 private InetSocketAddress getSocketAddress(final Host host, final int port) {
481 if (host.getDomainName() != null) {
482 return new InetSocketAddress(host.getDomainName().getValue(), port);
484 final IpAddress ipAddress = host.getIpAddress();
485 final String ip = ipAddress.getIpv4Address() != null
486 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
487 return new InetSocketAddress(ip, port);
491 private Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
492 // if none of yang-module-capabilities or non-module-capabilities is specified
493 // just return absent
494 if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
495 return Optional.absent();
498 final List<String> capabilities = new ArrayList<>();
500 boolean overrideYangModuleCaps = false;
501 if (node.getYangModuleCapabilities() != null) {
502 capabilities.addAll(node.getYangModuleCapabilities().getCapability());
503 overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
506 //non-module capabilities should not exist in yang module capabilities
507 final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
508 Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
509 "List yang-module-capabilities/capability should contain only module based capabilities. "
510 + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
512 boolean overrideNonModuleCaps = false;
513 if (node.getNonModuleCapabilities() != null) {
514 capabilities.addAll(node.getNonModuleCapabilities().getCapability());
515 overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
518 return Optional.of(new UserPreferences(NetconfSessionPreferences
519 .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
522 private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
523 private final Long connectionAttempts;
524 private final EventExecutor executor;
525 private final double sleepFactor;
526 private final int minSleep;
528 TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
529 final int minSleep, final BigDecimal sleepFactor) {
530 if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
531 connectionAttempts = maxConnectionAttempts;
533 connectionAttempts = null;
536 this.sleepFactor = sleepFactor.doubleValue();
537 this.executor = executor;
538 this.minSleep = minSleep;
542 public ReconnectStrategy createReconnectStrategy() {
543 final Long maxSleep = null;
544 final Long deadline = null;
546 return new TimedReconnectStrategy(executor, minSleep,
547 minSleep, sleepFactor, maxSleep, connectionAttempts, deadline);
551 protected static class NetconfConnectorDTO implements AutoCloseable {
553 private final NetconfDeviceCommunicator communicator;
554 private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
556 public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
557 final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
558 this.communicator = communicator;
559 this.facade = facade;
562 public NetconfDeviceCommunicator getCommunicator() {
566 public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
570 public NetconfClientSessionListener getSessionListener() {
575 public void close() {
576 communicator.close();