2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.sal.connect.netconf.util;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.COMMIT_RPC_CONTENT;
13 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.EDIT_CONTENT_NODEID;
14 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
15 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_PATH;
16 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_NODEID;
17 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_PATH;
18 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_NODEID;
19 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_PATH;
20 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_NODEID;
21 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_PATH;
22 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_NODEID;
23 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_NODEID;
24 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_PATH;
25 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_NODEID;
26 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_PATH;
27 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_NODEID;
28 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_PATH;
29 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME;
30 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_SOURCE_NODEID;
31 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_NODEID;
32 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_NODEID;
33 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_PATH;
34 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_NODEID;
35 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_PATH;
36 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION;
37 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure;
38 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
40 import com.google.common.util.concurrent.FutureCallback;
41 import com.google.common.util.concurrent.Futures;
42 import com.google.common.util.concurrent.ListenableFuture;
43 import com.google.common.util.concurrent.MoreExecutors;
44 import java.util.Locale;
45 import java.util.Optional;
46 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
47 import org.opendaylight.mdsal.dom.api.DOMRpcService;
48 import org.opendaylight.netconf.api.ModifyAction;
49 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade.KeepaliveDOMRpcService;
50 import org.opendaylight.netconf.sal.connect.netconf.sal.SchemalessNetconfDeviceRpc;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.copy.config.input.target.ConfigTarget;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.input.source.ConfigSource;
53 import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext;
54 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
55 import org.opendaylight.yangtools.yang.common.Empty;
56 import org.opendaylight.yangtools.yang.common.QName;
57 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
58 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
59 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
60 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
61 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
62 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
63 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
64 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
65 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
66 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
69 * Provides base operations for netconf e.g. get, get-config, edit-config, (un)lock, commit etc.
70 * According to RFC-6241
72 public final class NetconfBaseOps {
73 private static final NodeIdentifier CONFIG_SOURCE_NODEID = NodeIdentifier.create(ConfigSource.QNAME);
74 private static final NodeIdentifier CONFIG_TARGET_NODEID = NodeIdentifier.create(ConfigTarget.QNAME);
76 private final DOMRpcService rpc;
77 private final MountPointContext mountContext;
78 private final RpcStructureTransformer transformer;
81 public NetconfBaseOps(final DOMRpcService rpc, final EffectiveModelContext schemaContext) {
82 this(rpc, new EmptyMountPointContext(schemaContext));
85 public NetconfBaseOps(final DOMRpcService rpc, final MountPointContext mountContext) {
87 this.mountContext = mountContext;
89 if (rpc instanceof KeepaliveDOMRpcService
90 && ((KeepaliveDOMRpcService) rpc).getDeviceRpc() instanceof SchemalessNetconfDeviceRpc) {
91 this.transformer = new SchemalessRpcStructureTransformer();
93 this.transformer = new NetconfRpcStructureTransformer(mountContext);
97 public ListenableFuture<? extends DOMRpcResult> lock(final FutureCallback<DOMRpcResult> callback,
98 final QName datastore) {
99 requireNonNull(callback);
100 requireNonNull(datastore);
102 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_LOCK_PATH,
103 getLockContent(datastore));
104 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
108 public ListenableFuture<? extends DOMRpcResult> lockCandidate(final FutureCallback<DOMRpcResult> callback) {
109 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_LOCK_PATH,
110 getLockContent(NETCONF_CANDIDATE_QNAME));
111 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
115 public ListenableFuture<? extends DOMRpcResult> lockRunning(final FutureCallback<DOMRpcResult> callback) {
116 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_LOCK_PATH,
117 getLockContent(NETCONF_RUNNING_QNAME));
118 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
122 public ListenableFuture<? extends DOMRpcResult> unlock(final FutureCallback<DOMRpcResult> callback,
123 final QName datastore) {
124 requireNonNull(callback);
125 requireNonNull(datastore);
127 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_UNLOCK_PATH,
128 getUnLockContent(datastore));
129 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
133 public ListenableFuture<? extends DOMRpcResult> unlockRunning(final FutureCallback<DOMRpcResult> callback) {
134 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_UNLOCK_PATH,
135 getUnLockContent(NETCONF_RUNNING_QNAME));
136 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
140 public ListenableFuture<? extends DOMRpcResult> unlockCandidate(final FutureCallback<DOMRpcResult> callback) {
141 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_UNLOCK_PATH,
142 getUnLockContent(NETCONF_CANDIDATE_QNAME));
143 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
147 public ListenableFuture<? extends DOMRpcResult> discardChanges(final FutureCallback<DOMRpcResult> callback) {
148 requireNonNull(callback);
150 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_PATH, null);
151 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
155 public ListenableFuture<? extends DOMRpcResult> commit(final FutureCallback<DOMRpcResult> callback) {
156 requireNonNull(callback);
158 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_COMMIT_PATH, COMMIT_RPC_CONTENT);
159 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
163 public ListenableFuture<? extends DOMRpcResult> validate(final FutureCallback<DOMRpcResult> callback,
164 final QName datastore) {
165 requireNonNull(callback);
167 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_VALIDATE_PATH,
168 getValidateContent(requireNonNull(datastore)));
169 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
173 public ListenableFuture<? extends DOMRpcResult> validateCandidate(final FutureCallback<DOMRpcResult> callback) {
174 return validate(callback, NETCONF_CANDIDATE_QNAME);
177 public ListenableFuture<? extends DOMRpcResult> validateRunning(final FutureCallback<DOMRpcResult> callback) {
178 return validate(callback, NETCONF_RUNNING_QNAME);
181 public ListenableFuture<? extends DOMRpcResult> copyConfig(final FutureCallback<DOMRpcResult> callback,
182 final QName source, final QName target) {
183 requireNonNull(callback);
185 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_COPY_CONFIG_PATH,
186 getCopyConfigContent(requireNonNull(source), requireNonNull(target)));
187 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
191 public ListenableFuture<? extends DOMRpcResult> copyRunningToCandidate(
192 final FutureCallback<DOMRpcResult> callback) {
193 return copyConfig(callback, NETCONF_RUNNING_QNAME, NETCONF_CANDIDATE_QNAME);
196 public ListenableFuture<? extends DOMRpcResult> getConfig(final FutureCallback<DOMRpcResult> callback,
197 final QName datastore,
198 final Optional<YangInstanceIdentifier> filterPath) {
199 requireNonNull(callback);
200 requireNonNull(datastore);
202 final ListenableFuture<? extends DOMRpcResult> future;
203 if (isFilterPresent(filterPath)) {
204 final DataContainerChild<?, ?> node = transformer.toFilterStructure(filterPath.get());
205 future = rpc.invokeRpc(NETCONF_GET_CONFIG_PATH,
206 NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore), node));
208 future = rpc.invokeRpc(NETCONF_GET_CONFIG_PATH,
209 NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore)));
212 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
216 public ListenableFuture<Optional<NormalizedNode<?, ?>>> getConfigRunningData(
217 final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath) {
218 return extractData(filterPath, getConfigRunning(callback, filterPath));
221 public ListenableFuture<Optional<NormalizedNode<?, ?>>> getData(final FutureCallback<DOMRpcResult> callback,
222 final Optional<YangInstanceIdentifier> filterPath) {
223 return extractData(filterPath, get(callback, filterPath));
226 private ListenableFuture<Optional<NormalizedNode<?, ?>>> extractData(
227 final Optional<YangInstanceIdentifier> path, final ListenableFuture<? extends DOMRpcResult> configRunning) {
228 return Futures.transform(configRunning, result -> {
229 checkArgument(result.getErrors().isEmpty(), "Unable to read data: %s, errors: %s", path,
231 final DataContainerChild<?, ?> dataNode = ((ContainerNode) result.getResult())
232 .getChild(NetconfMessageTransformUtil.NETCONF_DATA_NODEID).get();
233 return transformer.selectFromDataStructure(dataNode, path.get());
234 }, MoreExecutors.directExecutor());
237 public ListenableFuture<? extends DOMRpcResult> getConfigRunning(final FutureCallback<DOMRpcResult> callback,
238 final Optional<YangInstanceIdentifier> filterPath) {
239 return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath);
242 public ListenableFuture<? extends DOMRpcResult> getConfigCandidate(final FutureCallback<DOMRpcResult> callback,
243 final Optional<YangInstanceIdentifier> filterPath) {
244 return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath);
247 public ListenableFuture<? extends DOMRpcResult> get(final FutureCallback<DOMRpcResult> callback,
248 final Optional<YangInstanceIdentifier> filterPath) {
249 requireNonNull(callback);
251 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_GET_PATH,
252 isFilterPresent(filterPath)
253 ? NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID,
254 toFilterStructure(filterPath.get(), mountContext.getSchemaContext()))
255 : NetconfMessageTransformUtil.GET_RPC_CONTENT);
256 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
260 private static boolean isFilterPresent(final Optional<YangInstanceIdentifier> filterPath) {
261 return filterPath.isPresent() && !filterPath.get().isEmpty();
264 public ListenableFuture<? extends DOMRpcResult> editConfigCandidate(
265 final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure,
266 final ModifyAction modifyAction, final boolean rollback) {
267 return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.of(modifyAction), rollback);
270 public ListenableFuture<? extends DOMRpcResult> editConfigCandidate(
271 final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure,
272 final boolean rollback) {
273 return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.empty(), rollback);
276 public ListenableFuture<? extends DOMRpcResult> editConfigRunning(
277 final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure,
278 final ModifyAction modifyAction, final boolean rollback) {
279 return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.of(modifyAction), rollback);
282 public ListenableFuture<? extends DOMRpcResult> editConfigRunning(
283 final FutureCallback<? super DOMRpcResult> callback, final DataContainerChild<?, ?> editStructure,
284 final boolean rollback) {
285 return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.empty(), rollback);
288 public ListenableFuture<? extends DOMRpcResult> editConfig(
289 final FutureCallback<? super DOMRpcResult> callback, final QName datastore,
290 final DataContainerChild<?, ?> editStructure, final Optional<ModifyAction> modifyAction,
291 final boolean rollback) {
292 requireNonNull(callback);
294 final ListenableFuture<? extends DOMRpcResult> future = rpc.invokeRpc(NETCONF_EDIT_CONFIG_PATH,
295 getEditConfigContent(requireNonNull(datastore), requireNonNull(editStructure), modifyAction, rollback));
297 Futures.addCallback(future, callback, MoreExecutors.directExecutor());
301 public ChoiceNode createEditConfigStrcture(final Optional<NormalizedNode<?, ?>> lastChild,
302 final Optional<ModifyAction> operation,
303 final YangInstanceIdentifier dataPath) {
304 return Builders.choiceBuilder()
305 .withNodeIdentifier(EDIT_CONTENT_NODEID)
306 .withChild(transformer.createEditConfigStructure(lastChild, dataPath, operation))
310 private static ContainerNode getEditConfigContent(
311 final QName datastore, final DataContainerChild<?, ?> editStructure,
312 final Optional<ModifyAction> defaultOperation, final boolean rollback) {
313 final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> editBuilder =
314 Builders.containerBuilder().withNodeIdentifier(NETCONF_EDIT_CONFIG_NODEID);
317 editBuilder.withChild(getTargetNode(datastore));
320 if (defaultOperation.isPresent()) {
321 final String opString = defaultOperation.get().name().toLowerCase(Locale.ROOT);
322 editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(NETCONF_DEFAULT_OPERATION_NODEID)
323 .withValue(opString).build());
328 editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(NETCONF_ERROR_OPTION_NODEID)
329 .withValue(ROLLBACK_ON_ERROR_OPTION).build());
333 editBuilder.withChild(editStructure);
334 return editBuilder.build();
337 public static ContainerNode getSourceNode(final QName datastore) {
338 return Builders.containerBuilder()
339 .withNodeIdentifier(NETCONF_SOURCE_NODEID)
340 .withChild(Builders.choiceBuilder()
341 .withNodeIdentifier(CONFIG_SOURCE_NODEID)
342 .withChild(ImmutableNodes.leafNode(datastore, Empty.getInstance()))
347 public static ContainerNode getLockContent(final QName datastore) {
348 return Builders.containerBuilder().withNodeIdentifier(NETCONF_LOCK_NODEID)
349 .withChild(getTargetNode(datastore)).build();
352 public static ContainerNode getTargetNode(final QName datastore) {
353 return Builders.containerBuilder().withNodeIdentifier(NETCONF_TARGET_NODEID)
354 .withChild(Builders.choiceBuilder().withNodeIdentifier(CONFIG_TARGET_NODEID).withChild(
355 Builders.leafBuilder().withNodeIdentifier(toId(datastore)).withValue(Empty.getInstance()).build())
359 public static ContainerNode getCopyConfigContent(final QName source, final QName target) {
360 return Builders.containerBuilder().withNodeIdentifier(NETCONF_COPY_CONFIG_NODEID)
361 .withChild(getTargetNode(target)).withChild(getSourceNode(source)).build();
364 public static ContainerNode getValidateContent(final QName source) {
365 return Builders.containerBuilder().withNodeIdentifier(NETCONF_VALIDATE_NODEID)
366 .withChild(getSourceNode(source)).build();
369 public static ContainerNode getUnLockContent(final QName datastore) {
370 return Builders.containerBuilder().withNodeIdentifier(NETCONF_UNLOCK_NODEID)
371 .withChild(getTargetNode(datastore)).build();