Factor out SchemaResourceManager
[netconf.git] / netconf / netconf-topology / src / main / java / org / opendaylight / netconf / topology / spi / 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 package org.opendaylight.netconf.topology.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Preconditions;
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.ListeningExecutorService;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import io.netty.util.concurrent.EventExecutor;
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 java.util.Optional;
29 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
30 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
31 import org.opendaylight.controller.config.threadpool.ThreadPool;
32 import org.opendaylight.mdsal.binding.api.DataBroker;
33 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
34 import org.opendaylight.netconf.api.NetconfMessage;
35 import org.opendaylight.netconf.client.NetconfClientDispatcher;
36 import org.opendaylight.netconf.client.NetconfClientSessionListener;
37 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
38 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
39 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder;
40 import org.opendaylight.netconf.nettyutil.ReconnectStrategyFactory;
41 import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
42 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
43 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
44 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
45 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
46 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
47 import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager;
48 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
49 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
50 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO;
51 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
52 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
53 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
54 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
55 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
56 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
57 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
58 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
59 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
60 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
61 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
62 import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl;
63 import org.opendaylight.netconf.topology.api.NetconfTopology;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
65 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
66 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.schema.storage.YangLibrary;
80 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
81 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
82 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
83 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
84 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
85 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
86 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
89
90 public abstract class AbstractNetconfTopology implements NetconfTopology {
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 boolean DEFAULT_IS_TCP_ONLY = false;
98     private static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0;
99     private static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000;
100     private static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L;
101     private static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5);
102
103
104     private final NetconfClientDispatcher clientDispatcher;
105     private final EventExecutor eventExecutor;
106     private final DeviceActionFactory deviceActionFactory;
107     private final NetconfKeystoreAdapter keystoreAdapter;
108     private final SchemaResourceManager schemaManager;
109
110     protected final ScheduledThreadPool keepaliveExecutor;
111     protected final ListeningExecutorService processingExecutor;
112     protected final DataBroker dataBroker;
113     protected final DOMMountPointService mountPointService;
114     protected final String topologyId;
115     protected String privateKeyPath;
116     protected String privateKeyPassphrase;
117     protected final AAAEncryptionService encryptionService;
118     protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
119
120     protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
121                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
122                                       final ThreadPool processingExecutor, final SchemaResourceManager schemaManager,
123                                       final DataBroker dataBroker, final DOMMountPointService mountPointService,
124                                       final AAAEncryptionService encryptionService,
125                                       final DeviceActionFactory deviceActionFactory) {
126         this.topologyId = topologyId;
127         this.clientDispatcher = clientDispatcher;
128         this.eventExecutor = eventExecutor;
129         this.keepaliveExecutor = keepaliveExecutor;
130         this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
131         this.schemaManager = requireNonNull(schemaManager);
132         this.deviceActionFactory = deviceActionFactory;
133         this.dataBroker = dataBroker;
134         this.mountPointService = mountPointService;
135         this.encryptionService = encryptionService;
136
137         this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
138     }
139
140     @Override
141     public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
142         LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode));
143         return setupConnection(nodeId, configNode);
144     }
145
146     /**
147      * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
148      *
149      * @param nodeConfiguration Node configuration container.
150      * @return String representation of node configuration with credentials replaced by asterisks.
151      */
152     @VisibleForTesting
153     public static String hideCredentials(final Node nodeConfiguration) {
154         final NetconfNode netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
155         final String nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
156         final String nodeConfigurationString = nodeConfiguration.toString();
157         return nodeConfigurationString.replace(nodeCredentials, "***");
158     }
159
160     @Override
161     public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
162         LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
163         if (!activeConnectors.containsKey(nodeId)) {
164             return Futures.immediateFailedFuture(
165                     new IllegalStateException("Unable to disconnect device that is not connected"));
166         }
167
168         // retrieve connection, and disconnect it
169         final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
170         connectorDTO.getCommunicator().close();
171         connectorDTO.getFacade().close();
172         return Futures.immediateFuture(null);
173     }
174
175     protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
176                                                                           final Node configNode) {
177         final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
178         final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
179
180         requireNonNull(netconfNode.getHost());
181         requireNonNull(netconfNode.getPort());
182
183         final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional);
184         final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
185         final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
186         final NetconfReconnectingClientConfiguration clientConfig =
187                 getClientConfig(netconfClientSessionListener, netconfNode);
188         final ListenableFuture<NetconfDeviceCapabilities> future =
189                 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
190
191         activeConnectors.put(nodeId, deviceCommunicatorDTO);
192
193         Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
194             @Override
195             public void onSuccess(final NetconfDeviceCapabilities result) {
196                 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
197             }
198
199             @Override
200             public void onFailure(final Throwable throwable) {
201                 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
202                 // remove this node from active connectors?
203             }
204         }, MoreExecutors.directExecutor());
205
206         return future;
207     }
208
209     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
210         return createDeviceCommunicator(nodeId, node, null);
211     }
212
213     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
214             final NetconfNodeAugmentedOptional nodeOptional) {
215         //setup default values since default value is not supported in mdsal
216         final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
217                 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis().toJava();
218         final long keepaliveDelay = node.getKeepaliveDelay() == null
219                 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay().toJava();
220
221         final IpAddress ipAddress = node.getHost().getIpAddress();
222         final InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null
223                 ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(),
224                 node.getPort().getValue().toJava());
225         final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
226
227         RemoteDeviceHandler<NetconfSessionPreferences> salFacade = createSalFacade(remoteDeviceId);
228
229         if (keepaliveDelay > 0) {
230             LOG.warn("Adding keepalive facade, for device {}", nodeId);
231             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
232                     keepaliveDelay, defaultRequestTimeoutMillis);
233         }
234
235         final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
236         if (node.isSchemaless()) {
237             device = new SchemalessNetconfDevice(remoteDeviceId, salFacade);
238         } else {
239             device = createNetconfDevice(remoteDeviceId, salFacade, nodeId, node, nodeOptional);
240         }
241
242         final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
243         final int rpcMessageLimit = node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT
244             : node.getConcurrentRpcLimit().toJava();
245
246         if (rpcMessageLimit < 1) {
247             LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
248         }
249
250         NetconfDeviceCommunicator netconfDeviceCommunicator =
251              userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
252                      userCapabilities.get(), rpcMessageLimit)
253             : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
254
255         if (salFacade instanceof KeepaliveSalFacade) {
256             ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
257         }
258         return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade);
259     }
260
261     private NetconfDevice createNetconfDevice(final RemoteDeviceId remoteDeviceId,
262             final RemoteDeviceHandler<NetconfSessionPreferences> salFacade, final NodeId nodeId, final NetconfNode node,
263             final NetconfNodeAugmentedOptional nodeOptional) {
264         final boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null
265                 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema();
266
267         final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node, nodeId.getValue());
268
269         final NetconfDevice device = new NetconfDeviceBuilder()
270                 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
271                 .setSchemaResourcesDTO(resources)
272                 .setGlobalProcessingExecutor(this.processingExecutor)
273                 .setId(remoteDeviceId)
274                 .setSalFacade(salFacade)
275                 .setNode(node)
276                 .setEventExecutor(eventExecutor)
277                 .setNodeOptional(nodeOptional)
278                 .setDeviceActionFactory(deviceActionFactory)
279                 .build();
280
281         final YangLibrary yangLibrary = node.getYangLibrary();
282         if (yangLibrary != null) {
283             final Uri uri = yangLibrary.getYangLibraryUrl();
284             if (uri != null) {
285                 // FIXME: NETCONF-675: these registrations need to be torn down with the device. This does not look
286                 //                     quite right, though, as we can end up adding a lot of registrations on a
287                 //                     per-device basis.
288                 //                     This leak is also detected by SpotBugs as soon as this initialization is switched
289                 //                     to proper "new ArrayList<>" and hence we really need to attach these somewhere
290                 //                     else.
291                 //                     It seems we should be subclassing NetconfConnectorDTO for this purpose as a
292                 //                     first step and then perhaps do some refcounting or similar based on the
293                 //                     schemaRegistry instance.
294                 final List<SchemaSourceRegistration<?>> registeredYangLibSources = Lists.newArrayList();
295                 final String yangLibURL = uri.getValue();
296                 final SchemaSourceRegistry schemaRegistry = resources.getSchemaRegistry();
297
298                 // pre register yang library sources as fallback schemas to schema registry
299                 final LibraryModulesSchemas schemas;
300                 final String yangLibUsername = yangLibrary.getUsername();
301                 final String yangLigPassword = yangLibrary.getPassword();
302                 if (yangLibUsername != null && yangLigPassword != null) {
303                     schemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
304                 } else {
305                     schemas = LibraryModulesSchemas.create(yangLibURL);
306                 }
307
308                 for (final Map.Entry<SourceIdentifier, URL> entry : schemas.getAvailableModels().entrySet()) {
309                     registeredYangLibSources.add(schemaRegistry.registerSchemaSource(
310                         new YangLibrarySchemaYangSourceProvider(remoteDeviceId, schemas.getAvailableModels()),
311                         PotentialSchemaSource.create(entry.getKey(), YangTextSchemaSource.class,
312                             PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
313                 }
314             }
315         }
316
317         return device;
318     }
319
320     /**
321      * Sets the private key path from location specified in configuration file using blueprint.
322      */
323     public void setPrivateKeyPath(final String privateKeyPath) {
324         this.privateKeyPath = privateKeyPath;
325     }
326
327     /**
328      * Sets the private key passphrase from location specified in configuration file using blueprint.
329      */
330     public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
331         this.privateKeyPassphrase = privateKeyPassphrase;
332     }
333
334     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
335                                                                   final NetconfNode node) {
336
337         //setup default values since default value is not supported in mdsal
338         final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
339                 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis().toJava();
340         final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
341                 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts().toJava();
342         final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
343                 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis().toJava();
344         final boolean useTcp = node.isTcpOnly() == null ? DEFAULT_IS_TCP_ONLY : node.isTcpOnly();
345         final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
346
347         final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue().toJava());
348
349         final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
350                 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
351
352         final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
353         final Protocol protocol = node.getProtocol();
354         if (useTcp) {
355             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
356                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
357                     .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
358         } else if (protocol == null || protocol.getName() == Name.SSH) {
359             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
360                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
361                     .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
362         } else if (protocol.getName() == Name.TLS) {
363             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
364                 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
365                 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
366         } else {
367             throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
368         }
369
370         if (node.getOdlHelloMessageCapabilities() != null) {
371             reconnectingClientConfigurationBuilder
372                     .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
373         }
374
375         return reconnectingClientConfigurationBuilder
376                 .withAddress(socketAddress)
377                 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
378                 .withReconnectStrategy(sf.createReconnectStrategy())
379                 .withConnectStrategyFactory(sf)
380                 .withSessionListener(listener)
381                 .build();
382     }
383
384     private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
385         if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
386                 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
387             final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
388                     .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
389                     = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
390                     .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
391             return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
392         }
393         if (credentials instanceof LoginPwUnencrypted) {
394             final LoginPasswordUnencrypted loginPassword =
395                     ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
396             return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
397         }
398         if (credentials instanceof LoginPw) {
399             final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
400             return new LoginPasswordHandler(loginPassword.getUsername(),
401                     encryptionService.decrypt(loginPassword.getPassword()));
402         }
403         if (credentials instanceof KeyAuth) {
404             final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
405             return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
406                     keystoreAdapter, encryptionService);
407         }
408         throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
409     }
410
411     protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
412
413     private static InetSocketAddress getSocketAddress(final Host host, final int port) {
414         if (host.getDomainName() != null) {
415             return new InetSocketAddress(host.getDomainName().getValue(), port);
416         }
417
418         final IpAddress ipAddress = host.getIpAddress();
419         final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
420                 : ipAddress.getIpv6Address().getValue();
421         return new InetSocketAddress(ip, port);
422     }
423
424     private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
425         // if none of yang-module-capabilities or non-module-capabilities is specified
426         // just return absent
427         if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
428             return Optional.empty();
429         }
430
431         final List<String> capabilities = new ArrayList<>();
432
433         boolean overrideYangModuleCaps = false;
434         if (node.getYangModuleCapabilities() != null) {
435             capabilities.addAll(node.getYangModuleCapabilities().getCapability());
436             overrideYangModuleCaps = node.getYangModuleCapabilities().isOverride();
437         }
438
439         //non-module capabilities should not exist in yang module capabilities
440         final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
441         Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
442                 "List yang-module-capabilities/capability should contain only module based capabilities. "
443                         + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
444
445         boolean overrideNonModuleCaps = false;
446         if (node.getNonModuleCapabilities() != null) {
447             capabilities.addAll(node.getNonModuleCapabilities().getCapability());
448             overrideNonModuleCaps = node.getNonModuleCapabilities().isOverride();
449         }
450
451         return Optional.of(new UserPreferences(NetconfSessionPreferences
452             .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
453     }
454
455     protected static class NetconfConnectorDTO implements AutoCloseable {
456
457         private final NetconfDeviceCommunicator communicator;
458         private final RemoteDeviceHandler<NetconfSessionPreferences> facade;
459
460         public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator,
461                                    final RemoteDeviceHandler<NetconfSessionPreferences> facade) {
462             this.communicator = communicator;
463             this.facade = facade;
464         }
465
466         public NetconfDeviceCommunicator getCommunicator() {
467             return communicator;
468         }
469
470         public RemoteDeviceHandler<NetconfSessionPreferences> getFacade() {
471             return facade;
472         }
473
474         public NetconfClientSessionListener getSessionListener() {
475             return communicator;
476         }
477
478         @Override
479         public void close() {
480             communicator.close();
481             facade.close();
482         }
483     }
484 }