Remove unused reference to DOMMountPointService
[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.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
27 import org.opendaylight.controller.config.threadpool.ThreadPool;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
30 import org.opendaylight.controller.sal.core.api.Broker;
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 = CACHE_DIRECTORY + File.separator+ DEFAULT_CACHE_DIRECTORY;
107
108     /**
109      * The name for the default schema repository
110      */
111     private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector";
112
113     /**
114      * The default schema repository in the case that one is not specified.
115      */
116     private static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY =
117             new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME);
118
119     /**
120      * The default <code>FilesystemSchemaSourceCache</code>, which stores cached files in <code>cache/schema</code>.
121      */
122     private static final FilesystemSchemaSourceCache<YangTextSchemaSource> DEFAULT_CACHE =
123             new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class,
124                     new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY));
125
126     /**
127      * The default factory for creating <code>SchemaContext</code> instances.
128      */
129     private static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY =
130             DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
131
132     /**
133      * Keeps track of initialized Schema resources.  A Map is maintained in which the key represents the name
134      * of the schema cache directory, and the value is a corresponding <code>SchemaResourcesDTO</code>.  The
135      * <code>SchemaResourcesDTO</code> is essentially a container that allows for the extraction of the
136      * <code>SchemaRegistry</code> and <code>SchemaContextFactory</code> which should be used for a particular
137      * Netconf mount.  Access to <code>schemaResourcesDTOs</code> should be surrounded by appropriate
138      * synchronization locks.
139      */
140     private static volatile Map<String, NetconfDevice.SchemaResourcesDTO> schemaResourcesDTOs = new HashMap<>();
141
142     // Initializes default constant instances for the case when the default schema repository
143     // directory cache/schema is used.
144     static {
145         schemaResourcesDTOs.put(DEFAULT_CACHE_DIRECTORY,
146                 new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY,
147                         DEFAULT_SCHEMA_CONTEXT_FACTORY,
148                         new NetconfStateSchemasResolverImpl()));
149         DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE);
150         DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(
151                 TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY));
152     }
153
154     protected final String topologyId;
155     private final NetconfClientDispatcher clientDispatcher;
156     protected final BindingAwareBroker bindingAwareBroker;
157     protected final Broker domBroker;
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
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 BindingAwareBroker bindingAwareBroker, final Broker domBroker,
172                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
173                                       final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider,
174                                       final DataBroker dataBroker) {
175         this.topologyId = topologyId;
176         this.clientDispatcher = clientDispatcher;
177         this.bindingAwareBroker = bindingAwareBroker;
178         this.domBroker = domBroker;
179         this.eventExecutor = eventExecutor;
180         this.keepaliveExecutor = keepaliveExecutor;
181         this.processingExecutor = processingExecutor;
182         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
183         this.dataBroker = dataBroker;
184     }
185
186     public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
187         this.schemaRegistry = schemaRegistry;
188     }
189
190     public void setSchemaContextFactory(final SchemaContextFactory schemaContextFactory) {
191         this.schemaContextFactory = schemaContextFactory;
192     }
193
194     @Override
195     public ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode) {
196         LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
197         return setupConnection(nodeId, configNode);
198     }
199
200     @Override
201     public ListenableFuture<Void> disconnectNode(NodeId nodeId) {
202         LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
203         if (!activeConnectors.containsKey(nodeId)) {
204             return Futures.immediateFailedFuture(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 = getClientConfig(netconfClientSessionListener, netconfNode);
226         final ListenableFuture<NetconfDeviceCapabilities> future = deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
227
228         activeConnectors.put(nodeId, deviceCommunicatorDTO);
229
230         Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
231             @Override
232             public void onSuccess(NetconfDeviceCapabilities result) {
233                 LOG.debug("Connector for : " + nodeId.getValue() + " started succesfully");
234             }
235
236             @Override
237             public void onFailure(Throwable t) {
238                 LOG.error("Connector for : " + nodeId.getValue() + " failed");
239                 // remove this node from active connectors?
240             }
241         });
242
243         return future;
244     }
245
246     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId,
247                                                          final NetconfNode node) {
248         //setup default values since default value is not supported in mdsal
249         final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis();
250         final Long keepaliveDelay = node.getKeepaliveDelay() == null ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay();
251         final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
252
253         IpAddress ipAddress = node.getHost().getIpAddress();
254         InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null ?
255                 ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
256                 node.getPort().getValue());
257         RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
258
259         RemoteDeviceHandler<NetconfSessionPreferences> salFacade =
260                 createSalFacade(remoteDeviceId, domBroker, bindingAwareBroker);
261
262         if (keepaliveDelay > 0) {
263             LOG.warn("Adding keepalive facade, for device {}", nodeId);
264             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, keepaliveExecutor.getExecutor(), keepaliveDelay, defaultRequestTimeoutMillis);
265         }
266
267         // pre register yang library sources as fallback schemas to schema registry
268         List<SchemaSourceRegistration<YangTextSchemaSource>> registeredYangLibSources = Lists.newArrayList();
269         if (node.getYangLibrary() != null) {
270             final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue();
271             final String yangLibUsername = node.getYangLibrary().getUsername();
272             final String yangLigPassword = node.getYangLibrary().getPassword();
273
274             LibraryModulesSchemas libraryModulesSchemas;
275             if(yangLibURL != null) {
276                 if(yangLibUsername != null && yangLigPassword != null) {
277                     libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
278                 } else {
279                     libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL);
280                 }
281
282                 for (Map.Entry<SourceIdentifier, URL> sourceIdentifierURLEntry : libraryModulesSchemas.getAvailableModels().entrySet()) {
283                     registeredYangLibSources.
284                             add(schemaRegistry.registerSchemaSource(
285                                     new YangLibrarySchemaYangSourceProvider(remoteDeviceId, libraryModulesSchemas.getAvailableModels()),
286                                     PotentialSchemaSource
287                                             .create(sourceIdentifierURLEntry.getKey(), YangTextSchemaSource.class,
288                                             PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
289                 }
290             }
291         }
292
293         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node);
294         final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
295         if (node.isSchemaless()) {
296             device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
297         } else {
298             device = new NetconfDeviceBuilder()
299                     .setReconnectOnSchemasChange(reconnectOnChangedSchema)
300                     .setSchemaResourcesDTO(schemaResourcesDTO)
301                     .setGlobalProcessingExecutor(processingExecutor.getExecutor())
302                     .setId(remoteDeviceId)
303                     .setSalFacade(salFacade)
304                     .build();
305         }
306
307         final Optional<NetconfSessionPreferences> userCapabilities = getUserCapabilities(node);
308         final int rpcMessageLimit =
309                 node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit();
310
311         if (rpcMessageLimit < 1) {
312             LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
313         }
314
315         return new NetconfConnectorDTO(
316                 userCapabilities.isPresent() ?
317                         new NetconfDeviceCommunicator(
318                                 remoteDeviceId, device, new UserPreferences(userCapabilities.get(), node.getYangModuleCapabilities().isOverride()), rpcMessageLimit):
319                         new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade);
320     }
321
322     protected NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) {
323         // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc.
324         NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null;
325         final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory();
326         // Only checks to ensure the String is not empty or null;  further checks related to directory accessibility and file permissions
327         // are handled during the FilesystemSchemaSourceCache initialization.
328         if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) {
329             // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry and
330             // SchemaContextFactory remain the default values.
331             if (!moduleSchemaCacheDirectory.equals(DEFAULT_CACHE_DIRECTORY)) {
332                 // Multiple modules may be created at once;  synchronize to avoid issues with data consistency among threads.
333                 synchronized(schemaResourcesDTOs) {
334                     // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if they already exist
335                     schemaResourcesDTO = schemaResourcesDTOs.get(moduleSchemaCacheDirectory);
336                     if (schemaResourcesDTO == null) {
337                         schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory);
338                         schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener(
339                                 TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(), schemaResourcesDTO.getSchemaRegistry())
340                         );
341                         schemaResourcesDTOs.put(moduleSchemaCacheDirectory, schemaResourcesDTO);
342                     }
343                 }
344                 LOG.info("Netconf connector for device {} will use schema cache directory {} instead of {}",
345                         nodeId.getValue(), moduleSchemaCacheDirectory, DEFAULT_CACHE_DIRECTORY);
346             }
347         } else {
348             LOG.warn("schema-cache-directory for {} is null or empty;  using the default {}",
349                     nodeId.getValue(), QUALIFIED_DEFAULT_CACHE_DIRECTORY);
350         }
351
352         if (schemaResourcesDTO == null) {
353             schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaContextFactory,
354                     new NetconfStateSchemasResolverImpl());
355         }
356
357         return schemaResourcesDTO;
358     }
359
360     /**
361      * Creates the backing Schema classes for a particular directory.
362      *
363      * @param moduleSchemaCacheDirectory The string directory relative to "cache"
364      * @return A DTO containing the Schema classes for the Netconf mount.
365      */
366     private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) {
367         final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory);
368         final SchemaContextFactory schemaContextFactory
369                 = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT);
370         setSchemaRegistry(repository);
371         setSchemaContextFactory(schemaContextFactory);
372         final FilesystemSchemaSourceCache<YangTextSchemaSource> deviceCache =
373                 createDeviceFilesystemCache(moduleSchemaCacheDirectory);
374         repository.registerSchemaSourceListener(deviceCache);
375         return new NetconfDevice.SchemaResourcesDTO(repository, repository, schemaContextFactory,
376                 new NetconfStateSchemasResolverImpl());
377     }
378
379     /**
380      * Creates a <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory.
381      *
382      * @param schemaCacheDirectory The custom cache directory relative to "cache"
383      * @return A <code>FilesystemSchemaSourceCache</code> for the custom schema cache directory
384      */
385     private FilesystemSchemaSourceCache<YangTextSchemaSource> createDeviceFilesystemCache(final String schemaCacheDirectory) {
386         final String relativeSchemaCacheDirectory = CACHE_DIRECTORY + File.separator + schemaCacheDirectory;
387         return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, new File(relativeSchemaCacheDirectory));
388     }
389
390     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener, NetconfNode node) {
391
392         //setup default values since default value is not supported in mdsal
393         final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis();
394         final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts();
395         final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis();
396         final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
397
398         final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue());
399
400         final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
401                 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
402         final ReconnectStrategy strategy = sf.createReconnectStrategy();
403
404         final AuthenticationHandler authHandler;
405         final Credentials credentials = node.getCredentials();
406         if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) {
407             authHandler = new LoginPassword(
408                     ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(),
409                     ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getPassword());
410         } else {
411             throw new IllegalStateException("Only login/password authentification is supported");
412         }
413
414         return NetconfReconnectingClientConfigurationBuilder.create()
415                 .withAddress(socketAddress)
416                 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
417                 .withReconnectStrategy(strategy)
418                 .withAuthHandler(authHandler)
419                 .withProtocol(node.isTcpOnly() ?
420                         NetconfClientConfiguration.NetconfClientProtocol.TCP :
421                         NetconfClientConfiguration.NetconfClientProtocol.SSH)
422                 .withConnectStrategyFactory(sf)
423                 .withSessionListener(listener)
424                 .build();
425     }
426
427     protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker);
428
429     private InetSocketAddress getSocketAddress(final Host host, int port) {
430         if(host.getDomainName() != null) {
431             return new InetSocketAddress(host.getDomainName().getValue(), port);
432         } else {
433             final IpAddress ipAddress = host.getIpAddress();
434             final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
435             return new InetSocketAddress(ip, port);
436         }
437     }
438
439     private Optional<NetconfSessionPreferences> getUserCapabilities(final NetconfNode node) {
440         if(node.getYangModuleCapabilities() == null) {
441             return Optional.absent();
442         }
443
444         final List<String> capabilities = node.getYangModuleCapabilities().getCapability();
445         if(capabilities == null || capabilities.isEmpty()) {
446             return Optional.absent();
447         }
448
449         final NetconfSessionPreferences parsedOverrideCapabilities = NetconfSessionPreferences.fromStrings(capabilities, CapabilityOrigin.UserDefined);
450         Preconditions.checkState(parsedOverrideCapabilities.getNonModuleCaps().isEmpty(), "Capabilities to override can " +
451                 "only contain module based capabilities, non-module capabilities will be retrieved from the device," +
452                 " configured non-module capabilities: " + parsedOverrideCapabilities.getNonModuleCaps());
453
454         return Optional.of(parsedOverrideCapabilities);
455     }
456
457     private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory {
458         private final Long connectionAttempts;
459         private final EventExecutor executor;
460         private final double sleepFactor;
461         private final int minSleep;
462
463         TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, final int minSleep, final BigDecimal sleepFactor) {
464             if (maxConnectionAttempts != null && maxConnectionAttempts > 0) {
465                 connectionAttempts = maxConnectionAttempts;
466             } else {
467                 connectionAttempts = null;
468             }
469
470             this.sleepFactor = sleepFactor.doubleValue();
471             this.executor = executor;
472             this.minSleep = minSleep;
473         }
474
475         @Override
476         public ReconnectStrategy createReconnectStrategy() {
477             final Long maxSleep = null;
478             final Long deadline = null;
479
480             return new TimedReconnectStrategy(executor, minSleep,
481                     minSleep, sleepFactor, maxSleep, connectionAttempts, deadline);
482         }
483     }
484
485     protected static class NetconfConnectorDTO implements AutoCloseable {
486
487         private final NetconfDeviceCommunicator communicator;
488         private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
489
490         public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
491             this.communicator = communicator;
492             this.facade = facade;
493         }
494
495         public NetconfDeviceCommunicator getCommunicator() {
496             return communicator;
497         }
498
499         public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
500             return facade;
501         }
502
503         public NetconfClientSessionListener getSessionListener() {
504             return communicator;
505         }
506
507         @Override
508         public void close() {
509             communicator.close();
510             facade.close();
511         }
512     }
513
514 }