Remove netconf-topology-singleton's NetconfConnectorDTO
[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.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.ListeningExecutorService;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import io.netty.util.concurrent.EventExecutor;
20 import java.math.BigDecimal;
21 import java.net.InetSocketAddress;
22 import java.net.URL;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
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.mdsal.binding.api.DataBroker;
32 import org.opendaylight.mdsal.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.ReconnectStrategyFactory;
40 import org.opendaylight.netconf.nettyutil.TimedReconnectStrategyFactory;
41 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
42 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
43 import org.opendaylight.netconf.sal.connect.api.DeviceActionFactory;
44 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
45 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
46 import org.opendaylight.netconf.sal.connect.api.SchemaResourceManager;
47 import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
48 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO;
49 import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder;
50 import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice;
51 import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth;
52 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
53 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
54 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
55 import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
56 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
57 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter;
58 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
59 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseNetconfSchemas;
60 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
61 import org.opendaylight.netconf.sal.connect.util.SslHandlerFactoryImpl;
62 import org.opendaylight.netconf.topology.api.NetconfTopology;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
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     private final NetconfClientDispatcher clientDispatcher;
104     private final EventExecutor eventExecutor;
105     private final DeviceActionFactory deviceActionFactory;
106     private final NetconfKeystoreAdapter keystoreAdapter;
107     private final SchemaResourceManager schemaManager;
108     private final BaseNetconfSchemas baseSchemas;
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
121     protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
122                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
123                                       final ThreadPool processingExecutor, final SchemaResourceManager schemaManager,
124                                       final DataBroker dataBroker, final DOMMountPointService mountPointService,
125                                       final AAAEncryptionService encryptionService,
126                                       final DeviceActionFactory deviceActionFactory,
127                                       final BaseNetconfSchemas baseSchemas) {
128         this.topologyId = topologyId;
129         this.clientDispatcher = clientDispatcher;
130         this.eventExecutor = eventExecutor;
131         this.keepaliveExecutor = keepaliveExecutor;
132         this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
133         this.schemaManager = requireNonNull(schemaManager);
134         this.deviceActionFactory = deviceActionFactory;
135         this.dataBroker = dataBroker;
136         this.mountPointService = mountPointService;
137         this.encryptionService = encryptionService;
138         this.baseSchemas = requireNonNull(baseSchemas);
139
140         this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
141     }
142
143     @Override
144     public ListenableFuture<NetconfDeviceCapabilities> connectNode(final NodeId nodeId, final Node configNode) {
145         LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode));
146         return setupConnection(nodeId, configNode);
147     }
148
149     /**
150      * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
151      *
152      * @param nodeConfiguration Node configuration container.
153      * @return String representation of node configuration with credentials replaced by asterisks.
154      */
155     @VisibleForTesting
156     public static String hideCredentials(final Node nodeConfiguration) {
157         final NetconfNode netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
158         final String nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
159         final String nodeConfigurationString = nodeConfiguration.toString();
160         return nodeConfigurationString.replace(nodeCredentials, "***");
161     }
162
163     @Override
164     public ListenableFuture<Void> disconnectNode(final NodeId nodeId) {
165         LOG.debug("Disconnecting RemoteDevice{{}}", nodeId.getValue());
166
167         final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
168         if (connectorDTO == null) {
169             return Futures.immediateFailedFuture(
170                 new IllegalStateException("Unable to disconnect device that is not connected"));
171         }
172
173         connectorDTO.close();
174         return Futures.immediateFuture(null);
175     }
176
177     protected ListenableFuture<NetconfDeviceCapabilities> setupConnection(final NodeId nodeId,
178                                                                           final Node configNode) {
179         final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
180         final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
181
182         requireNonNull(netconfNode.getHost());
183         requireNonNull(netconfNode.getPort());
184
185         final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional);
186         final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
187         final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
188         final NetconfReconnectingClientConfiguration clientConfig =
189                 getClientConfig(netconfClientSessionListener, netconfNode);
190         final ListenableFuture<NetconfDeviceCapabilities> future =
191                 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
192
193         activeConnectors.put(nodeId, deviceCommunicatorDTO);
194
195         Futures.addCallback(future, new FutureCallback<NetconfDeviceCapabilities>() {
196             @Override
197             public void onSuccess(final NetconfDeviceCapabilities result) {
198                 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
199             }
200
201             @Override
202             public void onFailure(final Throwable throwable) {
203                 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
204                 // remove this node from active connectors?
205             }
206         }, MoreExecutors.directExecutor());
207
208         return future;
209     }
210
211     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
212         return createDeviceCommunicator(nodeId, node, null);
213     }
214
215     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
216             final NetconfNodeAugmentedOptional nodeOptional) {
217         //setup default values since default value is not supported in mdsal
218         final long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null
219                 ? DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis().toJava();
220         final long keepaliveDelay = node.getKeepaliveDelay() == null
221                 ? DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay().toJava();
222
223         final InetSocketAddress address;
224         final Host host = node.getHost();
225         final IpAddress ipAddress = host.getIpAddress();
226         if (ipAddress != null) {
227             address = new InetSocketAddress(IetfInetUtil.INSTANCE.inetAddressFor(ipAddress),
228                     node.getPort().getValue().toJava());
229         } else {
230             address = new InetSocketAddress(host.getDomainName().getValue(),
231                     node.getPort().getValue().toJava());
232         }
233         final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(nodeId.getValue(), address);
234
235         RemoteDeviceHandler<NetconfSessionPreferences> salFacade = createSalFacade(remoteDeviceId);
236
237         if (keepaliveDelay > 0) {
238             LOG.warn("Adding keepalive facade, for device {}", nodeId);
239             salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, this.keepaliveExecutor.getExecutor(),
240                     keepaliveDelay, defaultRequestTimeoutMillis);
241         }
242
243         final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> device;
244         final List<SchemaSourceRegistration<?>> yanglibRegistrations;
245         if (node.getSchemaless()) {
246             device = new SchemalessNetconfDevice(baseSchemas, remoteDeviceId, salFacade);
247             yanglibRegistrations = List.of();
248         } else {
249             final boolean reconnectOnChangedSchema = node.getReconnectOnChangedSchema() == null
250                 ? DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.getReconnectOnChangedSchema();
251
252             final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node, nodeId.getValue());
253             device = new NetconfDeviceBuilder()
254                 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
255                 .setSchemaResourcesDTO(resources)
256                 .setGlobalProcessingExecutor(this.processingExecutor)
257                 .setId(remoteDeviceId)
258                 .setSalFacade(salFacade)
259                 .setNode(node)
260                 .setEventExecutor(eventExecutor)
261                 .setNodeOptional(nodeOptional)
262                 .setDeviceActionFactory(deviceActionFactory)
263                 .setBaseSchemas(baseSchemas)
264                 .build();
265             yanglibRegistrations = registerDeviceSchemaSources(remoteDeviceId, nodeId, node, resources);
266         }
267
268         final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
269         final int rpcMessageLimit = node.getConcurrentRpcLimit() == null ? DEFAULT_CONCURRENT_RPC_LIMIT
270             : node.getConcurrentRpcLimit().toJava();
271
272         if (rpcMessageLimit < 1) {
273             LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", remoteDeviceId);
274         }
275
276         NetconfDeviceCommunicator netconfDeviceCommunicator =
277              userCapabilities.isPresent() ? new NetconfDeviceCommunicator(remoteDeviceId, device,
278                      userCapabilities.get(), rpcMessageLimit)
279             : new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit);
280
281         if (salFacade instanceof KeepaliveSalFacade) {
282             ((KeepaliveSalFacade)salFacade).setListener(netconfDeviceCommunicator);
283         }
284
285         return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations);
286     }
287
288     private List<SchemaSourceRegistration<?>> registerDeviceSchemaSources(final RemoteDeviceId remoteDeviceId,
289             final NodeId nodeId, final NetconfNode node, final SchemaResourcesDTO resources) {
290         final YangLibrary yangLibrary = node.getYangLibrary();
291         if (yangLibrary != null) {
292             final Uri uri = yangLibrary.getYangLibraryUrl();
293             if (uri != null) {
294                 final List<SchemaSourceRegistration<?>> registrations = new ArrayList<>();
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                     registrations.add(schemaRegistry.registerSchemaSource(new YangLibrarySchemaYangSourceProvider(
310                         remoteDeviceId, schemas.getAvailableModels()),
311                         PotentialSchemaSource.create(entry.getKey(), YangTextSchemaSource.class,
312                             PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
313                 }
314                 return List.copyOf(registrations);
315             }
316         }
317
318         return List.of();
319     }
320
321     /**
322      * Sets the private key path from location specified in configuration file using blueprint.
323      */
324     public void setPrivateKeyPath(final String privateKeyPath) {
325         this.privateKeyPath = privateKeyPath;
326     }
327
328     /**
329      * Sets the private key passphrase from location specified in configuration file using blueprint.
330      */
331     public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
332         this.privateKeyPassphrase = privateKeyPassphrase;
333     }
334
335     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
336                                                                   final NetconfNode node) {
337
338         //setup default values since default value is not supported in mdsal
339         final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null
340                 ? DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis().toJava();
341         final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null
342                 ? DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts().toJava();
343         final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null
344                 ? DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis().toJava();
345         final boolean useTcp = node.getTcpOnly() == null ? DEFAULT_IS_TCP_ONLY : node.getTcpOnly();
346         final BigDecimal sleepFactor = node.getSleepFactor() == null ? DEFAULT_SLEEP_FACTOR : node.getSleepFactor();
347
348         final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue().toJava());
349
350         final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
351                 maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor);
352
353         final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
354         final Protocol protocol = node.getProtocol();
355         if (useTcp) {
356             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
357                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
358                     .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
359         } else if (protocol == null || protocol.getName() == Name.SSH) {
360             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
361                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
362                     .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
363         } else if (protocol.getName() == Name.TLS) {
364             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
365                 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
366                 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
367         } else {
368             throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
369         }
370
371         if (node.getOdlHelloMessageCapabilities() != null) {
372             reconnectingClientConfigurationBuilder
373                     .withOdlHelloCapabilities(node.getOdlHelloMessageCapabilities().getCapability());
374         }
375
376         return reconnectingClientConfigurationBuilder
377                 .withAddress(socketAddress)
378                 .withConnectionTimeoutMillis(clientConnectionTimeoutMillis)
379                 .withReconnectStrategy(sf.createReconnectStrategy())
380                 .withConnectStrategyFactory(sf)
381                 .withSessionListener(listener)
382                 .build();
383     }
384
385     private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
386         if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
387                 .rev150114.netconf.node.credentials.credentials.LoginPassword) {
388             final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
389                     .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword
390                     = (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
391                     .rev150114.netconf.node.credentials.credentials.LoginPassword) credentials;
392             return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
393         }
394         if (credentials instanceof LoginPwUnencrypted) {
395             final LoginPasswordUnencrypted loginPassword =
396                     ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
397             return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
398         }
399         if (credentials instanceof LoginPw) {
400             final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
401             return new LoginPasswordHandler(loginPassword.getUsername(),
402                     encryptionService.decrypt(loginPassword.getPassword()));
403         }
404         if (credentials instanceof KeyAuth) {
405             final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
406             return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
407                     keystoreAdapter, encryptionService);
408         }
409         throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
410     }
411
412     protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(RemoteDeviceId id);
413
414     private static InetSocketAddress getSocketAddress(final Host host, final int port) {
415         if (host.getDomainName() != null) {
416             return new InetSocketAddress(host.getDomainName().getValue(), port);
417         }
418
419         final IpAddress ipAddress = host.getIpAddress();
420         final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue()
421                 : ipAddress.getIpv6Address().getValue();
422         return new InetSocketAddress(ip, port);
423     }
424
425     private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
426         // if none of yang-module-capabilities or non-module-capabilities is specified
427         // just return absent
428         if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
429             return Optional.empty();
430         }
431
432         final List<String> capabilities = new ArrayList<>();
433
434         boolean overrideYangModuleCaps = false;
435         if (node.getYangModuleCapabilities() != null) {
436             capabilities.addAll(node.getYangModuleCapabilities().getCapability());
437             overrideYangModuleCaps = node.getYangModuleCapabilities().getOverride();
438         }
439
440         //non-module capabilities should not exist in yang module capabilities
441         final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
442         Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
443                 "List yang-module-capabilities/capability should contain only module based capabilities. "
444                         + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
445
446         boolean overrideNonModuleCaps = false;
447         if (node.getNonModuleCapabilities() != null) {
448             capabilities.addAll(node.getNonModuleCapabilities().getCapability());
449             overrideNonModuleCaps = node.getNonModuleCapabilities().getOverride();
450         }
451
452         return Optional.of(new UserPreferences(NetconfSessionPreferences
453             .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
454     }
455 }