BUG 8839: Revert "Make netconf utilize encrypted passwords only"
[netconf.git] / netconf / netconf-topology / src / main / java / org / opendaylight / netconf / topology / AbstractNetconfTopology.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netconf.topology;
10
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 io.netty.util.concurrent.EventExecutor;
19 import java.io.File;
20 import java.math.BigDecimal;
21 import java.net.InetSocketAddress;
22 import java.net.URL;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
28 import org.opendaylight.controller.config.threadpool.ThreadPool;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
31 import org.opendaylight.netconf.api.NetconfMessage;
32 import org.opendaylight.netconf.client.NetconfClientDispatcher;
33 import org.opendaylight.netconf.client.NetconfClientSessionListener;
34 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
35 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
36 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
37 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
38 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
39 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
40 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
41 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
42 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
43 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
44 import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl;
45 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
46 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
47 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
48 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
49 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
50 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
51 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
52 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
53 import org.opendaylight.netconf.topology.api.NetconfTopology;
54 import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
55 import org.opendaylight.protocol.framework.ReconnectStrategy;
56 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
57 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
63 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
64 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
65 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
66 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
67 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
68 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
69 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
70 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
71 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
72 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
73 import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
74 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
75 import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 public abstract class AbstractNetconfTopology implements NetconfTopology {
80
81     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
82
83     protected static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L;
84     protected static final int DEFAULT_KEEPALIVE_DELAY = 0;
85     protected static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false;
86     protected static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0;
87     private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
88     private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
89     private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
90     private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
91
92     // constants related to Schema Cache(s)
93     /**
94      * Filesystem based caches are stored relative to the cache directory.
95      */
96     private static final String CACHE_DIRECTORY = "cache";
97
98     /**
99      * The default cache directory relative to <code>CACHE_DIRECTORY</code>.
100      */
101     private static final String DEFAULT_CACHE_DIRECTORY = "schema";
102
103     /**
104      * The qualified schema cache directory <code>cache/schema</code>.
105      */
106     private static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY =
107             CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY;
108
109     /**
110      * The name for the default schema repository.
111      */
112     private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
113
114     /**
115      * The default schema repository in the case that one is not specified.
116      */
117     private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
118             new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
119
120     /**
121      * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
122      */
123     private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
124             new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
125                     new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
126
127     /**
128      * The default factory for creating <code>SchemaContext</code> instances.
129      */
130     private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
131             DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
132
133     /**
134      * Keeps track of initialized Schema resources.  A Map is maintained in which the key represents the name
135      * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>.  The
136      * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
137      * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
138      * Netconf mount.  Access to <code>SCHEMA_RESOURCES_DTO_MAP</code> should be surrounded by appropriate
139      * synchronization locks.
140      */
141     private static final Map<String, NetconfDevice.SchemaResourcesDTO> SCHEMA_RESOURCES_DTO_MAP = new HashMap<>();
142
143     // Initializes default constant instances for the case when the default schema repository
144     // directory cache/schema is used.
145     static {
146         SCHEMA_RESOURCES_DTO_MAP.put(DEFAULT_CACHE_DIRECTORY,
147                 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
148                         DEFAULT_SCHEMA_CONTEXT_FACTORY,
149                         new NetconfStateSchemasResolverImpl()));
150         DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
151         DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
152                 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
153     }
154
155     protected final String topologyId;
156     private final NetconfClientDispatcher clientDispatcher;
157     private final EventExecutor eventExecutor;
158     protected final ScheduledThreadPool keepaliveExecutor;
159     protected final ThreadPool processingExecutor;
160     protected final SharedSchemaRepository sharedSchemaRepository;
161     protected final DataBroker dataBroker;
162     protected final DOMMountPointService mountPointService;
163
164     protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
165     protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
166     protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
167
168     protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
169
170     protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
171                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
172                                       final ThreadPool processingExecutor,
173                                       final SchemaRepositoryProvider schemaRepositoryProvider,
174                                       final DataBroker dataBroker, final DOMMountPointService mountPointService) {
175         this.topologyId = topologyId;
176         this.clientDispatcher = clientDispatcher;
177         this.eventExecutor = eventExecutor;
178         this.keepaliveExecutor = keepaliveExecutor;
179         this.processingExecutor = processingExecutor;
180         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
181         this.dataBroker = dataBroker;
182         this.mountPointService = mountPointService;
183     }
184
185     public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
186         this.schemaRegistry = schemaRegistry;
187     }
188
189     public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
190         this.schemaContextFactory = schemaContextFactory;
191     }
192
193     @Override
194     public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
195         LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
196         return setupConnection(nodeId, configNode);
197     }
198
199     @Override
200     public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
201         LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
202         if (!activeConnectors.containsKey(nodeId)) {
203             return Futures.immediateFailedFuture(
204                     new IllegalStateException("Unable to disconnect device that is not connected"));
205         }
206
207         // retrieve connection, and disconnect it
208         final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
209         connectorDTO.getCommunicator().close();
210         connectorDTO.getFacade().close();
211         return Futures.immediateFuture(null);
212     }
213
214     protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
215                                                                         final Node configNode) {
216         final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class);
217
218         Preconditions.checkNotNull(netconfNode.getHost());
219         Preconditions.checkNotNull(netconfNode.getPort());
220         Preconditions.checkNotNull(netconfNode.isTcpOnly());
221
222         final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode);
223         final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
224         final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
225         final NetconfReconnectingClientConfiguration clientConfig =
226                 getClientConfig(netconfClientSessionListener, netconfNode);
227         final ListenableFuture<NetconfDeviceCapabilities> future =
228                 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
229
230         activeConnectors.put(nodeId, deviceCommunicatorDTO);
231
232         Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
233             @Override
234             public void onSuccess(final NetconfDeviceCapabilities result) {
235                 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
236             }
237
238             @Override
239             public void onFailure(final Throwable throwable) {
240                 LOG.error("Connector for : " + nodeId.getValue() + " failed");
241                 // remove this node from active connectors?
242             }
243         });
244
245         return future;
246     }
247
248     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
249                                                          final NetconfNode node) {
250         //setup default values since default value is not supported in mdsal
251         final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
252                 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
253         final Long keepaliveDelay = node.getKeepaliveDelay() == null
254                 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
255         final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
256                 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
257
258         final IpAddress ipAddress = node.getHost().getIpAddress();
259         final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
260                 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
261                 node.getPort().getValue());
262         final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
263
264         RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
265                 createSalFacade(remoteDeviceId);
266
267         if (keepaliveDelay > 0) {
268             LOG.warn("Adding keepalive facade, for device {}", nodeId);
269             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(),
270                     keepaliveDelay, defaultRequestTimeoutMillis);
271         }
272
273         // pre register yang library sources as fallback schemas to schema registry
274         final List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
275         if (node.getYangLibrary() != null) {
276             final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
277             final String yangLibUsername = node.getYangLibrary().getUsername();
278             final String yangLigPassword = node.getYangLibrary().getPassword();
279
280             final LibraryModulesSchemas libraryModulesSchemas;
281             if (yangLibURL != null) {
282                 if (yangLibUsername != null && yangLigPassword != null) {
283                     libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
284                 } else {
285                     libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
286                 }
287
288                 for (final Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry
289                         : libraryModulesSchemas.getAvailableModels().entrySet()) {
290                     registeredYangLibSources
291                         .add(schemaRegistry.registerSchemaSource(
292                                 new YangLibrarySchemaYangSourceProvider(remoteDeviceId,
293                                         libraryModulesSchemas.getAvailableModels()),
294                                 PotentialSchemaSource.create(sourceIdentifierURLEntry.getKey(),
295                                         YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
296                 }
297             }
298         }
299
300         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
301         final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
302         if (node.isSchemaless()) {
303             device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
304         } else {
305             device = new NetconfDeviceBuilder()
306                     .setReconnectOnSchemasChange(reconnectOnChangedSchema)
307                     .setSchemaResourcesDTO(schemaResourcesDTO)
308                     .setGlobalProcessingExecutor(processingExecutor.getExecutor())
309                     .setId(remoteDeviceId)
310                     .setSalFacade(salFacade)
311                     .build();
312         }
313
314         final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
315         final int rpcMessageLimit =
316                 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
317
318         if (rpcMessageLimit < 1) {
319             LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
320         }
321
322         return new NetconfConnectorDTO(userCapabilities.isPresent()
323                 ? new NetconfDeviceCommunicator(remoteDeviceId, device, userCapabilities.get(), rpcMessageLimit)
324                 : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade);
325     }
326
327     protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
328         // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
329         NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
330         final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
331         // Only checks to ensure the String is not empty or null; further checks related to directory
332         // accessibility and file permissionsare handled during the FilesystemSchemaSourceCache initialization.
333         if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
334             // If a custom schema cache directory is specified, create the backing DTO; otherwise,
335             // the SchemaRegistry and SchemaContextFactory remain the default values.
336             if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
337                 // Multiple modules may be created at once;
338                 // synchronize to avoid issues with data consistency among threads.
339                 synchronized (SCHEMA_RESOURCES_DTO_MAP) {
340                     // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables
341                     // if they already exist
342                     schemaResourcesDTO = SCHEMA_RESOURCES_DTO_MAP.get(moduleSchemaCacheDirectory);
343                     if (schemaResourcesDTO == null) {
344                         schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
345                         schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
346                                 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(),
347                                         schemaResourcesDTO.getSchemaRegistry())
348                         );
349                         SCHEMA_RESOURCES_DTO_MAP.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
350                     }
351                 }
352                 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
353                         nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
354             }
355         } else {
356             LOG.warn("schema-cache-directory for {} is null or empty;  using the default {}",
357                     nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
358         }
359
360         if (schemaResourcesDTO == null) {
361             schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository,
362                     schemaContextFactory, new NetconfStateSchemasResolverImpl());
363         }
364
365         return schemaResourcesDTO;
366     }
367
368     /**
369      * Creates the backing Schema classes for a particular directory.
370      *
371      * @param moduleSchemaCacheDirectory The string directory relative to "cache"
372      * @return A DTO containing the Schema classes for the Netconf mount.
373      */
374     private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
375         final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
376         final SchemaContextFactory schemaContextFactory
377                 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
378         setSchemaRegistry(repository);
379         setSchemaContextFactory(schemaContextFactory);
380         final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
381                 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
382         repository.registerSchemaSourceListener(deviceCache);
383         return new NetconfDevice.SchemaResourcesDTO(repository, repository, schemaContextFactory,
384                 new NetconfStateSchemasResolverImpl());
385     }
386
387     /**
388      * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
389      *
390      * @param schemaCacheDirectory The custom cache directory relative to "cache"
391      * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
392      */
393     private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(
394             final String schemaCacheDirectory) {
395         final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
396         return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class,
397                 new File(relativeSchemaCacheDirectory));
398     }
399
400     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
401                                                                   final NetconfNode node) {
402
403         //setup default values since default value is not supported in mdsal
404         final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
405                 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
406         final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
407                 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
408         final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
409                 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
410         final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
411
412         final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
413
414         final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
415                 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
416         final ReconnectStrategy strategy = sf.createReconnectStrategy();
417
418         final AuthenticationHandler authHandler;
419         final Credentials credentials = node.getCredentials();
420         if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114
421                 .netconf.node.credentials.credentials.LoginPassword) {
422             authHandler = new LoginPassword(
423                     ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114
424                             .netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(),
425                     ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114
426                             .netconf.node.credentials.credentials.LoginPassword) credentials).getPassword());
427         } else {
428             throw new IllegalStateException("Only login/password authentification is supported");
429         }
430
431         return NetconfReconnectingClientConfigurationBuilder.create()
432                 .withAddress(socketAddress)
433                 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
434                 .withReconnectStrategy(strategy)
435                 .withAuthHandler(authHandler)
436                 .withProtocol(node.isTcpOnly() ? NetconfClientConfiguration.NetconfClientProtocol.TCP :
437                         NetconfClientConfiguration.NetconfClientProtocol.SSH)
438                 .withConnectStrategyFactory(sf)
439                 .withSessionListener(listener)
440                 .build();
441     }
442
443     protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
444
445     private InetSocketAddress getSocketAddress(final Host host, final int port) {
446         if (host.getDomainName() != null) {
447             return new InetSocketAddress(host.getDomainName().getValue(), port);
448         } else {
449             final IpAddress ipAddress = host.getIpAddress();
450             final String ip = ipAddress.getIpv4Address() != null
451                     ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
452             return new InetSocketAddress(ip, port);
453         }
454     }
455
456     private Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
457         // if none of yang-module-capabilities or non-module-capabilities is specified
458         // just return absent
459         if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
460             return Optional.absent();
461         }
462
463         final List<String> capabilities = new ArrayList<>();
464
465         boolean overrideYangModuleCaps = false;
466         if (node.getYangModuleCapabilities() != null) {
467             capabilities.addAll(node.getYangModuleCapabilities().getCapability());
468             overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
469         }
470
471         //non-module capabilities should not exist in yang module capabilities
472         final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
473         Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
474                 "List yang-module-capabilities/capability should contain only module based capabilities. "
475                         + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
476
477         boolean overrideNonModuleCaps = false;
478         if (node.getNonModuleCapabilities() != null) {
479             capabilities.addAll(node.getNonModuleCapabilities().getCapability());
480             overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
481         }
482
483         return Optional.of(new UserPreferences(NetconfSessionPreferences
484             .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
485     }
486
487     private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
488         private final Long connectionAttempts;
489         private final EventExecutor executor;
490         private final double sleepFactor;
491         private final int minSleep;
492
493         TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts,
494                                       final int minSleep, final BigDecimal sleepFactor) {
495             if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
496                 connectionAttempts = maxConnectionAttempts;
497             } else {
498                 connectionAttempts = null;
499             }
500
501             this.sleepFactor = sleepFactor.doubleValue();
502             this.executor = executor;
503             this.minSleep = minSleep;
504         }
505
506         @Override
507         public ReconnectStrategy createReconnectStrategy() {
508             final Long maxSleep = null;
509             final Long deadline = null;
510
511             return new TimedReconnectStrategy(executor, minSleep,
512                     minSleep, sleepFactor, maxSleep, connectionAttempts, deadline);
513         }
514     }
515
516     protected static class NetconfConnectorDTO implements AutoCloseable {
517
518         private final NetconfDeviceCommunicator communicator;
519         private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
520
521         public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
522                                    final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
523             this.communicator = communicator;
524             this.facade = facade;
525         }
526
527         public NetconfDeviceCommunicator getCommunicator() {
528             return communicator;
529         }
530
531         public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
532             return facade;
533         }
534
535         public NetconfClientSessionListener getSessionListener() {
536             return communicator;
537         }
538
539         @Override
540         public void close() {
541             communicator.close();
542             facade.close();
543         }
544     }
545 }