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(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
104 final DOMNotificationService domNotification, final 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(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
112 final DOMNotificationService domNotification, final 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 throw dataBrokerUnavailable(path);
183 * Read operational data by path.
189 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
190 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
191 return readDataViaTransaction(tx, OPERATIONAL, path);
196 * Read operational data from mount point by path.
199 * mount point for reading data
204 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
205 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
206 if (domDataBrokerService.isPresent()) {
207 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
208 return readDataViaTransaction(tx, OPERATIONAL, path);
211 throw dataBrokerUnavailable(path);
215 * <b>PUT configuration data</b>
218 * Prepare result(status) for PUT operation and PUT data via transaction.
219 * Return wrapped status and future from PUT.
221 * @param globalSchema
222 * used by merge parents (if contains list)
231 * @return wrapper of status and future of PUT
233 public PutResult commitConfigurationDataPut(
234 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
235 final String insert, final String point) {
236 Preconditions.checkNotNull(globalSchema);
237 Preconditions.checkNotNull(path);
238 Preconditions.checkNotNull(payload);
240 final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
241 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
243 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
244 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
245 return new PutResult(status, future);
249 * <b>PUT configuration data (Mount point)</b>
252 * Prepare result(status) for PUT operation and PUT data via transaction.
253 * Return wrapped status and future from PUT.
256 * mount point for getting transaction for operation and schema
257 * context for merging parents(if contains list)
266 * @return wrapper of status and future of PUT
268 public PutResult commitMountPointDataPut(
269 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
270 final String insert, final String point) {
271 Preconditions.checkNotNull(mountPoint);
272 Preconditions.checkNotNull(path);
273 Preconditions.checkNotNull(payload);
275 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
276 if (domDataBrokerService.isPresent()) {
277 final DOMDataReadWriteTransaction newReadWriteTransaction =
278 domDataBrokerService.get().newReadWriteTransaction();
279 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
280 ? Status.OK : Status.CREATED;
281 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
282 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
284 return new PutResult(status, future);
286 throw dataBrokerUnavailable(path);
289 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
291 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
293 // get new transaction and schema context on server or on mounted device
294 final SchemaContext schemaContext;
295 final DOMDataReadWriteTransaction patchTransaction;
296 if (mountPoint == null) {
297 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
298 patchTransaction = this.domDataBroker.newReadWriteTransaction();
300 schemaContext = mountPoint.getSchemaContext();
302 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
304 if (optional.isPresent()) {
305 patchTransaction = optional.get().newReadWriteTransaction();
307 // if mount point does not have broker it is not possible to continue and global error is reported
308 LOG.error("Http Patch {} has failed - device {} does not support broker service",
309 patchContext.getPatchId(), mountPoint.getIdentifier());
310 return new PatchStatusContext(
311 patchContext.getPatchId(),
314 ImmutableList.of(new RestconfError(
315 ErrorType.APPLICATION,
316 ErrorTag.OPERATION_FAILED,
317 "DOM data broker service isn't available for mount point "
318 + mountPoint.getIdentifier()))
323 final List<PatchStatusEntity> editCollection = new ArrayList<>();
324 List<RestconfError> editErrors;
325 boolean withoutError = true;
327 for (final PatchEntity patchEntity : patchContext.getData()) {
328 final PatchEditOperation operation = patchEntity.getOperation();
333 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
334 patchEntity.getNode(), schemaContext);
335 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
336 } catch (final RestconfDocumentedException e) {
337 LOG.error("Error call http Patch operation {} on target {}",
339 patchEntity.getTargetNode().toString());
341 editErrors = new ArrayList<>();
342 editErrors.addAll(e.getErrors());
343 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
344 withoutError = false;
351 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
352 .getTargetNode(), patchEntity.getNode(), schemaContext);
353 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
354 } catch (final RestconfDocumentedException e) {
355 LOG.error("Error call http Patch operation {} on target {}",
357 patchEntity.getTargetNode().toString());
359 editErrors = new ArrayList<>();
360 editErrors.addAll(e.getErrors());
361 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
362 withoutError = false;
370 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
372 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
373 } catch (final RestconfDocumentedException e) {
374 LOG.error("Error call http Patch operation {} on target {}",
376 patchEntity.getTargetNode().toString());
378 editErrors = new ArrayList<>();
379 editErrors.addAll(e.getErrors());
380 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
381 withoutError = false;
388 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
389 patchEntity.getNode(), schemaContext);
390 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
391 } catch (final RestconfDocumentedException e) {
392 LOG.error("Error call http Patch operation {} on target {}",
394 patchEntity.getTargetNode().toString());
396 editErrors = new ArrayList<>();
397 editErrors.addAll(e.getErrors());
398 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
399 withoutError = false;
404 LOG.error("Unsupported http Patch operation {} on target {}",
406 patchEntity.getTargetNode().toString());
411 // if errors then cancel transaction and return error status
413 patchTransaction.cancel();
414 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
417 // if no errors commit transaction
418 final CountDownLatch waiter = new CountDownLatch(1);
419 final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
420 final PatchStatusContextHelper status = new PatchStatusContextHelper();
422 Futures.addCallback(future, new FutureCallback<Void>() {
424 public void onSuccess(@Nullable final Void result) {
425 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
431 public void onFailure(final Throwable throwable) {
432 // if commit failed it is global error
433 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
434 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
435 false, ImmutableList.of(
436 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
439 }, MoreExecutors.directExecutor());
442 return status.getStatus();
445 // POST configuration
446 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
447 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
448 final String insert, final String point) {
449 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
450 globalSchema, insert, point);
453 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
454 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
455 final String insert, final String point) {
456 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
457 if (domDataBrokerService.isPresent()) {
458 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
459 payload, mountPoint.getSchemaContext(), insert, point);
461 throw dataBrokerUnavailable(path);
464 // DELETE configuration
465 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
466 final YangInstanceIdentifier path) {
467 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
470 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
471 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
472 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
473 if (domDataBrokerService.isPresent()) {
474 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
476 throw dataBrokerUnavailable(path);
480 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type,
481 final NormalizedNode<?, ?> input) {
482 if (this.rpcService == null) {
483 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
485 LOG.trace("Invoke RPC {} with input: {}", type, input);
486 return this.rpcService.invokeRpc(type, input);
489 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
490 final ListenerAdapter listener) {
491 if (listener.isListening()) {
495 final YangInstanceIdentifier path = listener.getPath();
496 DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
497 this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
498 if (changeService == null) {
499 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
500 + this.domDataBroker);
502 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
503 ListenerRegistration<ListenerAdapter> registration =
504 changeService.registerDataTreeChangeListener(root, listener);
505 listener.setRegistration(registration);
508 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
509 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
510 return readDataViaTransaction(transaction, datastore, path, null);
513 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
514 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
515 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
518 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).checkedGet();
519 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
520 prepareDataByParamWithDef(optional.get(), path, withDefa);
521 } catch (ReadFailedException e) {
522 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
523 for (final RpcError error : e.getErrorList()) {
524 if (error.getErrorType() == RpcError.ErrorType.TRANSPORT
525 && error.getTag().equals(ErrorTag.RESOURCE_DENIED.getTagValue())) {
526 throw new RestconfDocumentedException(
529 ErrorTag.RESOURCE_DENIED_TRANSPORT, e);
532 throw new RestconfDocumentedException("Error reading data.", e, e.getErrorList());
536 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
537 final YangInstanceIdentifier path, final String withDefa) {
547 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
550 final SchemaContext ctx = controllerContext.getGlobalSchema();
551 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
552 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
553 if (result instanceof ContainerNode) {
554 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
555 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
556 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
557 return builder.build();
560 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
561 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
562 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
563 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
564 return builder.build();
567 private void buildMapEntryBuilder(
568 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
569 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
570 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
571 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
572 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
573 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
574 if (child instanceof ContainerNode) {
575 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
576 Builders.containerBuilder((ContainerSchemaNode) childSchema);
577 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
578 builder.withChild(childBuilder.build());
579 } else if (child instanceof MapNode) {
580 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
581 Builders.mapBuilder((ListSchemaNode) childSchema);
582 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
583 ((ListSchemaNode) childSchema).getKeyDefinition());
584 builder.withChild(childBuilder.build());
585 } else if (child instanceof LeafNode) {
586 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
587 final Object nodeVal = child.getValue();
588 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
589 Builders.leafBuilder((LeafSchemaNode) childSchema);
590 if (keys.contains(child.getNodeType())) {
591 leafBuilder.withValue(child.getValue());
592 builder.withChild(leafBuilder.build());
595 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
596 leafBuilder.withValue(child.getValue());
597 builder.withChild(leafBuilder.build());
600 if (defaultVal != null && defaultVal.equals(nodeVal)) {
601 leafBuilder.withValue(child.getValue());
602 builder.withChild(leafBuilder.build());
610 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
611 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
612 final List<QName> keys) {
613 for (final MapEntryNode mapEntryNode : result.getValue()) {
614 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
615 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
616 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
617 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
618 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
619 builder.withChild(mapEntryBuilder.build());
623 private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
624 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
625 final YangInstanceIdentifier actualPath, final boolean trim) {
626 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
627 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
628 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
629 if (child instanceof ContainerNode) {
630 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
631 Builders.containerBuilder((ContainerSchemaNode) childSchema);
632 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
633 builder.withChild(builderChild.build());
634 } else if (child instanceof MapNode) {
635 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
636 Builders.mapBuilder((ListSchemaNode) childSchema);
637 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
638 ((ListSchemaNode) childSchema).getKeyDefinition());
639 builder.withChild(childBuilder.build());
640 } else if (child instanceof LeafNode) {
641 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
642 final Object nodeVal = child.getValue();
643 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
644 Builders.leafBuilder((LeafSchemaNode) childSchema);
646 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
647 leafBuilder.withValue(child.getValue());
648 builder.withChild(leafBuilder.build());
651 if (defaultVal != null && defaultVal.equals(nodeVal)) {
652 leafBuilder.withValue(child.getValue());
653 builder.withChild(leafBuilder.build());
661 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
663 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
664 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
665 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
666 final String insert, final String point) {
667 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
668 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
669 return rwTransaction.submit();
673 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
675 private void postDataWithinTransaction(
676 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
677 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
678 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
679 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
682 private void postData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
683 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
684 final SchemaContext schemaContext, final String insert, final String point) {
685 if (insert == null) {
686 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
690 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
691 checkItemDoesNotExists(rwTransaction, datastore, path);
694 if (schemaNode instanceof ListSchemaNode) {
695 final OrderedMapNode readList =
696 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
697 if (readList == null || readList.getValue().isEmpty()) {
698 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
700 rwTransaction.delete(datastore, path.getParent().getParent());
701 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
702 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
706 final OrderedLeafSetNode<?> readLeafList =
707 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
708 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
709 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
711 rwTransaction.delete(datastore, path.getParent());
712 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
713 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
719 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
722 if (schemaNode instanceof ListSchemaNode) {
723 final OrderedMapNode readList =
724 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
725 if (readList == null || readList.getValue().isEmpty()) {
726 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
728 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
733 final OrderedLeafSetNode<?> readLeafList =
734 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
735 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
736 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
738 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
744 if (schemaNode instanceof ListSchemaNode) {
745 final OrderedMapNode readList =
746 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
747 if (readList == null || readList.getValue().isEmpty()) {
748 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
750 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
755 final OrderedLeafSetNode<?> readLeafList =
756 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
757 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
758 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
760 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
761 readLeafList, false);
766 throw new RestconfDocumentedException(
767 "Used bad value of insert parameter. Possible values are first, last, before or after, "
768 + "but was: " + insert);
772 private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rwTransaction,
773 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
774 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
775 final boolean before) {
776 rwTransaction.delete(datastore, path.getParent().getParent());
777 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
778 int lastItemPosition = 0;
779 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
780 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
788 int lastInsertedPosition = 0;
789 final NormalizedNode<?, ?> emptySubtree =
790 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
791 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
792 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
793 if (lastInsertedPosition == lastItemPosition) {
794 checkItemDoesNotExists(rwTransaction, datastore, path);
795 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
797 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
798 checkItemDoesNotExists(rwTransaction, datastore, childPath);
799 rwTransaction.put(datastore, childPath, nodeChild);
800 lastInsertedPosition++;
804 private void insertWithPointListPost(final DOMDataReadWriteTransaction rwTransaction,
805 final LogicalDatastoreType datastore,
806 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
807 final String point, final MapNode readList, final boolean before) {
808 rwTransaction.delete(datastore, path.getParent().getParent());
809 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
810 int lastItemPosition = 0;
811 for (final MapEntryNode mapEntryNode : readList.getValue()) {
812 if (mapEntryNode.getIdentifier()
813 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
821 int lastInsertedPosition = 0;
822 final NormalizedNode<?, ?> emptySubtree =
823 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
824 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
825 for (final MapEntryNode mapEntryNode : readList.getValue()) {
826 if (lastInsertedPosition == lastItemPosition) {
827 checkItemDoesNotExists(rwTransaction, datastore, path);
828 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
830 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
831 checkItemDoesNotExists(rwTransaction, datastore, childPath);
832 rwTransaction.put(datastore, childPath, mapEntryNode);
833 lastInsertedPosition++;
837 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
838 final YangInstanceIdentifier parent = path.getParent();
839 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
840 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
842 if (dataSchemaNode instanceof ListSchemaNode) {
843 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
844 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
846 return dataSchemaNode;
848 if (dataSchemaNode instanceof LeafListSchemaNode) {
849 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
850 throw new RestconfDocumentedException(
851 "Insert parameter can be used only with ordered-by user leaf-list.");
853 return dataSchemaNode;
855 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
858 private static void makeNormalPost(final DOMDataReadWriteTransaction rwTransaction,
859 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
860 final SchemaContext schemaContext) {
861 final Collection<? extends NormalizedNode<?, ?>> children;
862 if (payload instanceof MapNode) {
863 children = ((MapNode) payload).getValue();
864 } else if (payload instanceof LeafSetNode) {
865 children = ((LeafSetNode<?>) payload).getValue();
867 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
871 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
872 if (children.isEmpty()) {
873 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
874 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
878 // Kick off batch existence check first...
879 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
881 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
882 // result of the existence checks...
883 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
884 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
885 for (final NormalizedNode<?, ?> child : children) {
886 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
887 // as that would allow us to skip the existence checks
888 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
891 // ... finally collect existence checks and abort the transaction if any of them failed.
892 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
894 failure = check.getFailure();
895 } catch (InterruptedException e) {
896 rwTransaction.cancel();
897 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
900 if (failure != null) {
901 rwTransaction.cancel();
902 final ReadFailedException e = failure.getValue();
904 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
905 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
908 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
913 private static void simplePostPut(final DOMDataReadWriteTransaction rwTransaction,
914 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
915 final SchemaContext schemaContext) {
916 checkItemDoesNotExists(rwTransaction, datastore, path);
917 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
918 rwTransaction.put(datastore, path, payload);
921 private static boolean doesItemExist(final DOMDataReadWriteTransaction rwTransaction,
922 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
924 return rwTransaction.exists(store, path).checkedGet();
925 } catch (ReadFailedException e) {
926 rwTransaction.cancel();
927 throw new RestconfDocumentedException("Could not determine the existence of path " + path,
928 e, e.getErrorList());
933 * Check if item already exists. Throws error if it does NOT already exist.
934 * @param rwTransaction Current transaction
935 * @param store Used datastore
936 * @param path Path to item to verify its existence
938 private static void checkItemExists(final DOMDataReadWriteTransaction rwTransaction,
939 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
940 if (!doesItemExist(rwTransaction, store, path)) {
941 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
942 rwTransaction.cancel();
943 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
944 ErrorTag.DATA_MISSING);
949 * Check if item does NOT already exist. Throws error if it already exists.
950 * @param rwTransaction Current transaction
951 * @param store Used datastore
952 * @param path Path to item to verify its existence
954 private static void checkItemDoesNotExists(final DOMDataReadWriteTransaction rwTransaction,
955 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
956 if (doesItemExist(rwTransaction, store, path)) {
957 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
958 rwTransaction.cancel();
959 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
960 ErrorTag.DATA_EXISTS);
965 * PUT data and submit {@link DOMDataReadWriteTransaction}.
972 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
973 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
974 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
975 final String insert, final String point) {
976 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
977 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
978 return readWriteTransaction.submit();
982 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
984 private void putDataWithinTransaction(
985 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
986 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
987 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
988 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
991 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
992 private void putData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
993 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
994 final String insert, final String point) {
995 if (insert == null) {
996 makePut(rwTransaction, datastore, path, payload, schemaContext);
1000 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1001 checkItemDoesNotExists(rwTransaction, datastore, path);
1004 if (schemaNode instanceof ListSchemaNode) {
1005 final OrderedMapNode readList =
1006 (OrderedMapNode) this.readConfigurationData(path.getParent());
1007 if (readList == null || readList.getValue().isEmpty()) {
1008 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1010 rwTransaction.delete(datastore, path.getParent());
1011 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1012 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1015 final OrderedLeafSetNode<?> readLeafList =
1016 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1017 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1018 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1020 rwTransaction.delete(datastore, path.getParent());
1021 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1022 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1028 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1031 if (schemaNode instanceof ListSchemaNode) {
1032 final OrderedMapNode readList =
1033 (OrderedMapNode) this.readConfigurationData(path.getParent());
1034 if (readList == null || readList.getValue().isEmpty()) {
1035 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1037 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1041 final OrderedLeafSetNode<?> readLeafList =
1042 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1043 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1044 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1046 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1047 readLeafList, true);
1052 if (schemaNode instanceof ListSchemaNode) {
1053 final OrderedMapNode readList =
1054 (OrderedMapNode) this.readConfigurationData(path.getParent());
1055 if (readList == null || readList.getValue().isEmpty()) {
1056 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1058 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1062 final OrderedLeafSetNode<?> readLeafList =
1063 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1064 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1065 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1067 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1068 readLeafList, false);
1073 throw new RestconfDocumentedException(
1074 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1079 private void insertWithPointLeafListPut(final DOMDataWriteTransaction tx,
1080 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1081 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1082 final boolean before) {
1083 tx.delete(datastore, path.getParent());
1084 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1086 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1087 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1096 final NormalizedNode<?, ?> emptySubtree =
1097 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1098 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1099 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1100 if (index2 == index1) {
1101 simplePut(datastore, path, tx, schemaContext, payload);
1103 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1104 tx.put(datastore, childPath, nodeChild);
1109 private void insertWithPointListPut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1110 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1111 final String point, final OrderedMapNode readList, final boolean before) {
1112 tx.delete(datastore, path.getParent());
1113 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1115 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1116 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1125 final NormalizedNode<?, ?> emptySubtree =
1126 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1127 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1128 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1129 if (index2 == index1) {
1130 simplePut(datastore, path, tx, schemaContext, payload);
1132 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1133 tx.put(datastore, childPath, mapEntryNode);
1138 private static void makePut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1139 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1140 if (payload instanceof MapNode) {
1141 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1142 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1143 ensureParentsByMerge(datastore, path, tx, schemaContext);
1144 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1145 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1146 tx.put(datastore, childPath, child);
1149 simplePut(datastore, path, tx, schemaContext, payload);
1153 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1154 final DOMDataWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode<?, ?> payload) {
1155 ensureParentsByMerge(datastore, path, tx, schemaContext);
1156 tx.put(datastore, path, payload);
1159 private static CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
1160 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1161 final YangInstanceIdentifier path) {
1162 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1163 checkItemExists(readWriteTransaction, datastore, path);
1164 readWriteTransaction.delete(datastore, path);
1165 return readWriteTransaction.submit();
1168 private static void deleteDataWithinTransaction(final DOMDataWriteTransaction tx,
1169 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1170 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1171 tx.delete(datastore, path);
1174 private static void mergeDataWithinTransaction(final DOMDataWriteTransaction tx,
1175 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1176 final SchemaContext schemaContext) {
1177 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1178 ensureParentsByMerge(datastore, path, tx, schemaContext);
1180 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1181 // OpenDaylight should not change it.
1182 tx.merge(datastore, path, payload);
1185 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1186 if (listener.isListening()) {
1190 final SchemaPath path = listener.getSchemaPath();
1191 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1192 .registerNotificationListener(listener, path);
1194 listener.setRegistration(registration);
1197 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1198 final YangInstanceIdentifier normalizedPath, final DOMDataWriteTransaction tx,
1199 final SchemaContext schemaContext) {
1200 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1201 YangInstanceIdentifier rootNormalizedPath = null;
1203 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1205 while (it.hasNext()) {
1206 final PathArgument pathArgument = it.next();
1207 if (rootNormalizedPath == null) {
1208 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1212 normalizedPathWithoutChildArgs.add(pathArgument);
1216 if (normalizedPathWithoutChildArgs.isEmpty()) {
1220 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1222 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1223 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1224 tx.merge(store, rootNormalizedPath, parentStructure);
1227 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1228 LOG.warn("DOM data broker service is not available for mount point {}", path);
1229 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1232 private static final class PatchStatusContextHelper {
1233 PatchStatusContext status;
1235 public PatchStatusContext getStatus() {
1239 public void setStatus(final PatchStatusContext status) {
1240 this.status = status;