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