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.restconf.impl;
10 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Preconditions;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.io.Closeable;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.Objects;
27 import java.util.concurrent.CountDownLatch;
28 import javax.annotation.Nullable;
29 import javax.ws.rs.core.Response.Status;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
37 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
38 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
39 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
40 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
41 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
42 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
43 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
44 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
45 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
46 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
47 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
48 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
49 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
50 import org.opendaylight.restconf.common.errors.RestconfError;
51 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
52 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
53 import org.opendaylight.restconf.common.patch.PatchContext;
54 import org.opendaylight.restconf.common.patch.PatchEditOperation;
55 import org.opendaylight.restconf.common.patch.PatchEntity;
56 import org.opendaylight.restconf.common.patch.PatchStatusContext;
57 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
58 import org.opendaylight.restconf.common.util.DataChangeScope;
59 import org.opendaylight.yangtools.concepts.ListenerRegistration;
60 import org.opendaylight.yangtools.yang.common.QName;
61 import org.opendaylight.yangtools.yang.common.RpcError;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
66 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
68 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
69 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
76 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
77 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
78 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
79 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
80 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
81 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
82 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
83 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
85 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
86 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
87 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
88 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
89 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
90 import org.slf4j.Logger;
91 import org.slf4j.LoggerFactory;
93 @SuppressWarnings("checkstyle:FinalClass")
94 public class BrokerFacade implements Closeable {
95 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
97 private volatile DOMRpcService rpcService;
99 private final DOMDataBroker domDataBroker;
100 private final DOMNotificationService domNotification;
101 private final ControllerContext controllerContext;
103 private BrokerFacade(DOMRpcService rpcService, DOMDataBroker domDataBroker, DOMNotificationService domNotification,
104 ControllerContext controllerContext) {
105 this.rpcService = Objects.requireNonNull(rpcService);
106 this.domDataBroker = Objects.requireNonNull(domDataBroker);
107 this.domNotification = Objects.requireNonNull(domNotification);
108 this.controllerContext = Objects.requireNonNull(controllerContext);
111 public static BrokerFacade newInstance(DOMRpcService rpcService, DOMDataBroker domDataBroker,
112 DOMNotificationService domNotification, ControllerContext controllerContext) {
113 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
117 public void close() {
121 * Read config data by path.
127 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
128 return readConfigurationData(path, null);
132 * Read config data by path.
137 * value of with-defaults parameter
140 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
141 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
142 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
147 * Read config data from mount point by path.
150 * mount point for reading data
155 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
156 final YangInstanceIdentifier path) {
157 return readConfigurationData(mountPoint, path, null);
161 * Read config data from mount point by path.
164 * mount point for reading data
168 * value of with-defaults parameter
171 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
172 final String withDefa) {
173 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
174 if (domDataBrokerService.isPresent()) {
175 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
176 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
179 final String errMsg = "DOM data broker service isn't available for mount point " + path;
181 throw new RestconfDocumentedException(errMsg);
185 * Read operational data by path.
191 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
192 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
193 return readDataViaTransaction(tx, OPERATIONAL, path);
198 * Read operational data from mount point by path.
201 * mount point for reading data
206 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
207 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
208 if (domDataBrokerService.isPresent()) {
209 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
210 return readDataViaTransaction(tx, OPERATIONAL, path);
213 final String errMsg = "DOM data broker service isn't available for mount point " + path;
215 throw new RestconfDocumentedException(errMsg);
219 * <b>PUT configuration data</b>
222 * Prepare result(status) for PUT operation and PUT data via transaction.
223 * Return wrapped status and future from PUT.
225 * @param globalSchema
226 * used by merge parents (if contains list)
235 * @return wrapper of status and future of PUT
237 public PutResult commitConfigurationDataPut(
238 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
239 final String insert, final String point) {
240 Preconditions.checkNotNull(globalSchema);
241 Preconditions.checkNotNull(path);
242 Preconditions.checkNotNull(payload);
244 final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
245 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
247 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
248 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
249 return new PutResult(status, future);
253 * <b>PUT configuration data (Mount point)</b>
256 * Prepare result(status) for PUT operation and PUT data via transaction.
257 * Return wrapped status and future from PUT.
260 * mount point for getting transaction for operation and schema
261 * context for merging parents(if contains list)
270 * @return wrapper of status and future of PUT
272 public PutResult commitMountPointDataPut(
273 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
274 final String insert, final String point) {
275 Preconditions.checkNotNull(mountPoint);
276 Preconditions.checkNotNull(path);
277 Preconditions.checkNotNull(payload);
279 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
280 if (domDataBrokerService.isPresent()) {
281 final DOMDataReadWriteTransaction newReadWriteTransaction =
282 domDataBrokerService.get().newReadWriteTransaction();
283 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
284 ? Status.OK : Status.CREATED;
285 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
286 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
288 return new PutResult(status, future);
290 final String errMsg = "DOM data broker service isn't available for mount point " + path;
292 throw new RestconfDocumentedException(errMsg);
295 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
297 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
299 // get new transaction and schema context on server or on mounted device
300 final SchemaContext schemaContext;
301 final DOMDataReadWriteTransaction patchTransaction;
302 if (mountPoint == null) {
303 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
304 patchTransaction = this.domDataBroker.newReadWriteTransaction();
306 schemaContext = mountPoint.getSchemaContext();
308 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
310 if (optional.isPresent()) {
311 patchTransaction = optional.get().newReadWriteTransaction();
313 // if mount point does not have broker it is not possible to continue and global error is reported
314 LOG.error("Http Patch {} has failed - device {} does not support broker service",
315 patchContext.getPatchId(), mountPoint.getIdentifier());
316 return new PatchStatusContext(
317 patchContext.getPatchId(),
320 ImmutableList.of(new RestconfError(
321 ErrorType.APPLICATION,
322 ErrorTag.OPERATION_FAILED,
323 "DOM data broker service isn't available for mount point "
324 + mountPoint.getIdentifier()))
329 final List<PatchStatusEntity> editCollection = new ArrayList<>();
330 List<RestconfError> editErrors;
331 boolean withoutError = true;
333 for (final PatchEntity patchEntity : patchContext.getData()) {
334 final PatchEditOperation operation = patchEntity.getOperation();
339 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
340 patchEntity.getNode(), schemaContext);
341 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
342 } catch (final RestconfDocumentedException e) {
343 LOG.error("Error call http Patch operation {} on target {}",
345 patchEntity.getTargetNode().toString());
347 editErrors = new ArrayList<>();
348 editErrors.addAll(e.getErrors());
349 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
350 withoutError = false;
357 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
358 .getTargetNode(), patchEntity.getNode(), schemaContext);
359 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
360 } catch (final RestconfDocumentedException e) {
361 LOG.error("Error call http Patch operation {} on target {}",
363 patchEntity.getTargetNode().toString());
365 editErrors = new ArrayList<>();
366 editErrors.addAll(e.getErrors());
367 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
368 withoutError = false;
376 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
378 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
379 } catch (final RestconfDocumentedException e) {
380 LOG.error("Error call http Patch operation {} on target {}",
382 patchEntity.getTargetNode().toString());
384 editErrors = new ArrayList<>();
385 editErrors.addAll(e.getErrors());
386 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
387 withoutError = false;
394 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
395 patchEntity.getNode(), schemaContext);
396 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
397 } catch (final RestconfDocumentedException e) {
398 LOG.error("Error call http Patch operation {} on target {}",
400 patchEntity.getTargetNode().toString());
402 editErrors = new ArrayList<>();
403 editErrors.addAll(e.getErrors());
404 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
405 withoutError = false;
410 LOG.error("Unsupported http Patch operation {} on target {}",
412 patchEntity.getTargetNode().toString());
417 // if errors then cancel transaction and return error status
419 patchTransaction.cancel();
420 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
423 // if no errors commit transaction
424 final CountDownLatch waiter = new CountDownLatch(1);
425 final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
426 final PatchStatusContextHelper status = new PatchStatusContextHelper();
428 Futures.addCallback(future, new FutureCallback<Void>() {
430 public void onSuccess(@Nullable final Void result) {
431 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
437 public void onFailure(final Throwable throwable) {
438 // if commit failed it is global error
439 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
440 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
441 false, ImmutableList.of(
442 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
445 }, MoreExecutors.directExecutor());
448 return status.getStatus();
451 // POST configuration
452 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
453 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
454 final String insert, final String point) {
455 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
456 globalSchema, insert, point);
459 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
460 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
461 final String insert, final String point) {
462 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
463 if (domDataBrokerService.isPresent()) {
464 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
465 payload, mountPoint.getSchemaContext(), insert, point);
467 final String errMsg = "DOM data broker service isn't available for mount point " + path;
469 throw new RestconfDocumentedException(errMsg);
472 // DELETE configuration
473 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
474 final YangInstanceIdentifier path) {
475 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
478 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
479 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
480 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
481 if (domDataBrokerService.isPresent()) {
482 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
484 final String errMsg = "DOM data broker service isn't available for mount point " + path;
486 throw new RestconfDocumentedException(errMsg);
490 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type,
491 final NormalizedNode<?, ?> input) {
492 if (this.rpcService == null) {
493 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
495 LOG.trace("Invoke RPC {} with input: {}", type, input);
496 return this.rpcService.invokeRpc(type, input);
499 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
500 final ListenerAdapter listener) {
501 if (listener.isListening()) {
505 final YangInstanceIdentifier path = listener.getPath();
506 DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
507 this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
508 if (changeService == null) {
509 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
510 + this.domDataBroker);
512 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
513 ListenerRegistration<ListenerAdapter> registration =
514 changeService.registerDataTreeChangeListener(root, listener);
515 listener.setRegistration(registration);
518 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
519 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
520 return readDataViaTransaction(transaction, datastore, path, null);
523 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
524 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
525 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
528 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).checkedGet();
529 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
530 prepareDataByParamWithDef(optional.get(), path, withDefa);
531 } catch (ReadFailedException e) {
532 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
533 for (final RpcError error : e.getErrorList()) {
534 if (error.getErrorType() == RpcError.ErrorType.TRANSPORT
535 && error.getTag().equals(ErrorTag.RESOURCE_DENIED.getTagValue())) {
536 throw new RestconfDocumentedException(
539 ErrorTag.RESOURCE_DENIED_TRANSPORT, e);
542 throw new RestconfDocumentedException("Error reading data.", e, e.getErrorList());
546 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
547 final YangInstanceIdentifier path, final String withDefa) {
557 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
560 final SchemaContext ctx = controllerContext.getGlobalSchema();
561 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
562 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
563 if (result instanceof ContainerNode) {
564 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
565 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
566 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
567 return builder.build();
570 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
571 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
572 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
573 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
574 return builder.build();
577 private void buildMapEntryBuilder(
578 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
579 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
580 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
581 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
582 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
583 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
584 if (child instanceof ContainerNode) {
585 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
586 Builders.containerBuilder((ContainerSchemaNode) childSchema);
587 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
588 builder.withChild(childBuilder.build());
589 } else if (child instanceof MapNode) {
590 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
591 Builders.mapBuilder((ListSchemaNode) childSchema);
592 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
593 ((ListSchemaNode) childSchema).getKeyDefinition());
594 builder.withChild(childBuilder.build());
595 } else if (child instanceof LeafNode) {
596 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
597 final Object nodeVal = child.getValue();
598 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
599 Builders.leafBuilder((LeafSchemaNode) childSchema);
600 if (keys.contains(child.getNodeType())) {
601 leafBuilder.withValue(child.getValue());
602 builder.withChild(leafBuilder.build());
605 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
606 leafBuilder.withValue(child.getValue());
607 builder.withChild(leafBuilder.build());
610 if (defaultVal != null && defaultVal.equals(nodeVal)) {
611 leafBuilder.withValue(child.getValue());
612 builder.withChild(leafBuilder.build());
620 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
621 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
622 final List<QName> keys) {
623 for (final MapEntryNode mapEntryNode : result.getValue()) {
624 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
625 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
626 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
627 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
628 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
629 builder.withChild(mapEntryBuilder.build());
633 private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
634 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
635 final YangInstanceIdentifier actualPath, final boolean trim) {
636 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
637 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
638 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
639 if (child instanceof ContainerNode) {
640 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
641 Builders.containerBuilder((ContainerSchemaNode) childSchema);
642 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
643 builder.withChild(builderChild.build());
644 } else if (child instanceof MapNode) {
645 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
646 Builders.mapBuilder((ListSchemaNode) childSchema);
647 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
648 ((ListSchemaNode) childSchema).getKeyDefinition());
649 builder.withChild(childBuilder.build());
650 } else if (child instanceof LeafNode) {
651 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
652 final Object nodeVal = child.getValue();
653 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
654 Builders.leafBuilder((LeafSchemaNode) childSchema);
656 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
657 leafBuilder.withValue(child.getValue());
658 builder.withChild(leafBuilder.build());
661 if (defaultVal != null && defaultVal.equals(nodeVal)) {
662 leafBuilder.withValue(child.getValue());
663 builder.withChild(leafBuilder.build());
671 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
673 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
674 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
675 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
676 final String insert, final String point) {
677 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
678 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
679 return rwTransaction.submit();
683 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
685 private void postDataWithinTransaction(
686 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
687 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
688 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
689 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
692 private void postData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
693 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
694 final SchemaContext schemaContext, final String insert, final String point) {
695 if (insert == null) {
696 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
700 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
701 checkItemDoesNotExists(rwTransaction, datastore, path);
704 if (schemaNode instanceof ListSchemaNode) {
705 final OrderedMapNode readList =
706 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
707 if (readList == null || readList.getValue().isEmpty()) {
708 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
710 rwTransaction.delete(datastore, path.getParent().getParent());
711 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
712 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
716 final OrderedLeafSetNode<?> readLeafList =
717 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
718 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
719 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
721 rwTransaction.delete(datastore, path.getParent());
722 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
723 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
729 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
732 if (schemaNode instanceof ListSchemaNode) {
733 final OrderedMapNode readList =
734 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
735 if (readList == null || readList.getValue().isEmpty()) {
736 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
738 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
743 final OrderedLeafSetNode<?> readLeafList =
744 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
745 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
746 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
748 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
754 if (schemaNode instanceof ListSchemaNode) {
755 final OrderedMapNode readList =
756 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
757 if (readList == null || readList.getValue().isEmpty()) {
758 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
760 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
765 final OrderedLeafSetNode<?> readLeafList =
766 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
767 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
768 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
770 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
771 readLeafList, false);
776 throw new RestconfDocumentedException(
777 "Used bad value of insert parameter. Possible values are first, last, before or after, "
778 + "but was: " + insert);
782 private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rwTransaction,
783 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
784 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
785 final boolean before) {
786 rwTransaction.delete(datastore, path.getParent().getParent());
787 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
788 int lastItemPosition = 0;
789 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
790 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
798 int lastInsertedPosition = 0;
799 final NormalizedNode<?, ?> emptySubtree =
800 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
801 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
802 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
803 if (lastInsertedPosition == lastItemPosition) {
804 checkItemDoesNotExists(rwTransaction, datastore, path);
805 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
807 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
808 checkItemDoesNotExists(rwTransaction, datastore, childPath);
809 rwTransaction.put(datastore, childPath, nodeChild);
810 lastInsertedPosition++;
814 private void insertWithPointListPost(final DOMDataReadWriteTransaction rwTransaction,
815 final LogicalDatastoreType datastore,
816 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
817 final String point, final MapNode readList, final boolean before) {
818 rwTransaction.delete(datastore, path.getParent().getParent());
819 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
820 int lastItemPosition = 0;
821 for (final MapEntryNode mapEntryNode : readList.getValue()) {
822 if (mapEntryNode.getIdentifier()
823 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
831 int lastInsertedPosition = 0;
832 final NormalizedNode<?, ?> emptySubtree =
833 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
834 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
835 for (final MapEntryNode mapEntryNode : readList.getValue()) {
836 if (lastInsertedPosition == lastItemPosition) {
837 checkItemDoesNotExists(rwTransaction, datastore, path);
838 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
840 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
841 checkItemDoesNotExists(rwTransaction, datastore, childPath);
842 rwTransaction.put(datastore, childPath, mapEntryNode);
843 lastInsertedPosition++;
847 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
848 final YangInstanceIdentifier parent = path.getParent();
849 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
850 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
852 if (dataSchemaNode instanceof ListSchemaNode) {
853 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
854 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
856 return dataSchemaNode;
858 if (dataSchemaNode instanceof LeafListSchemaNode) {
859 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
860 throw new RestconfDocumentedException(
861 "Insert parameter can be used only with ordered-by user leaf-list.");
863 return dataSchemaNode;
865 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
868 private static void makeNormalPost(final DOMDataReadWriteTransaction rwTransaction,
869 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
870 final SchemaContext schemaContext) {
871 final Collection<? extends NormalizedNode<?, ?>> children;
872 if (payload instanceof MapNode) {
873 children = ((MapNode) payload).getValue();
874 } else if (payload instanceof LeafSetNode) {
875 children = ((LeafSetNode<?>) payload).getValue();
877 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
881 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
882 if (children.isEmpty()) {
883 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
884 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
888 // Kick off batch existence check first...
889 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
891 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
892 // result of the existence checks...
893 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
894 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
895 for (final NormalizedNode<?, ?> child : children) {
896 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
897 // as that would allow us to skip the existence checks
898 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
901 // ... finally collect existence checks and abort the transaction if any of them failed.
902 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
904 failure = check.getFailure();
905 } catch (InterruptedException e) {
906 rwTransaction.cancel();
907 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
910 if (failure != null) {
911 rwTransaction.cancel();
912 final ReadFailedException e = failure.getValue();
914 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
915 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
918 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
923 private static void simplePostPut(final DOMDataReadWriteTransaction rwTransaction,
924 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
925 final SchemaContext schemaContext) {
926 checkItemDoesNotExists(rwTransaction, datastore, path);
927 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
928 rwTransaction.put(datastore, path, payload);
931 private static boolean doesItemExist(final DOMDataReadWriteTransaction rwTransaction,
932 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
934 return rwTransaction.exists(store, path).checkedGet();
935 } catch (ReadFailedException e) {
936 rwTransaction.cancel();
937 throw new RestconfDocumentedException("Could not determine the existence of path " + path,
938 e, e.getErrorList());
943 * Check if item already exists. Throws error if it does NOT already exist.
944 * @param rwTransaction Current transaction
945 * @param store Used datastore
946 * @param path Path to item to verify its existence
948 private static void checkItemExists(final DOMDataReadWriteTransaction rwTransaction,
949 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
950 if (!doesItemExist(rwTransaction, store, path)) {
951 final String errMsg = "Operation via Restconf was not executed because data does not exist";
952 LOG.trace("{}:{}", errMsg, path);
953 rwTransaction.cancel();
954 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
955 ErrorTag.DATA_MISSING);
960 * Check if item does NOT already exist. Throws error if it already exists.
961 * @param rwTransaction Current transaction
962 * @param store Used datastore
963 * @param path Path to item to verify its existence
965 private static void checkItemDoesNotExists(final DOMDataReadWriteTransaction rwTransaction,
966 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
967 if (doesItemExist(rwTransaction, store, path)) {
968 final String errMsg = "Operation via Restconf was not executed because data already exists";
969 LOG.trace("{}:{}", errMsg, path);
970 rwTransaction.cancel();
971 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
972 ErrorTag.DATA_EXISTS);
977 * PUT data and submit {@link DOMDataReadWriteTransaction}.
984 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
985 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
986 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
987 final String insert, final String point) {
988 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
989 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
990 return readWriteTransaction.submit();
994 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
996 private void putDataWithinTransaction(
997 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
998 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
999 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1000 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1003 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1004 private void putData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1005 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1006 final String insert, final String point) {
1007 if (insert == null) {
1008 makePut(rwTransaction, datastore, path, payload, schemaContext);
1012 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1013 checkItemDoesNotExists(rwTransaction, datastore, path);
1016 if (schemaNode instanceof ListSchemaNode) {
1017 final OrderedMapNode readList =
1018 (OrderedMapNode) this.readConfigurationData(path.getParent());
1019 if (readList == null || readList.getValue().isEmpty()) {
1020 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1022 rwTransaction.delete(datastore, path.getParent());
1023 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1024 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1027 final OrderedLeafSetNode<?> readLeafList =
1028 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1029 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1030 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1032 rwTransaction.delete(datastore, path.getParent());
1033 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1034 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1040 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1043 if (schemaNode instanceof ListSchemaNode) {
1044 final OrderedMapNode readList =
1045 (OrderedMapNode) this.readConfigurationData(path.getParent());
1046 if (readList == null || readList.getValue().isEmpty()) {
1047 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1049 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1053 final OrderedLeafSetNode<?> readLeafList =
1054 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1055 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1056 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1058 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1059 readLeafList, true);
1064 if (schemaNode instanceof ListSchemaNode) {
1065 final OrderedMapNode readList =
1066 (OrderedMapNode) this.readConfigurationData(path.getParent());
1067 if (readList == null || readList.getValue().isEmpty()) {
1068 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1070 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1074 final OrderedLeafSetNode<?> readLeafList =
1075 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1076 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1077 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1079 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1080 readLeafList, false);
1085 throw new RestconfDocumentedException(
1086 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1091 private void insertWithPointLeafListPut(final DOMDataWriteTransaction tx,
1092 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1093 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1094 final boolean before) {
1095 tx.delete(datastore, path.getParent());
1096 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1098 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1099 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1108 final NormalizedNode<?, ?> emptySubtree =
1109 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1110 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1111 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1112 if (index2 == index1) {
1113 simplePut(datastore, path, tx, schemaContext, payload);
1115 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1116 tx.put(datastore, childPath, nodeChild);
1121 private void insertWithPointListPut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1122 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1123 final String point, final OrderedMapNode readList, final boolean before) {
1124 tx.delete(datastore, path.getParent());
1125 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1127 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1128 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1137 final NormalizedNode<?, ?> emptySubtree =
1138 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1139 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1140 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1141 if (index2 == index1) {
1142 simplePut(datastore, path, tx, schemaContext, payload);
1144 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1145 tx.put(datastore, childPath, mapEntryNode);
1150 private static void makePut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1151 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1152 if (payload instanceof MapNode) {
1153 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1154 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1155 ensureParentsByMerge(datastore, path, tx, schemaContext);
1156 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1157 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1158 tx.put(datastore, childPath, child);
1161 simplePut(datastore, path, tx, schemaContext, payload);
1165 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1166 final DOMDataWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode<?, ?> payload) {
1167 ensureParentsByMerge(datastore, path, tx, schemaContext);
1168 tx.put(datastore, path, payload);
1171 private static CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
1172 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1173 final YangInstanceIdentifier path) {
1174 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1175 checkItemExists(readWriteTransaction, datastore, path);
1176 readWriteTransaction.delete(datastore, path);
1177 return readWriteTransaction.submit();
1180 private static void deleteDataWithinTransaction(final DOMDataWriteTransaction tx,
1181 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1182 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1183 tx.delete(datastore, path);
1186 private static void mergeDataWithinTransaction(final DOMDataWriteTransaction tx,
1187 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1188 final SchemaContext schemaContext) {
1189 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1190 ensureParentsByMerge(datastore, path, tx, schemaContext);
1192 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1193 // OpenDaylight should not change it.
1194 tx.merge(datastore, path, payload);
1197 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1198 if (listener.isListening()) {
1202 final SchemaPath path = listener.getSchemaPath();
1203 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1204 .registerNotificationListener(listener, path);
1206 listener.setRegistration(registration);
1209 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1210 final YangInstanceIdentifier normalizedPath, final DOMDataWriteTransaction tx,
1211 final SchemaContext schemaContext) {
1212 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1213 YangInstanceIdentifier rootNormalizedPath = null;
1215 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1217 while (it.hasNext()) {
1218 final PathArgument pathArgument = it.next();
1219 if (rootNormalizedPath == null) {
1220 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1224 normalizedPathWithoutChildArgs.add(pathArgument);
1228 if (normalizedPathWithoutChildArgs.isEmpty()) {
1232 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1234 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1235 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1236 tx.merge(store, rootNormalizedPath, parentStructure);
1239 private static final class PatchStatusContextHelper {
1240 PatchStatusContext status;
1242 public PatchStatusContext getStatus() {
1246 public void setStatus(final PatchStatusContext status) {
1247 this.status = status;