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