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