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