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;
100 private BrokerFacade() {}
102 public void setRpcService(final DOMRpcService router) {
103 this.rpcService = router;
106 public void setDomNotificationService(final DOMNotificationService domNotification) {
107 this.domNotification = domNotification;
110 public static BrokerFacade getInstance() {
111 return BrokerFacade.INSTANCE;
114 private void checkPreconditions() {
115 if (this.domDataBroker == null) {
116 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
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 checkPreconditions();
142 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
143 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
148 * Read config data from mount point by path.
151 * mount point for reading data
156 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
157 final YangInstanceIdentifier path) {
158 return readConfigurationData(mountPoint, path, null);
162 * Read config data from mount point by path.
165 * mount point for reading data
169 * value of with-defaults parameter
172 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
173 final String withDefa) {
174 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
175 if (domDataBrokerService.isPresent()) {
176 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
177 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
180 final String errMsg = "DOM data broker service isn't available for mount point " + path;
182 throw new RestconfDocumentedException(errMsg);
186 * Read operational data by path.
192 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
193 checkPreconditions();
195 try (DOMDataReadOnlyTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
196 return readDataViaTransaction(tx, OPERATIONAL, path);
201 * Read operational data from mount point by path.
204 * mount point for reading data
209 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
210 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
211 if (domDataBrokerService.isPresent()) {
212 try (DOMDataReadOnlyTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
213 return readDataViaTransaction(tx, OPERATIONAL, path);
216 final String errMsg = "DOM data broker service isn't available for mount point " + path;
218 throw new RestconfDocumentedException(errMsg);
222 * <b>PUT configuration data</b>
225 * Prepare result(status) for PUT operation and PUT data via transaction.
226 * Return wrapped status and future from PUT.
228 * @param globalSchema
229 * used by merge parents (if contains list)
238 * @return wrapper of status and future of PUT
240 public PutResult commitConfigurationDataPut(
241 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
242 final String insert, final String point) {
243 Preconditions.checkNotNull(globalSchema);
244 Preconditions.checkNotNull(path);
245 Preconditions.checkNotNull(payload);
247 checkPreconditions();
249 final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
250 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
252 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
253 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
254 return new PutResult(status, future);
258 * <b>PUT configuration data (Mount point)</b>
261 * Prepare result(status) for PUT operation and PUT data via transaction.
262 * Return wrapped status and future from PUT.
265 * mount point for getting transaction for operation and schema
266 * context for merging parents(if contains list)
275 * @return wrapper of status and future of PUT
277 public PutResult commitMountPointDataPut(
278 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
279 final String insert, final String point) {
280 Preconditions.checkNotNull(mountPoint);
281 Preconditions.checkNotNull(path);
282 Preconditions.checkNotNull(payload);
284 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
285 if (domDataBrokerService.isPresent()) {
286 final DOMDataReadWriteTransaction newReadWriteTransaction =
287 domDataBrokerService.get().newReadWriteTransaction();
288 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
289 ? Status.OK : Status.CREATED;
290 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
291 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
293 return new PutResult(status, future);
295 final String errMsg = "DOM data broker service isn't available for mount point " + path;
297 throw new RestconfDocumentedException(errMsg);
300 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
302 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
304 // get new transaction and schema context on server or on mounted device
305 final SchemaContext schemaContext;
306 final DOMDataReadWriteTransaction patchTransaction;
307 if (mountPoint == null) {
308 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
309 patchTransaction = this.domDataBroker.newReadWriteTransaction();
311 schemaContext = mountPoint.getSchemaContext();
313 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
315 if (optional.isPresent()) {
316 patchTransaction = optional.get().newReadWriteTransaction();
318 // if mount point does not have broker it is not possible to continue and global error is reported
319 LOG.error("Http Patch {} has failed - device {} does not support broker service",
320 patchContext.getPatchId(), mountPoint.getIdentifier());
321 return new PatchStatusContext(
322 patchContext.getPatchId(),
325 ImmutableList.of(new RestconfError(
326 ErrorType.APPLICATION,
327 ErrorTag.OPERATION_FAILED,
328 "DOM data broker service isn't available for mount point "
329 + mountPoint.getIdentifier()))
334 final List<PatchStatusEntity> editCollection = new ArrayList<>();
335 List<RestconfError> editErrors;
336 boolean withoutError = true;
338 for (final PatchEntity patchEntity : patchContext.getData()) {
339 final PatchEditOperation operation = patchEntity.getOperation();
344 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
345 patchEntity.getNode(), schemaContext);
346 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
347 } catch (final RestconfDocumentedException e) {
348 LOG.error("Error call http Patch operation {} on target {}",
350 patchEntity.getTargetNode().toString());
352 editErrors = new ArrayList<>();
353 editErrors.addAll(e.getErrors());
354 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
355 withoutError = false;
362 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
363 .getTargetNode(), patchEntity.getNode(), schemaContext);
364 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
365 } catch (final RestconfDocumentedException e) {
366 LOG.error("Error call http Patch operation {} on target {}",
368 patchEntity.getTargetNode().toString());
370 editErrors = new ArrayList<>();
371 editErrors.addAll(e.getErrors());
372 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
373 withoutError = false;
380 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
382 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
383 } catch (final RestconfDocumentedException e) {
384 LOG.error("Error call http Patch operation {} on target {}",
386 patchEntity.getTargetNode().toString());
388 editErrors = new ArrayList<>();
389 editErrors.addAll(e.getErrors());
390 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
391 withoutError = false;
398 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
400 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
401 } catch (final RestconfDocumentedException e) {
402 LOG.error("Error call http Patch operation {} on target {}",
404 patchEntity.getTargetNode().toString());
406 editErrors = new ArrayList<>();
407 editErrors.addAll(e.getErrors());
408 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
409 withoutError = false;
416 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
417 patchEntity.getNode(), schemaContext);
418 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
419 } catch (final RestconfDocumentedException e) {
420 LOG.error("Error call http Patch operation {} on target {}",
422 patchEntity.getTargetNode().toString());
424 editErrors = new ArrayList<>();
425 editErrors.addAll(e.getErrors());
426 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
427 withoutError = false;
432 LOG.error("Unsupported http Patch operation {} on target {}",
434 patchEntity.getTargetNode().toString());
439 // if errors then cancel transaction and return error status
441 patchTransaction.cancel();
442 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
445 // if no errors commit transaction
446 final CountDownLatch waiter = new CountDownLatch(1);
447 final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
448 final PatchStatusContextHelper status = new PatchStatusContextHelper();
450 Futures.addCallback(future, new FutureCallback<Void>() {
452 public void onSuccess(@Nullable final Void result) {
453 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
459 public void onFailure(final Throwable throwable) {
460 // if commit failed it is global error
461 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
462 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
463 false, ImmutableList.of(
464 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
467 }, MoreExecutors.directExecutor());
470 return status.getStatus();
473 // POST configuration
474 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
475 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
476 final String insert, final String point) {
477 checkPreconditions();
478 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
479 globalSchema, insert, point);
482 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
483 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
484 final String insert, final String point) {
485 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
486 if (domDataBrokerService.isPresent()) {
487 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
488 payload, mountPoint.getSchemaContext(), insert, point);
490 final String errMsg = "DOM data broker service isn't available for mount point " + path;
492 throw new RestconfDocumentedException(errMsg);
495 // DELETE configuration
496 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
497 final YangInstanceIdentifier path) {
498 checkPreconditions();
499 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
502 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
503 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
504 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
505 if (domDataBrokerService.isPresent()) {
506 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
508 final String errMsg = "DOM data broker service isn't available for mount point " + path;
510 throw new RestconfDocumentedException(errMsg);
514 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type,
515 final NormalizedNode<?, ?> input) {
516 checkPreconditions();
517 if (this.rpcService == null) {
518 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
520 LOG.trace("Invoke RPC {} with input: {}", type, input);
521 return this.rpcService.invokeRpc(type, input);
524 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
525 final ListenerAdapter listener) {
526 checkPreconditions();
528 if (listener.isListening()) {
532 final YangInstanceIdentifier path = listener.getPath();
533 DOMDataTreeChangeService changeService = (DOMDataTreeChangeService)
534 this.domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
535 if (changeService == null) {
536 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
537 + this.domDataBroker);
539 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
540 ListenerRegistration<ListenerAdapter> registration =
541 changeService.registerDataTreeChangeListener(root, listener);
542 listener.setRegistration(registration);
545 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
546 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
547 return readDataViaTransaction(transaction, datastore, path, null);
550 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
551 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
552 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
555 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).checkedGet();
556 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
557 prepareDataByParamWithDef(optional.get(), path, withDefa);
558 } catch (ReadFailedException e) {
559 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
560 for (final RpcError error : e.getErrorList()) {
561 if (error.getErrorType() == RpcError.ErrorType.TRANSPORT
562 && error.getTag().equals(ErrorTag.RESOURCE_DENIED.getTagValue())) {
563 throw new RestconfDocumentedException(
566 ErrorTag.RESOURCE_DENIED_TRANSPORT);
569 throw new RestconfDocumentedException("Error reading data.", e, e.getErrorList());
573 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
574 final YangInstanceIdentifier path, final String withDefa) {
584 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
587 final SchemaContext ctx = ControllerContext.getInstance().getGlobalSchema();
588 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
589 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
590 if (result instanceof ContainerNode) {
591 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
592 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
593 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
594 return builder.build();
597 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
598 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
599 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
600 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
601 return builder.build();
604 private void buildMapEntryBuilder(
605 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
606 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
607 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
608 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
609 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
610 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
611 if (child instanceof ContainerNode) {
612 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
613 Builders.containerBuilder((ContainerSchemaNode) childSchema);
614 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
615 builder.withChild(childBuilder.build());
616 } else if (child instanceof MapNode) {
617 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
618 Builders.mapBuilder((ListSchemaNode) childSchema);
619 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
620 ((ListSchemaNode) childSchema).getKeyDefinition());
621 builder.withChild(childBuilder.build());
622 } else if (child instanceof LeafNode) {
623 final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
624 final String nodeVal = ((LeafNode<String>) child).getValue();
625 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
626 Builders.leafBuilder((LeafSchemaNode) childSchema);
627 if (keys.contains(child.getNodeType())) {
628 leafBuilder.withValue(((LeafNode<?>) child).getValue());
629 builder.withChild(leafBuilder.build());
632 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
633 leafBuilder.withValue(((LeafNode<?>) child).getValue());
634 builder.withChild(leafBuilder.build());
637 if (defaultVal != null && defaultVal.equals(nodeVal)) {
638 leafBuilder.withValue(((LeafNode<?>) child).getValue());
639 builder.withChild(leafBuilder.build());
647 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
648 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
649 final List<QName> keys) {
650 for (final MapEntryNode mapEntryNode : result.getValue()) {
651 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
652 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
653 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
654 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
655 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
656 builder.withChild(mapEntryBuilder.build());
660 private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
661 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
662 final YangInstanceIdentifier actualPath, final boolean trim) {
663 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
664 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
665 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
666 if (child instanceof ContainerNode) {
667 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
668 Builders.containerBuilder((ContainerSchemaNode) childSchema);
669 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
670 builder.withChild(builderChild.build());
671 } else if (child instanceof MapNode) {
672 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
673 Builders.mapBuilder((ListSchemaNode) childSchema);
674 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
675 ((ListSchemaNode) childSchema).getKeyDefinition());
676 builder.withChild(childBuilder.build());
677 } else if (child instanceof LeafNode) {
678 final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
679 final String nodeVal = ((LeafNode<String>) child).getValue();
680 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
681 Builders.leafBuilder((LeafSchemaNode) childSchema);
683 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
684 leafBuilder.withValue(((LeafNode<?>) child).getValue());
685 builder.withChild(leafBuilder.build());
688 if (defaultVal != null && defaultVal.equals(nodeVal)) {
689 leafBuilder.withValue(((LeafNode<?>) child).getValue());
690 builder.withChild(leafBuilder.build());
698 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
700 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
701 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
702 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
703 final String insert, final String point) {
704 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
705 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
706 return rwTransaction.submit();
710 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
712 private void postDataWithinTransaction(
713 final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
714 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
715 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
716 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
719 private void postData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
720 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
721 final SchemaContext schemaContext, final String insert, final String point) {
722 if (insert == null) {
723 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
727 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
728 checkItemDoesNotExists(rwTransaction, datastore, path);
731 if (schemaNode instanceof ListSchemaNode) {
732 final OrderedMapNode readList =
733 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
734 if (readList == null || readList.getValue().isEmpty()) {
735 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
737 rwTransaction.delete(datastore, path.getParent().getParent());
738 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
739 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
743 final OrderedLeafSetNode<?> readLeafList =
744 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
745 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
746 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
748 rwTransaction.delete(datastore, path.getParent());
749 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
750 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
756 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
759 if (schemaNode instanceof ListSchemaNode) {
760 final OrderedMapNode readList =
761 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
762 if (readList == null || readList.getValue().isEmpty()) {
763 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
765 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
770 final OrderedLeafSetNode<?> readLeafList =
771 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
772 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
773 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
775 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
781 if (schemaNode instanceof ListSchemaNode) {
782 final OrderedMapNode readList =
783 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
784 if (readList == null || readList.getValue().isEmpty()) {
785 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
787 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
792 final OrderedLeafSetNode<?> readLeafList =
793 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
794 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
795 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
797 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
798 readLeafList, false);
803 throw new RestconfDocumentedException(
804 "Used bad value of insert parameter. Possible values are first, last, before or after, "
805 + "but was: " + insert);
809 private static void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rwTransaction,
810 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
811 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
812 final boolean before) {
813 rwTransaction.delete(datastore, path.getParent().getParent());
814 final InstanceIdentifierContext<?> instanceIdentifier =
815 ControllerContext.getInstance().toInstanceIdentifier(point);
816 int lastItemPosition = 0;
817 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
818 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
826 int lastInsertedPosition = 0;
827 final NormalizedNode<?, ?> emptySubtree =
828 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
829 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
830 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
831 if (lastInsertedPosition == lastItemPosition) {
832 checkItemDoesNotExists(rwTransaction, datastore, path);
833 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
835 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
836 checkItemDoesNotExists(rwTransaction, datastore, childPath);
837 rwTransaction.put(datastore, childPath, nodeChild);
838 lastInsertedPosition++;
842 private static void insertWithPointListPost(final DOMDataReadWriteTransaction rwTransaction,
843 final LogicalDatastoreType datastore,
844 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
845 final String point, final MapNode readList, final boolean before) {
846 rwTransaction.delete(datastore, path.getParent().getParent());
847 final InstanceIdentifierContext<?> instanceIdentifier =
848 ControllerContext.getInstance().toInstanceIdentifier(point);
849 int lastItemPosition = 0;
850 for (final MapEntryNode mapEntryNode : readList.getValue()) {
851 if (mapEntryNode.getIdentifier()
852 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
860 int lastInsertedPosition = 0;
861 final NormalizedNode<?, ?> emptySubtree =
862 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
863 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
864 for (final MapEntryNode mapEntryNode : readList.getValue()) {
865 if (lastInsertedPosition == lastItemPosition) {
866 checkItemDoesNotExists(rwTransaction, datastore, path);
867 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
869 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
870 checkItemDoesNotExists(rwTransaction, datastore, childPath);
871 rwTransaction.put(datastore, childPath, mapEntryNode);
872 lastInsertedPosition++;
876 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
877 final YangInstanceIdentifier parent = path.getParent();
878 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
879 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
881 if (dataSchemaNode instanceof ListSchemaNode) {
882 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
883 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
885 return dataSchemaNode;
887 if (dataSchemaNode instanceof LeafListSchemaNode) {
888 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
889 throw new RestconfDocumentedException(
890 "Insert parameter can be used only with ordered-by user leaf-list.");
892 return dataSchemaNode;
894 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
897 private static void makeNormalPost(final DOMDataReadWriteTransaction rwTransaction,
898 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
899 final SchemaContext schemaContext) {
900 final Collection<? extends NormalizedNode<?, ?>> children;
901 if (payload instanceof MapNode) {
902 children = ((MapNode) payload).getValue();
903 } else if (payload instanceof LeafSetNode) {
904 children = ((LeafSetNode<?>) payload).getValue();
906 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
910 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
911 if (children.isEmpty()) {
912 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
913 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
917 // Kick off batch existence check first...
918 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
920 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
921 // result of the existence checks...
922 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
923 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
924 for (final NormalizedNode<?, ?> child : children) {
925 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
926 // as that would allow us to skip the existence checks
927 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
930 // ... finally collect existence checks and abort the transaction if any of them failed.
931 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
933 failure = check.getFailure();
934 } catch (InterruptedException e) {
935 rwTransaction.cancel();
936 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
939 if (failure != null) {
940 rwTransaction.cancel();
941 final ReadFailedException e = failure.getValue();
943 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
944 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
947 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
952 private static void simplePostPut(final DOMDataReadWriteTransaction rwTransaction,
953 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
954 final SchemaContext schemaContext) {
955 checkItemDoesNotExists(rwTransaction, datastore, path);
956 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
957 rwTransaction.put(datastore, path, payload);
960 private static boolean doesItemExist(final DOMDataReadWriteTransaction rwTransaction,
961 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
963 return rwTransaction.exists(store, path).checkedGet();
964 } catch (ReadFailedException e) {
965 rwTransaction.cancel();
966 throw new RestconfDocumentedException("Could not determine the existence of path " + path,
967 e, e.getErrorList());
972 * Check if item already exists. Throws error if it does NOT already exist.
973 * @param rwTransaction Current transaction
974 * @param store Used datastore
975 * @param path Path to item to verify its existence
977 private static void checkItemExists(final DOMDataReadWriteTransaction rwTransaction,
978 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
979 if (!doesItemExist(rwTransaction, store, path)) {
980 final String errMsg = "Operation via Restconf was not executed because data does not exist";
981 LOG.trace("{}:{}", errMsg, path);
982 rwTransaction.cancel();
983 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
984 ErrorTag.DATA_MISSING);
989 * Check if item does NOT already exist. Throws error if it already exists.
990 * @param rwTransaction Current transaction
991 * @param store Used datastore
992 * @param path Path to item to verify its existence
994 private static void checkItemDoesNotExists(final DOMDataReadWriteTransaction rwTransaction,
995 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
996 if (doesItemExist(rwTransaction, store, path)) {
997 final String errMsg = "Operation via Restconf was not executed because data already exists";
998 LOG.trace("{}:{}", errMsg, path);
999 rwTransaction.cancel();
1000 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
1001 ErrorTag.DATA_EXISTS);
1006 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1013 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
1014 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1015 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1016 final String insert, final String point) {
1017 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1018 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1019 return readWriteTransaction.submit();
1023 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1025 private void putDataWithinTransaction(
1026 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1027 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1028 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1029 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1032 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1033 private void putData(final DOMDataReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1034 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1035 final String insert, final String point) {
1036 if (insert == null) {
1037 makePut(rwTransaction, datastore, path, payload, schemaContext);
1041 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1042 checkItemDoesNotExists(rwTransaction, datastore, path);
1045 if (schemaNode instanceof ListSchemaNode) {
1046 final OrderedMapNode readList =
1047 (OrderedMapNode) this.readConfigurationData(path.getParent());
1048 if (readList == null || readList.getValue().isEmpty()) {
1049 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1051 rwTransaction.delete(datastore, path.getParent());
1052 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1053 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1056 final OrderedLeafSetNode<?> readLeafList =
1057 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1058 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1059 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1061 rwTransaction.delete(datastore, path.getParent());
1062 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1063 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1069 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1072 if (schemaNode instanceof ListSchemaNode) {
1073 final OrderedMapNode readList =
1074 (OrderedMapNode) this.readConfigurationData(path.getParent());
1075 if (readList == null || readList.getValue().isEmpty()) {
1076 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1078 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1082 final OrderedLeafSetNode<?> readLeafList =
1083 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1084 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1085 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1087 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1088 readLeafList, true);
1093 if (schemaNode instanceof ListSchemaNode) {
1094 final OrderedMapNode readList =
1095 (OrderedMapNode) this.readConfigurationData(path.getParent());
1096 if (readList == null || readList.getValue().isEmpty()) {
1097 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1099 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1103 final OrderedLeafSetNode<?> readLeafList =
1104 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1105 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1106 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1108 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1109 readLeafList, false);
1114 throw new RestconfDocumentedException(
1115 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1120 private static void insertWithPointLeafListPut(final DOMDataWriteTransaction tx,
1121 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1122 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1123 final boolean before) {
1124 tx.delete(datastore, path.getParent());
1125 final InstanceIdentifierContext<?> instanceIdentifier =
1126 ControllerContext.getInstance().toInstanceIdentifier(point);
1128 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1129 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1138 final NormalizedNode<?, ?> emptySubtree =
1139 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1140 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1141 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1142 if (index2 == index1) {
1143 simplePut(datastore, path, tx, schemaContext, payload);
1145 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1146 tx.put(datastore, childPath, nodeChild);
1151 private static void insertWithPointListPut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1152 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1153 final String point, final OrderedMapNode readList, final boolean before) {
1154 tx.delete(datastore, path.getParent());
1155 final InstanceIdentifierContext<?> instanceIdentifier =
1156 ControllerContext.getInstance().toInstanceIdentifier(point);
1158 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1159 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1168 final NormalizedNode<?, ?> emptySubtree =
1169 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1170 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1171 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1172 if (index2 == index1) {
1173 simplePut(datastore, path, tx, schemaContext, payload);
1175 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1176 tx.put(datastore, childPath, mapEntryNode);
1181 private static void makePut(final DOMDataWriteTransaction tx, final LogicalDatastoreType datastore,
1182 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1183 if (payload instanceof MapNode) {
1184 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1185 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1186 ensureParentsByMerge(datastore, path, tx, schemaContext);
1187 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1188 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1189 tx.put(datastore, childPath, child);
1192 simplePut(datastore, path, tx, schemaContext, payload);
1196 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1197 final DOMDataWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode<?, ?> payload) {
1198 ensureParentsByMerge(datastore, path, tx, schemaContext);
1199 tx.put(datastore, path, payload);
1202 private static CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
1203 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1204 final YangInstanceIdentifier path) {
1205 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1206 checkItemExists(readWriteTransaction, datastore, path);
1207 readWriteTransaction.delete(datastore, path);
1208 return readWriteTransaction.submit();
1211 private static void deleteDataWithinTransaction(final DOMDataWriteTransaction tx,
1212 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1213 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1214 tx.delete(datastore, path);
1217 private static void mergeDataWithinTransaction(final DOMDataWriteTransaction tx,
1218 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1219 final SchemaContext schemaContext) {
1220 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1221 ensureParentsByMerge(datastore, path, tx, schemaContext);
1223 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1224 // OpenDaylight should not change it.
1225 tx.merge(datastore, path, payload);
1228 public void setDomDataBroker(final DOMDataBroker domDataBroker) {
1229 this.domDataBroker = domDataBroker;
1232 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1233 checkPreconditions();
1235 if (listener.isListening()) {
1239 final SchemaPath path = listener.getSchemaPath();
1240 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1241 .registerNotificationListener(listener, path);
1243 listener.setRegistration(registration);
1246 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1247 final YangInstanceIdentifier normalizedPath, final DOMDataWriteTransaction tx,
1248 final SchemaContext schemaContext) {
1249 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1250 YangInstanceIdentifier rootNormalizedPath = null;
1252 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1254 while (it.hasNext()) {
1255 final PathArgument pathArgument = it.next();
1256 if (rootNormalizedPath == null) {
1257 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1261 normalizedPathWithoutChildArgs.add(pathArgument);
1265 if (normalizedPathWithoutChildArgs.isEmpty()) {
1269 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1271 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1272 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1273 tx.merge(store, rootNormalizedPath, parentStructure);
1276 private static final class PatchStatusContextHelper {
1277 PatchStatusContext status;
1279 public PatchStatusContext getStatus() {
1283 public void setStatus(final PatchStatusContext status) {
1284 this.status = status;