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