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