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