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