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