Remove AbstractNetconfTopology.createSalFacade()
[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.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 java.util.concurrent.ExecutionException;
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.common.api.LogicalDatastoreType;
33 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
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.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.NetconfDeviceSalFacade;
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.Uri;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.optional.rev190614.NetconfNodeAugmentedOptional;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.parameters.Protocol.Name;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.schema.storage.YangLibrary;
77 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
78 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
79 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
80 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
81 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
82 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
83 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
84 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
85 import org.opendaylight.yangtools.yang.common.Empty;
86 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
87 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
88 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
89 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
90 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
91 import org.slf4j.Logger;
92 import org.slf4j.LoggerFactory;
93
94 public abstract class AbstractNetconfTopology implements NetconfTopology {
95     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
96
97     private final NetconfClientDispatcher clientDispatcher;
98     private final EventExecutor eventExecutor;
99     private final DeviceActionFactory deviceActionFactory;
100     private final NetconfKeystoreAdapter keystoreAdapter;
101     private final SchemaResourceManager schemaManager;
102     private final BaseNetconfSchemas baseSchemas;
103
104     protected final ScheduledThreadPool keepaliveExecutor;
105     protected final ListeningExecutorService processingExecutor;
106     protected final DataBroker dataBroker;
107     protected final DOMMountPointService mountPointService;
108     protected final String topologyId;
109     protected String privateKeyPath;
110     protected String privateKeyPassphrase;
111     protected final AAAEncryptionService encryptionService;
112     protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
113
114     protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
115                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
116                                       final ThreadPool processingExecutor, final SchemaResourceManager schemaManager,
117                                       final DataBroker dataBroker, final DOMMountPointService mountPointService,
118                                       final AAAEncryptionService encryptionService,
119                                       final DeviceActionFactory deviceActionFactory,
120                                       final BaseNetconfSchemas baseSchemas) {
121         this.topologyId = requireNonNull(topologyId);
122         this.clientDispatcher = clientDispatcher;
123         this.eventExecutor = eventExecutor;
124         this.keepaliveExecutor = keepaliveExecutor;
125         this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor.getExecutor());
126         this.schemaManager = requireNonNull(schemaManager);
127         this.deviceActionFactory = deviceActionFactory;
128         this.dataBroker = requireNonNull(dataBroker);
129         this.mountPointService = mountPointService;
130         this.encryptionService = encryptionService;
131         this.baseSchemas = requireNonNull(baseSchemas);
132
133         keystoreAdapter = new NetconfKeystoreAdapter(dataBroker);
134
135         // FIXME: this should be a put(), as we are initializing and will be re-populating the datastore with all the
136         //        devices. Whatever has been there before should be nuked to properly re-align lifecycle.
137         final var wtx = dataBroker.newWriteOnlyTransaction();
138         wtx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(NetworkTopology.class)
139             .child(Topology.class, new TopologyKey(new TopologyId(topologyId)))
140             .build(), new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build());
141         final var future = wtx.commit();
142         try {
143             future.get();
144         } catch (InterruptedException | ExecutionException e) {
145             LOG.error("Unable to initialize topology {}", topologyId, e);
146             throw new IllegalStateException(e);
147         }
148
149         LOG.debug("Topology {} initialized", topologyId);
150     }
151
152     @Override
153     public ListenableFuture<Empty> connectNode(final NodeId nodeId, final Node configNode) {
154         LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, hideCredentials(configNode));
155         return setupConnection(nodeId, configNode);
156     }
157
158     /**
159      * Hiding of private credentials from node configuration (credentials data is replaced by asterisks).
160      *
161      * @param nodeConfiguration Node configuration container.
162      * @return String representation of node configuration with credentials replaced by asterisks.
163      */
164     @VisibleForTesting
165     public static String hideCredentials(final Node nodeConfiguration) {
166         final NetconfNode netconfNodeAugmentation = nodeConfiguration.augmentation(NetconfNode.class);
167         final String nodeCredentials = netconfNodeAugmentation.getCredentials().toString();
168         final String nodeConfigurationString = nodeConfiguration.toString();
169         return nodeConfigurationString.replace(nodeCredentials, "***");
170     }
171
172     @Override
173     public ListenableFuture<Empty> disconnectNode(final NodeId nodeId) {
174         final var nodeName = nodeId.getValue();
175         LOG.debug("Disconnecting RemoteDevice{{}}", nodeName);
176
177         final NetconfConnectorDTO connectorDTO = activeConnectors.remove(nodeId);
178         if (connectorDTO == null) {
179             return Futures.immediateFailedFuture(
180                 new IllegalStateException("Cannot disconnect " + nodeName + " as it is not connected"));
181         }
182
183         connectorDTO.close();
184         return Futures.immediateFuture(Empty.value());
185     }
186
187     protected ListenableFuture<Empty> setupConnection(final NodeId nodeId, final Node configNode) {
188         final NetconfNode netconfNode = configNode.augmentation(NetconfNode.class);
189         final NetconfNodeAugmentedOptional nodeOptional = configNode.augmentation(NetconfNodeAugmentedOptional.class);
190
191         requireNonNull(netconfNode.getHost());
192         requireNonNull(netconfNode.getPort());
193
194         final NetconfConnectorDTO deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, nodeOptional);
195         final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator();
196         final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener();
197         final NetconfReconnectingClientConfiguration clientConfig =
198                 getClientConfig(netconfClientSessionListener, netconfNode, nodeId);
199         final ListenableFuture<Empty> future =
200                 deviceCommunicator.initializeRemoteConnection(clientDispatcher, clientConfig);
201
202         activeConnectors.put(nodeId, deviceCommunicatorDTO);
203
204         Futures.addCallback(future, new FutureCallback<>() {
205             @Override
206             public void onSuccess(final Empty result) {
207                 LOG.debug("Connector for {} started succesfully", nodeId.getValue());
208             }
209
210             @Override
211             public void onFailure(final Throwable throwable) {
212                 LOG.error("Connector for {} failed", nodeId.getValue(), throwable);
213                 // remove this node from active connectors?
214             }
215         }, MoreExecutors.directExecutor());
216
217         return future;
218     }
219
220     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node) {
221         return createDeviceCommunicator(nodeId, node, null);
222     }
223
224     protected NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node,
225             final NetconfNodeAugmentedOptional nodeOptional) {
226         final var deviceId = NetconfNodeUtils.toRemoteDeviceId(nodeId, node);
227         final long keepaliveDelay = node.requireKeepaliveDelay().toJava();
228
229         final var deviceSalFacade = new NetconfDeviceSalFacade(deviceId, mountPointService, dataBroker, topologyId);
230         // The facade we are going it present to NetconfDevice
231         RemoteDeviceHandler salFacade;
232         final KeepaliveSalFacade keepAliveFacade;
233         if (keepaliveDelay > 0) {
234             LOG.info("Adding keepalive facade, for device {}", nodeId);
235             salFacade = keepAliveFacade = new KeepaliveSalFacade(deviceId, deviceSalFacade,
236                 keepaliveExecutor.getExecutor(), keepaliveDelay, node.requireDefaultRequestTimeoutMillis().toJava());
237         } else {
238             salFacade = deviceSalFacade;
239             keepAliveFacade = null;
240         }
241
242         final RemoteDevice<NetconfDeviceCommunicator> device;
243         final List<SchemaSourceRegistration<?>> yanglibRegistrations;
244         if (node.requireSchemaless()) {
245             device = new SchemalessNetconfDevice(baseSchemas, deviceId, salFacade);
246             yanglibRegistrations = List.of();
247         } else {
248             final boolean reconnectOnChangedSchema = node.requireReconnectOnChangedSchema();
249             final SchemaResourcesDTO resources = schemaManager.getSchemaResources(node.getSchemaCacheDirectory(),
250                 nodeId.getValue());
251             device = new NetconfDeviceBuilder()
252                 .setReconnectOnSchemasChange(reconnectOnChangedSchema)
253                 .setSchemaResourcesDTO(resources)
254                 .setGlobalProcessingExecutor(processingExecutor)
255                 .setId(deviceId)
256                 .setSalFacade(salFacade)
257                 .setNode(node)
258                 .setEventExecutor(eventExecutor)
259                 .setNodeOptional(nodeOptional)
260                 .setDeviceActionFactory(deviceActionFactory)
261                 .setBaseSchemas(baseSchemas)
262                 .build();
263             yanglibRegistrations = registerDeviceSchemaSources(deviceId, node, resources);
264         }
265
266         final Optional<UserPreferences> userCapabilities = getUserCapabilities(node);
267         final int rpcMessageLimit = node.requireConcurrentRpcLimit().toJava();
268         if (rpcMessageLimit < 1) {
269             LOG.info("Concurrent rpc limit is smaller than 1, no limit will be enforced for device {}", deviceId);
270         }
271
272         final NetconfDeviceCommunicator netconfDeviceCommunicator =
273              userCapabilities.isPresent() ? new NetconfDeviceCommunicator(deviceId, device,
274                      userCapabilities.get(), rpcMessageLimit)
275             : new NetconfDeviceCommunicator(deviceId, device, rpcMessageLimit);
276
277         if (keepAliveFacade != null) {
278             keepAliveFacade.setListener(netconfDeviceCommunicator);
279         }
280
281         return new NetconfConnectorDTO(netconfDeviceCommunicator, salFacade, yanglibRegistrations);
282     }
283
284     private static List<SchemaSourceRegistration<?>> registerDeviceSchemaSources(final RemoteDeviceId remoteDeviceId,
285             final NetconfNode node, final SchemaResourcesDTO resources) {
286         final YangLibrary yangLibrary = node.getYangLibrary();
287         if (yangLibrary != null) {
288             final Uri uri = yangLibrary.getYangLibraryUrl();
289             if (uri != null) {
290                 final List<SchemaSourceRegistration<?>> registrations = new ArrayList<>();
291                 final String yangLibURL = uri.getValue();
292                 final SchemaSourceRegistry schemaRegistry = resources.getSchemaRegistry();
293
294                 // pre register yang library sources as fallback schemas to schema registry
295                 final LibraryModulesSchemas schemas;
296                 final String yangLibUsername = yangLibrary.getUsername();
297                 final String yangLigPassword = yangLibrary.getPassword();
298                 if (yangLibUsername != null && yangLigPassword != null) {
299                     schemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword);
300                 } else {
301                     schemas = LibraryModulesSchemas.create(yangLibURL);
302                 }
303
304                 for (final Map.Entry<SourceIdentifier, URL> entry : schemas.getAvailableModels().entrySet()) {
305                     registrations.add(schemaRegistry.registerSchemaSource(new YangLibrarySchemaYangSourceProvider(
306                         remoteDeviceId, schemas.getAvailableModels()),
307                         PotentialSchemaSource.create(entry.getKey(), YangTextSchemaSource.class,
308                             PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
309                 }
310                 return List.copyOf(registrations);
311             }
312         }
313
314         return List.of();
315     }
316
317     /**
318      * Sets the private key path from location specified in configuration file using blueprint.
319      */
320     public void setPrivateKeyPath(final String privateKeyPath) {
321         this.privateKeyPath = privateKeyPath;
322     }
323
324     /**
325      * Sets the private key passphrase from location specified in configuration file using blueprint.
326      */
327     public void setPrivateKeyPassphrase(final String privateKeyPassphrase) {
328         this.privateKeyPassphrase = privateKeyPassphrase;
329     }
330
331     public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
332                                                                   final NetconfNode node, final NodeId nodeId) {
333         final ReconnectStrategyFactory sf = new TimedReconnectStrategyFactory(eventExecutor,
334                 node.requireMaxConnectionAttempts().toJava(), node.requireBetweenAttemptsTimeoutMillis().toJava(),
335                 node.requireSleepFactor().decimalValue());
336         final NetconfReconnectingClientConfigurationBuilder reconnectingClientConfigurationBuilder;
337         final Protocol protocol = node.getProtocol();
338         if (node.requireTcpOnly()) {
339             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
340                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TCP)
341                     .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
342         } else if (protocol == null || protocol.getName() == Name.SSH) {
343             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
344                     .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
345                     .withAuthHandler(getHandlerFromCredentials(node.getCredentials()));
346         } else if (protocol.getName() == Name.TLS) {
347             reconnectingClientConfigurationBuilder = NetconfReconnectingClientConfigurationBuilder.create()
348                 .withSslHandlerFactory(new SslHandlerFactoryImpl(keystoreAdapter, protocol.getSpecification()))
349                 .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.TLS);
350         } else {
351             throw new IllegalStateException("Unsupported protocol type: " + protocol.getName());
352         }
353
354         if (node.getOdlHelloMessageCapabilities() != null) {
355             reconnectingClientConfigurationBuilder.withOdlHelloCapabilities(
356                     Lists.newArrayList(node.getOdlHelloMessageCapabilities().getCapability()));
357         }
358
359         return reconnectingClientConfigurationBuilder
360                 .withName(nodeId.getValue())
361                 .withAddress(NetconfNodeUtils.toInetSocketAddress(node))
362                 .withConnectionTimeoutMillis(node.requireConnectionTimeoutMillis().toJava())
363                 .withReconnectStrategy(sf.createReconnectStrategy())
364                 .withConnectStrategyFactory(sf)
365                 .withSessionListener(listener)
366                 .build();
367     }
368
369     private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) {
370         if (credentials
371                 instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology
372                     .rev150114.netconf.node.credentials.credentials.LoginPassword loginPassword) {
373             return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
374         }
375         if (credentials instanceof LoginPwUnencrypted) {
376             final LoginPasswordUnencrypted loginPassword =
377                     ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted();
378             return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword());
379         }
380         if (credentials instanceof LoginPw) {
381             final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword();
382             return new LoginPasswordHandler(loginPassword.getUsername(),
383                     encryptionService.decrypt(loginPassword.getPassword()));
384         }
385         if (credentials instanceof KeyAuth) {
386             final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased();
387             return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(),
388                     keystoreAdapter, encryptionService);
389         }
390         throw new IllegalStateException("Unsupported credential type: " + credentials.getClass());
391     }
392
393     private static Optional<UserPreferences> getUserCapabilities(final NetconfNode node) {
394         // if none of yang-module-capabilities or non-module-capabilities is specified
395         // just return absent
396         if (node.getYangModuleCapabilities() == null && node.getNonModuleCapabilities() == null) {
397             return Optional.empty();
398         }
399
400         final List<String> capabilities = new ArrayList<>();
401
402         boolean overrideYangModuleCaps = false;
403         if (node.getYangModuleCapabilities() != null) {
404             capabilities.addAll(node.getYangModuleCapabilities().getCapability());
405             overrideYangModuleCaps = node.getYangModuleCapabilities().getOverride();
406         }
407
408         //non-module capabilities should not exist in yang module capabilities
409         final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(capabilities);
410         Preconditions.checkState(netconfSessionPreferences.getNonModuleCaps().isEmpty(),
411                 "List yang-module-capabilities/capability should contain only module based capabilities. "
412                         + "Non-module capabilities used: " + netconfSessionPreferences.getNonModuleCaps());
413
414         boolean overrideNonModuleCaps = false;
415         if (node.getNonModuleCapabilities() != null) {
416             capabilities.addAll(node.getNonModuleCapabilities().getCapability());
417             overrideNonModuleCaps = node.getNonModuleCapabilities().getOverride();
418         }
419
420         return Optional.of(new UserPreferences(NetconfSessionPreferences
421             .fromStrings(capabilities, CapabilityOrigin.UserDefined), overrideYangModuleCaps, overrideNonModuleCaps));
422     }
423 }