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.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map.Entry;
25 import java.util.concurrent.CountDownLatch;
26 import javax.annotation.Nullable;
27 import javax.ws.rs.core.Response.Status;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
34 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
37 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
38 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
39 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
40 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
41 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
42 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
43 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
44 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
45 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
46 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
47 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
48 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
49 import org.opendaylight.restconf.common.errors.RestconfError;
50 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
51 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
52 import org.opendaylight.restconf.common.patch.PatchContext;
53 import org.opendaylight.restconf.common.patch.PatchEditOperation;
54 import org.opendaylight.restconf.common.patch.PatchEntity;
55 import org.opendaylight.restconf.common.patch.PatchStatusContext;
56 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
57 import org.opendaylight.yangtools.concepts.ListenerRegistration;
58 import org.opendaylight.yangtools.yang.common.QName;
59 import org.opendaylight.yangtools.yang.common.RpcError;
60 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
64 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
65 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
66 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
69 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
74 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
75 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
76 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
77 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
78 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
79 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
80 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
81 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
82 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
83 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
85 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
86 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
87 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
88 import org.slf4j.Logger;
89 import org.slf4j.LoggerFactory;
91 public class BrokerFacade {
92 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
93 private static final BrokerFacade INSTANCE = new BrokerFacade();
95 private volatile DOMRpcService rpcService;
97 private DOMDataBroker domDataBroker;
98 private DOMNotificationService domNotification;
99 private ControllerContext controllerContext;
105 public void setRpcService(final DOMRpcService router) {
106 this.rpcService = router;
109 public void setDomNotificationService(final DOMNotificationService service) {
110 this.domNotification = service;
113 public void setControllerContext(ControllerContext controllerContext) {
114 this.controllerContext = controllerContext;
117 public static BrokerFacade getInstance() {
118 return BrokerFacade.INSTANCE;
121 private void checkPreconditions() {
122 if (this.domDataBroker == null) {
123 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
128 * Read config data by path.
134 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
135 return readConfigurationData(path, null);
139 * Read config data by path.
144 * value of with-defaults parameter
147 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
148 checkPreconditions();
149 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
150 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
155 * Read config data from mount point by path.
158 * mount point for reading data
163 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
164 final YangInstanceIdentifier path) {
165 return readConfigurationData(mountPoint, path, null);
169 * Read config data from mount point by path.
172 * mount point for reading data
176 * value of with-defaults parameter
179 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
180 final String withDefa) {
181 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
182 if (domDataBrokerService.isPresent()) {
183 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
184 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
187 final String errMsg = "DOM data broker service isn't available for mount point " + path;
189 throw new RestconfDocumentedException(errMsg);
193 * Read operational data by path.
199 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
200 checkPreconditions();
202 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
203 return readDataViaTransaction(tx, OPERATIONAL, path);
208 * Read operational data from mount point by path.
211 * mount point for reading data
216 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
217 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
218 if (domDataBrokerService.isPresent()) {
219 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
220 return readDataViaTransaction(tx, OPERATIONAL, path);
223 final String errMsg = "DOM data broker service isn't available for mount point " + path;
225 throw new RestconfDocumentedException(errMsg);
229 * <b>PUT configuration data</b>
232 * Prepare result(status) for PUT operation and PUT data via transaction.
233 * Return wrapped status and future from PUT.
235 * @param globalSchema
236 * used by merge parents (if contains list)
245 * @return wrapper of status and future of PUT
247 public PutResult commitConfigurationDataPut(
248 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
249 final String insert, final String point) {
250 Preconditions.checkNotNull(globalSchema);
251 Preconditions.checkNotNull(path);
252 Preconditions.checkNotNull(payload);
254 checkPreconditions();
256 final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
257 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
259 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
260 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
261 return new PutResult(status, future);
265 * <b>PUT configuration data (Mount point)</b>
268 * Prepare result(status) for PUT operation and PUT data via transaction.
269 * Return wrapped status and future from PUT.
272 * mount point for getting transaction for operation and schema
273 * context for merging parents(if contains list)
282 * @return wrapper of status and future of PUT
284 public PutResult commitMountPointDataPut(
285 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
286 final String insert, final String point) {
287 Preconditions.checkNotNull(mountPoint);
288 Preconditions.checkNotNull(path);
289 Preconditions.checkNotNull(payload);
291 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
292 if (domDataBrokerService.isPresent()) {
293 final DOMDataReadWriteTransaction newReadWriteTransaction =
294 domDataBrokerService.get().newReadWriteTransaction();
295 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
296 ? Status.OK : Status.CREATED;
297 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
298 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
300 return new PutResult(status, future);
302 final String errMsg = "DOM data broker service isn't available for mount point " + path;
304 throw new RestconfDocumentedException(errMsg);
307 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
309 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
311 // get new transaction and schema context on server or on mounted device
312 final SchemaContext schemaContext;
313 final DOMDataReadWriteTransaction patchTransaction;
314 if (mountPoint == null) {
315 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
316 patchTransaction = this.domDataBroker.newReadWriteTransaction();
318 schemaContext = mountPoint.getSchemaContext();
320 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
322 if (optional.isPresent()) {
323 patchTransaction = optional.get().newReadWriteTransaction();
325 // if mount point does not have broker it is not possible to continue and global error is reported
326 LOG.error("Http Patch {} has failed - device {} does not support broker service",
327 patchContext.getPatchId(), mountPoint.getIdentifier());
328 return new PatchStatusContext(
329 patchContext.getPatchId(),
332 ImmutableList.of(new RestconfError(
333 ErrorType.APPLICATION,
334 ErrorTag.OPERATION_FAILED,
335 "DOM data broker service isn't available for mount point "
336 + mountPoint.getIdentifier()))
341 final List<PatchStatusEntity> editCollection = new ArrayList<>();
342 List<RestconfError> editErrors;
343 boolean withoutError = true;
345 for (final PatchEntity patchEntity : patchContext.getData()) {
346 final PatchEditOperation operation = patchEntity.getOperation();
351 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
352 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;
369 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
370 .getTargetNode(), patchEntity.getNode(), schemaContext);
371 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
372 } catch (final RestconfDocumentedException e) {
373 LOG.error("Error call http Patch operation {} on target {}",
375 patchEntity.getTargetNode().toString());
377 editErrors = new ArrayList<>();
378 editErrors.addAll(e.getErrors());
379 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
380 withoutError = false;
388 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
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;
406 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
407 patchEntity.getNode(), schemaContext);
408 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
409 } catch (final RestconfDocumentedException e) {
410 LOG.error("Error call http Patch operation {} on target {}",
412 patchEntity.getTargetNode().toString());
414 editErrors = new ArrayList<>();
415 editErrors.addAll(e.getErrors());
416 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
417 withoutError = false;
422 LOG.error("Unsupported http Patch operation {} on target {}",
424 patchEntity.getTargetNode().toString());
429 // if errors then cancel transaction and return error status
431 patchTransaction.cancel();
432 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
435 // if no errors commit transaction
436 final CountDownLatch waiter = new CountDownLatch(1);
437 final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
438 final PatchStatusContextHelper status = new PatchStatusContextHelper();
440 Futures.addCallback(future, new FutureCallback<Void>() {
442 public void onSuccess(@Nullable final Void result) {
443 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
449 public void onFailure(final Throwable throwable) {
450 // if commit failed it is global error
451 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
452 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
453 false, ImmutableList.of(
454 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
457 }, MoreExecutors.directExecutor());
460 return status.getStatus();
463 // POST configuration
464 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
465 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
466 final String insert, final String point) {
467 checkPreconditions();
468 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
469 globalSchema, insert, point);
472 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
473 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
474 final String insert, final String point) {
475 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
476 if (domDataBrokerService.isPresent()) {
477 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
478 payload, mountPoint.getSchemaContext(), insert, point);
480 final String errMsg = "DOM data broker service isn't available for mount point " + path;
482 throw new RestconfDocumentedException(errMsg);
485 // DELETE configuration
486 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
487 final YangInstanceIdentifier path) {
488 checkPreconditions();
489 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
492 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
493 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
494 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
495 if (domDataBrokerService.isPresent()) {
496 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
498 final String errMsg = "DOM data broker service isn't available for mount point " + path;
500 throw new RestconfDocumentedException(errMsg);
504 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type,
505 final NormalizedNode<?, ?> input) {
506 checkPreconditions();
507 if (this.rpcService == null) {
508 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
510 LOG.trace("Invoke RPC {} with input: {}", type, input);
511 return this.rpcService.invokeRpc(type, input);
514 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
515 final ListenerAdapter listener) {
516 checkPreconditions();
518 if (listener.isListening()) {
522 final YangInstanceIdentifier path = listener.getPath();
523 DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
524 this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
525 if (changeService == null) {
526 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
527 + this.domDataBroker);
529 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
530 ListenerRegistration<ListenerAdapter> registration =
531 changeService.registerDataTreeChangeListener(root, listener);
532 listener.setRegistration(registration);
535 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
536 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
537 return readDataViaTransaction(transaction, datastore, path, null);
540 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
541 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
542 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
545 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).checkedGet();
546 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
547 prepareDataByParamWithDef(optional.get(), path, withDefa);
548 } catch (ReadFailedException e) {
549 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
550 for (final RpcError error : e.getErrorList()) {
551 if (error.getErrorType() == RpcError.ErrorType.TRANSPORT
552 && error.getTag().equals(ErrorTag.RESOURCE_DENIED.getTagValue())) {
553 throw new RestconfDocumentedException(
556 ErrorTag.RESOURCE_DENIED_TRANSPORT, e);
559 throw new RestconfDocumentedException("Error reading data.", e, e.getErrorList());
563 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
564 final YangInstanceIdentifier path, final String withDefa) {
574 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
577 final SchemaContext ctx = controllerContext.getGlobalSchema();
578 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
579 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
580 if (result instanceof ContainerNode) {
581 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
582 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
583 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
584 return builder.build();
587 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
588 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
589 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
590 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
591 return builder.build();
594 private void buildMapEntryBuilder(
595 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
596 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
597 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
598 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
599 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
600 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
601 if (child instanceof ContainerNode) {
602 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
603 Builders.containerBuilder((ContainerSchemaNode) childSchema);
604 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
605 builder.withChild(childBuilder.build());
606 } else if (child instanceof MapNode) {
607 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
608 Builders.mapBuilder((ListSchemaNode) childSchema);
609 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
610 ((ListSchemaNode) childSchema).getKeyDefinition());
611 builder.withChild(childBuilder.build());
612 } else if (child instanceof LeafNode) {
613 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
614 final Object nodeVal = ((LeafNode<?>) child).getValue();
615 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
616 Builders.leafBuilder((LeafSchemaNode) childSchema);
617 if (keys.contains(child.getNodeType())) {
618 leafBuilder.withValue(((LeafNode<?>) child).getValue());
619 builder.withChild(leafBuilder.build());
622 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
623 leafBuilder.withValue(((LeafNode<?>) child).getValue());
624 builder.withChild(leafBuilder.build());
627 if (defaultVal != null && defaultVal.equals(nodeVal)) {
628 leafBuilder.withValue(((LeafNode<?>) child).getValue());
629 builder.withChild(leafBuilder.build());
637 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
638 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
639 final List<QName> keys) {
640 for (final MapEntryNode mapEntryNode : result.getValue()) {
641 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
642 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
643 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
644 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
645 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
646 builder.withChild(mapEntryBuilder.build());
650 private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
651 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
652 final YangInstanceIdentifier actualPath, final boolean trim) {
653 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
654 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
655 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
656 if (child instanceof ContainerNode) {
657 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
658 Builders.containerBuilder((ContainerSchemaNode) childSchema);
659 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
660 builder.withChild(builderChild.build());
661 } else if (child instanceof MapNode) {
662 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
663 Builders.mapBuilder((ListSchemaNode) childSchema);
664 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
665 ((ListSchemaNode) childSchema).getKeyDefinition());
666 builder.withChild(childBuilder.build());
667 } else if (child instanceof LeafNode) {
668 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
669 final Object nodeVal = ((LeafNode<?>) child).getValue();
670 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
671 Builders.leafBuilder((LeafSchemaNode) childSchema);
673 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
674 leafBuilder.withValue(((LeafNode<?>) child).getValue());
675 builder.withChild(leafBuilder.build());
678 if (defaultVal != null && defaultVal.equals(nodeVal)) {
679 leafBuilder.withValue(((LeafNode<?>) child).getValue());
680 builder.withChild(leafBuilder.build());
688 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
690 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
691 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
692 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
693 final String insert, final String point) {
694 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
695 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
696 return rwTransaction.submit();
700 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
702 private void postDataWithinTransaction(
703 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
704 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
705 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
706 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
709 private void postData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
710 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
711 final SchemaContext schemaContext, final String insert, final String point) {
712 if (insert == null) {
713 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
717 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
718 checkItemDoesNotExists(rwTransaction, datastore, path);
721 if (schemaNode instanceof ListSchemaNode) {
722 final OrderedMapNode readList =
723 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
724 if (readList == null || readList.getValue().isEmpty()) {
725 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
727 rwTransaction.delete(datastore, path.getParent().getParent());
728 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
729 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
733 final OrderedLeafSetNode<?> readLeafList =
734 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
735 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
736 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
738 rwTransaction.delete(datastore, path.getParent());
739 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
740 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
746 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
749 if (schemaNode instanceof ListSchemaNode) {
750 final OrderedMapNode readList =
751 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
752 if (readList == null || readList.getValue().isEmpty()) {
753 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
755 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
760 final OrderedLeafSetNode<?> readLeafList =
761 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
762 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
763 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
765 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
771 if (schemaNode instanceof ListSchemaNode) {
772 final OrderedMapNode readList =
773 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
774 if (readList == null || readList.getValue().isEmpty()) {
775 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
777 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
782 final OrderedLeafSetNode<?> readLeafList =
783 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
784 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
785 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
787 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
788 readLeafList, false);
793 throw new RestconfDocumentedException(
794 "Used bad value of insert parameter. Possible values are first, last, before or after, "
795 + "but was: " + insert);
799 private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rwTransaction,
800 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
801 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
802 final boolean before) {
803 rwTransaction.delete(datastore, path.getParent().getParent());
804 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
805 int lastItemPosition = 0;
806 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
807 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
815 int lastInsertedPosition = 0;
816 final NormalizedNode<?, ?> emptySubtree =
817 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
818 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
819 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
820 if (lastInsertedPosition == lastItemPosition) {
821 checkItemDoesNotExists(rwTransaction, datastore, path);
822 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
824 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
825 checkItemDoesNotExists(rwTransaction, datastore, childPath);
826 rwTransaction.put(datastore, childPath, nodeChild);
827 lastInsertedPosition++;
831 private void insertWithPointListPost(final DOMDataReadWriteTransaction rwTransaction,
832 final LogicalDatastoreType datastore,
833 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
834 final String point, final MapNode readList, final boolean before) {
835 rwTransaction.delete(datastore, path.getParent().getParent());
836 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
837 int lastItemPosition = 0;
838 for (final MapEntryNode mapEntryNode : readList.getValue()) {
839 if (mapEntryNode.getIdentifier()
840 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
848 int lastInsertedPosition = 0;
849 final NormalizedNode<?, ?> emptySubtree =
850 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
851 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
852 for (final MapEntryNode mapEntryNode : readList.getValue()) {
853 if (lastInsertedPosition == lastItemPosition) {
854 checkItemDoesNotExists(rwTransaction, datastore, path);
855 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
857 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
858 checkItemDoesNotExists(rwTransaction, datastore, childPath);
859 rwTransaction.put(datastore, childPath, mapEntryNode);
860 lastInsertedPosition++;
864 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
865 final YangInstanceIdentifier parent = path.getParent();
866 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
867 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
869 if (dataSchemaNode instanceof ListSchemaNode) {
870 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
871 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
873 return dataSchemaNode;
875 if (dataSchemaNode instanceof LeafListSchemaNode) {
876 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
877 throw new RestconfDocumentedException(
878 "Insert parameter can be used only with ordered-by user leaf-list.");
880 return dataSchemaNode;
882 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
885 private static void makeNormalPost(final DOMDataReadWriteTransaction rwTransaction,
886 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
887 final SchemaContext schemaContext) {
888 final Collection<? extends NormalizedNode<?, ?>> children;
889 if (payload instanceof MapNode) {
890 children = ((MapNode) payload).getValue();
891 } else if (payload instanceof LeafSetNode) {
892 children = ((LeafSetNode<?>) payload).getValue();
894 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
898 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
899 if (children.isEmpty()) {
900 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
901 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
905 // Kick off batch existence check first...
906 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
908 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
909 // result of the existence checks...
910 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
911 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
912 for (final NormalizedNode<?, ?> child : children) {
913 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
914 // as that would allow us to skip the existence checks
915 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
918 // ... finally collect existence checks and abort the transaction if any of them failed.
919 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
921 failure = check.getFailure();
922 } catch (InterruptedException e) {
923 rwTransaction.cancel();
924 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
927 if (failure != null) {
928 rwTransaction.cancel();
929 final ReadFailedException e = failure.getValue();
931 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
932 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
935 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
940 private static void simplePostPut(final DOMDataReadWriteTransaction rwTransaction,
941 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
942 final SchemaContext schemaContext) {
943 checkItemDoesNotExists(rwTransaction, datastore, path);
944 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
945 rwTransaction.put(datastore, path, payload);
948 private static boolean doesItemExist(final DOMDataReadWriteTransaction rwTransaction,
949 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
951 return rwTransaction.exists(store, path).checkedGet();
952 } catch (ReadFailedException e) {
953 rwTransaction.cancel();
954 throw new RestconfDocumentedException("Could not determine the existence of path " + path,
955 e, e.getErrorList());
960 * Check if item already exists. Throws error if it does NOT already exist.
961 * @param rwTransaction Current transaction
962 * @param store Used datastore
963 * @param path Path to item to verify its existence
965 private static void checkItemExists(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 does not exist";
969 LOG.trace("{}:{}", errMsg, path);
970 rwTransaction.cancel();
971 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
972 ErrorTag.DATA_MISSING);
977 * Check if item does NOT already exist. Throws error if it already exists.
978 * @param rwTransaction Current transaction
979 * @param store Used datastore
980 * @param path Path to item to verify its existence
982 private static void checkItemDoesNotExists(final DOMDataReadWriteTransaction rwTransaction,
983 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
984 if (doesItemExist(rwTransaction, store, path)) {
985 final String errMsg = "Operation via Restconf was not executed because data already exists";
986 LOG.trace("{}:{}", errMsg, path);
987 rwTransaction.cancel();
988 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
989 ErrorTag.DATA_EXISTS);
994 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1001 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
1002 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1003 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1004 final String insert, final String point) {
1005 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1006 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1007 return readWriteTransaction.submit();
1011 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1013 private void putDataWithinTransaction(
1014 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1015 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1016 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1017 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1020 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1021 private void putData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1022 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1023 final String insert, final String point) {
1024 if (insert == null) {
1025 makePut(rwTransaction, datastore, path, payload, schemaContext);
1029 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1030 checkItemDoesNotExists(rwTransaction, datastore, path);
1033 if (schemaNode instanceof ListSchemaNode) {
1034 final OrderedMapNode readList =
1035 (OrderedMapNode) this.readConfigurationData(path.getParent());
1036 if (readList == null || readList.getValue().isEmpty()) {
1037 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1039 rwTransaction.delete(datastore, path.getParent());
1040 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1041 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1044 final OrderedLeafSetNode<?> readLeafList =
1045 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1046 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1047 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1049 rwTransaction.delete(datastore, path.getParent());
1050 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1051 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1057 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1060 if (schemaNode instanceof ListSchemaNode) {
1061 final OrderedMapNode readList =
1062 (OrderedMapNode) this.readConfigurationData(path.getParent());
1063 if (readList == null || readList.getValue().isEmpty()) {
1064 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1066 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1070 final OrderedLeafSetNode<?> readLeafList =
1071 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1072 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1073 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1075 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1076 readLeafList, true);
1081 if (schemaNode instanceof ListSchemaNode) {
1082 final OrderedMapNode readList =
1083 (OrderedMapNode) this.readConfigurationData(path.getParent());
1084 if (readList == null || readList.getValue().isEmpty()) {
1085 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1087 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1091 final OrderedLeafSetNode<?> readLeafList =
1092 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1093 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1094 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1096 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1097 readLeafList, false);
1102 throw new RestconfDocumentedException(
1103 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1108 private void insertWithPointLeafListPut(final DOMDataWriteTransaction tx,
1109 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1110 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1111 final boolean before) {
1112 tx.delete(datastore, path.getParent());
1113 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1115 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1116 if (nodeChild.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 LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1129 if (index2 == index1) {
1130 simplePut(datastore, path, tx, schemaContext, payload);
1132 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1133 tx.put(datastore, childPath, nodeChild);
1138 private void insertWithPointListPut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1139 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1140 final String point, final OrderedMapNode readList, final boolean before) {
1141 tx.delete(datastore, path.getParent());
1142 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1144 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1145 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1154 final NormalizedNode<?, ?> emptySubtree =
1155 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1156 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1157 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1158 if (index2 == index1) {
1159 simplePut(datastore, path, tx, schemaContext, payload);
1161 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1162 tx.put(datastore, childPath, mapEntryNode);
1167 private static void makePut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1168 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1169 if (payload instanceof MapNode) {
1170 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1171 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1172 ensureParentsByMerge(datastore, path, tx, schemaContext);
1173 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1174 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1175 tx.put(datastore, childPath, child);
1178 simplePut(datastore, path, tx, schemaContext, payload);
1182 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1183 final DOMDataWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode<?, ?> payload) {
1184 ensureParentsByMerge(datastore, path, tx, schemaContext);
1185 tx.put(datastore, path, payload);
1188 private static CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
1189 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1190 final YangInstanceIdentifier path) {
1191 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1192 checkItemExists(readWriteTransaction, datastore, path);
1193 readWriteTransaction.delete(datastore, path);
1194 return readWriteTransaction.submit();
1197 private static void deleteDataWithinTransaction(final DOMDataWriteTransaction tx,
1198 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1199 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1200 tx.delete(datastore, path);
1203 private static void mergeDataWithinTransaction(final DOMDataWriteTransaction tx,
1204 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1205 final SchemaContext schemaContext) {
1206 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1207 ensureParentsByMerge(datastore, path, tx, schemaContext);
1209 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1210 // OpenDaylight should not change it.
1211 tx.merge(datastore, path, payload);
1214 public void setDomDataBroker(final DOMDataBroker domDataBroker) {
1215 this.domDataBroker = domDataBroker;
1218 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1219 checkPreconditions();
1221 if (listener.isListening()) {
1225 final SchemaPath path = listener.getSchemaPath();
1226 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1227 .registerNotificationListener(listener, path);
1229 listener.setRegistration(registration);
1232 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1233 final YangInstanceIdentifier normalizedPath, final DOMDataWriteTransaction tx,
1234 final SchemaContext schemaContext) {
1235 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1236 YangInstanceIdentifier rootNormalizedPath = null;
1238 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1240 while (it.hasNext()) {
1241 final PathArgument pathArgument = it.next();
1242 if (rootNormalizedPath == null) {
1243 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1247 normalizedPathWithoutChildArgs.add(pathArgument);
1251 if (normalizedPathWithoutChildArgs.isEmpty()) {
1255 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1257 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1258 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1259 tx.merge(store, rootNormalizedPath, parentStructure);
1262 private static final class PatchStatusContextHelper {
1263 PatchStatusContext status;
1265 public PatchStatusContext getStatus() {
1269 public void setStatus(final PatchStatusContext status) {
1270 this.status = status;