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