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