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.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Throwables;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.util.concurrent.FluentFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import com.google.common.util.concurrent.MoreExecutors;
20 import java.io.Closeable;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map.Entry;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutionException;
30 import javax.annotation.PreDestroy;
31 import javax.inject.Inject;
32 import javax.inject.Singleton;
33 import javax.ws.rs.core.Response.Status;
34 import org.apache.aries.blueprint.annotation.service.Reference;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.opendaylight.mdsal.common.api.CommitInfo;
37 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
38 import org.opendaylight.mdsal.common.api.ReadFailedException;
39 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
45 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
46 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
47 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
48 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
49 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
50 import org.opendaylight.mdsal.dom.api.DOMRpcService;
51 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
52 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
53 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
54 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
55 import org.opendaylight.restconf.common.errors.RestconfError;
56 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
57 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
58 import org.opendaylight.restconf.common.patch.PatchContext;
59 import org.opendaylight.restconf.common.patch.PatchEditOperation;
60 import org.opendaylight.restconf.common.patch.PatchEntity;
61 import org.opendaylight.restconf.common.patch.PatchStatusContext;
62 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
63 import org.opendaylight.restconf.common.util.DataChangeScope;
64 import org.opendaylight.yangtools.concepts.ListenerRegistration;
65 import org.opendaylight.yangtools.yang.common.QName;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
70 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
72 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
80 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
81 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
82 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
83 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
85 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
86 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
87 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
88 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
89 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
91 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
92 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
93 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
98 @SuppressWarnings("checkstyle:FinalClass")
99 public class BrokerFacade implements Closeable {
100 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
102 private volatile DOMRpcService rpcService;
104 private final DOMDataBroker domDataBroker;
105 private final DOMNotificationService domNotification;
106 private final ControllerContext controllerContext;
109 public BrokerFacade(final @Reference DOMRpcService rpcService, final DOMDataBroker domDataBroker,
110 final @Reference DOMNotificationService domNotification, final ControllerContext controllerContext) {
111 this.rpcService = Objects.requireNonNull(rpcService);
112 this.domDataBroker = Objects.requireNonNull(domDataBroker);
113 this.domNotification = Objects.requireNonNull(domNotification);
114 this.controllerContext = Objects.requireNonNull(controllerContext);
120 * @deprecated Just use
121 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
122 * constructor instead.
125 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
126 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
127 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
132 public void close() {
136 * Read config data by path.
142 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
143 return readConfigurationData(path, null);
147 * Read config data by path.
152 * value of with-defaults parameter
155 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
156 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
157 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
162 * Read config data from mount point by path.
165 * mount point for reading data
170 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
171 final YangInstanceIdentifier path) {
172 return readConfigurationData(mountPoint, path, null);
176 * Read config data from mount point by path.
179 * mount point for reading data
183 * value of with-defaults parameter
186 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
187 final String withDefa) {
188 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
189 if (domDataBrokerService.isPresent()) {
190 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
191 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
194 throw dataBrokerUnavailable(path);
198 * Read operational data by path.
204 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
205 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
206 return readDataViaTransaction(tx, OPERATIONAL, path);
211 * Read operational data from mount point by path.
214 * mount point for reading data
219 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
220 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
221 if (domDataBrokerService.isPresent()) {
222 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
223 return readDataViaTransaction(tx, OPERATIONAL, path);
226 throw dataBrokerUnavailable(path);
230 * <b>PUT configuration data</b>
233 * Prepare result(status) for PUT operation and PUT data via transaction.
234 * Return wrapped status and future from PUT.
236 * @param globalSchema
237 * used by merge parents (if contains list)
246 * @return wrapper of status and future of PUT
248 public PutResult commitConfigurationDataPut(
249 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
250 final String insert, final String point) {
251 Preconditions.checkNotNull(globalSchema);
252 Preconditions.checkNotNull(path);
253 Preconditions.checkNotNull(payload);
255 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
256 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
258 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
259 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
260 return new PutResult(status, future);
264 * <b>PUT configuration data (Mount point)</b>
267 * Prepare result(status) for PUT operation and PUT data via transaction.
268 * Return wrapped status and future from PUT.
271 * mount point for getting transaction for operation and schema
272 * context for merging parents(if contains list)
281 * @return wrapper of status and future of PUT
283 public PutResult commitMountPointDataPut(
284 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
285 final String insert, final String point) {
286 Preconditions.checkNotNull(mountPoint);
287 Preconditions.checkNotNull(path);
288 Preconditions.checkNotNull(payload);
290 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
291 if (domDataBrokerService.isPresent()) {
292 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
293 domDataBrokerService.get().newReadWriteTransaction();
294 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
295 ? Status.OK : Status.CREATED;
296 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
297 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
299 return new PutResult(status, future);
301 throw dataBrokerUnavailable(path);
304 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
306 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
308 // get new transaction and schema context on server or on mounted device
309 final SchemaContext schemaContext;
310 final DOMDataTreeReadWriteTransaction patchTransaction;
311 if (mountPoint == null) {
312 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
313 patchTransaction = this.domDataBroker.newReadWriteTransaction();
315 schemaContext = mountPoint.getSchemaContext();
317 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
319 if (optional.isPresent()) {
320 patchTransaction = optional.get().newReadWriteTransaction();
322 // if mount point does not have broker it is not possible to continue and global error is reported
323 LOG.error("Http Patch {} has failed - device {} does not support broker service",
324 patchContext.getPatchId(), mountPoint.getIdentifier());
325 return new PatchStatusContext(
326 patchContext.getPatchId(),
329 ImmutableList.of(new RestconfError(
330 ErrorType.APPLICATION,
331 ErrorTag.OPERATION_FAILED,
332 "DOM data broker service isn't available for mount point "
333 + mountPoint.getIdentifier()))
338 final List<PatchStatusEntity> editCollection = new ArrayList<>();
339 List<RestconfError> editErrors;
340 boolean withoutError = true;
342 for (final PatchEntity patchEntity : patchContext.getData()) {
343 final PatchEditOperation operation = patchEntity.getOperation();
348 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
349 patchEntity.getNode(), schemaContext);
350 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
351 } catch (final RestconfDocumentedException e) {
352 LOG.error("Error call http Patch operation {} on target {}",
354 patchEntity.getTargetNode().toString());
356 editErrors = new ArrayList<>();
357 editErrors.addAll(e.getErrors());
358 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
359 withoutError = false;
366 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
367 .getTargetNode(), patchEntity.getNode(), schemaContext);
368 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
369 } catch (final RestconfDocumentedException e) {
370 LOG.error("Error call http Patch operation {} on target {}",
372 patchEntity.getTargetNode().toString());
374 editErrors = new ArrayList<>();
375 editErrors.addAll(e.getErrors());
376 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
377 withoutError = false;
385 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
387 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
388 } catch (final RestconfDocumentedException e) {
389 LOG.error("Error call http Patch operation {} on target {}",
391 patchEntity.getTargetNode().toString());
393 editErrors = new ArrayList<>();
394 editErrors.addAll(e.getErrors());
395 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
396 withoutError = false;
403 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
404 patchEntity.getNode(), schemaContext);
405 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
406 } catch (final RestconfDocumentedException e) {
407 LOG.error("Error call http Patch operation {} on target {}",
409 patchEntity.getTargetNode().toString());
411 editErrors = new ArrayList<>();
412 editErrors.addAll(e.getErrors());
413 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
414 withoutError = false;
419 LOG.error("Unsupported http Patch operation {} on target {}",
421 patchEntity.getTargetNode().toString());
426 // if errors then cancel transaction and return error status
428 patchTransaction.cancel();
429 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
432 // if no errors commit transaction
433 final CountDownLatch waiter = new CountDownLatch(1);
434 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
435 final PatchStatusContextHelper status = new PatchStatusContextHelper();
437 future.addCallback(new FutureCallback<CommitInfo>() {
439 public void onSuccess(final CommitInfo result) {
440 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
446 public void onFailure(final Throwable throwable) {
447 // if commit failed it is global error
448 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
449 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
450 false, ImmutableList.of(
451 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
454 }, MoreExecutors.directExecutor());
457 return status.getStatus();
460 // POST configuration
461 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
462 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
463 final String insert, final String point) {
464 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
465 globalSchema, insert, point);
468 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
469 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
470 final String insert, final String point) {
471 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
472 if (domDataBrokerService.isPresent()) {
473 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
474 payload, mountPoint.getSchemaContext(), insert, point);
476 throw dataBrokerUnavailable(path);
479 // DELETE configuration
480 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
481 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
484 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
485 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
486 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
487 if (domDataBrokerService.isPresent()) {
488 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
490 throw dataBrokerUnavailable(path);
494 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull SchemaPath type,
495 final @NonNull NormalizedNode<?, ?> input) {
496 if (this.rpcService == null) {
497 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
499 LOG.trace("Invoke RPC {} with input: {}", type, input);
500 return this.rpcService.invokeRpc(type, input);
503 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
504 final ListenerAdapter listener) {
505 if (listener.isListening()) {
509 final YangInstanceIdentifier path = listener.getPath();
510 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
511 .getInstance(DOMDataTreeChangeService.class);
512 if (changeService == null) {
513 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
514 + this.domDataBroker);
516 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
517 ListenerRegistration<ListenerAdapter> registration =
518 changeService.registerDataTreeChangeListener(root, listener);
519 listener.setRegistration(registration);
522 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadOperations transaction,
523 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
524 return readDataViaTransaction(transaction, datastore, path, null);
527 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadOperations transaction,
528 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
529 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
532 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).get();
533 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
534 prepareDataByParamWithDef(optional.get(), path, withDefa);
535 } catch (InterruptedException e) {
536 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
537 throw new RestconfDocumentedException("Error reading data.", e);
538 } catch (ExecutionException e) {
539 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
540 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
541 ReadFailedException.class));
545 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
546 final YangInstanceIdentifier path, final String withDefa) {
556 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
559 final SchemaContext ctx = controllerContext.getGlobalSchema();
560 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
561 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
562 if (result instanceof ContainerNode) {
563 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
564 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
565 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
566 return builder.build();
569 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
570 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
571 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
572 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
573 return builder.build();
576 private void buildMapEntryBuilder(
577 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
578 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
579 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
580 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
581 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
582 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
583 if (child instanceof ContainerNode) {
584 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
585 Builders.containerBuilder((ContainerSchemaNode) childSchema);
586 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
587 builder.withChild(childBuilder.build());
588 } else if (child instanceof MapNode) {
589 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
590 Builders.mapBuilder((ListSchemaNode) childSchema);
591 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
592 ((ListSchemaNode) childSchema).getKeyDefinition());
593 builder.withChild(childBuilder.build());
594 } else if (child instanceof LeafNode) {
595 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
596 final Object nodeVal = child.getValue();
597 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
598 Builders.leafBuilder((LeafSchemaNode) childSchema);
599 if (keys.contains(child.getNodeType())) {
600 leafBuilder.withValue(child.getValue());
601 builder.withChild(leafBuilder.build());
604 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
605 leafBuilder.withValue(child.getValue());
606 builder.withChild(leafBuilder.build());
609 if (defaultVal != null && defaultVal.equals(nodeVal)) {
610 leafBuilder.withValue(child.getValue());
611 builder.withChild(leafBuilder.build());
619 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
620 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
621 final List<QName> keys) {
622 for (final MapEntryNode mapEntryNode : result.getValue()) {
623 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
624 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
625 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
626 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
627 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
628 builder.withChild(mapEntryBuilder.build());
632 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
633 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
634 final YangInstanceIdentifier actualPath, final boolean trim) {
635 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
636 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
637 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
638 if (child instanceof ContainerNode) {
639 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
640 Builders.containerBuilder((ContainerSchemaNode) childSchema);
641 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
642 builder.withChild(builderChild.build());
643 } else if (child instanceof MapNode) {
644 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
645 Builders.mapBuilder((ListSchemaNode) childSchema);
646 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
647 ((ListSchemaNode) childSchema).getKeyDefinition());
648 builder.withChild(childBuilder.build());
649 } else if (child instanceof LeafNode) {
650 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
651 final Object nodeVal = child.getValue();
652 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
653 Builders.leafBuilder((LeafSchemaNode) childSchema);
655 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
656 leafBuilder.withValue(child.getValue());
657 builder.withChild(leafBuilder.build());
660 if (defaultVal != null && defaultVal.equals(nodeVal)) {
661 leafBuilder.withValue(child.getValue());
662 builder.withChild(leafBuilder.build());
670 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
672 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
673 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
674 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
675 final String insert, final String point) {
676 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
677 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
678 return rwTransaction.commit();
682 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
684 private void postDataWithinTransaction(
685 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
686 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
687 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
688 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
691 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
692 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
693 final SchemaContext schemaContext, final String insert, final String point) {
694 if (insert == null) {
695 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
699 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
700 checkItemDoesNotExists(rwTransaction, datastore, path);
703 if (schemaNode instanceof ListSchemaNode) {
704 final OrderedMapNode readList =
705 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
706 if (readList == null || readList.getValue().isEmpty()) {
707 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
709 rwTransaction.delete(datastore, path.getParent().getParent());
710 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
711 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
715 final OrderedLeafSetNode<?> readLeafList =
716 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
717 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
718 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
720 rwTransaction.delete(datastore, path.getParent());
721 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
722 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
728 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
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 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
742 final OrderedLeafSetNode<?> readLeafList =
743 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
744 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
745 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
747 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
753 if (schemaNode instanceof ListSchemaNode) {
754 final OrderedMapNode readList =
755 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
756 if (readList == null || readList.getValue().isEmpty()) {
757 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
759 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
764 final OrderedLeafSetNode<?> readLeafList =
765 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
766 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
767 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
769 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
770 readLeafList, false);
775 throw new RestconfDocumentedException(
776 "Used bad value of insert parameter. Possible values are first, last, before or after, "
777 + "but was: " + insert);
781 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
782 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
783 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
784 final boolean before) {
785 rwTransaction.delete(datastore, path.getParent().getParent());
786 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
787 int lastItemPosition = 0;
788 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
789 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
797 int lastInsertedPosition = 0;
798 final NormalizedNode<?, ?> emptySubtree =
799 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
800 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
801 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
802 if (lastInsertedPosition == lastItemPosition) {
803 checkItemDoesNotExists(rwTransaction, datastore, path);
804 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
806 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
807 checkItemDoesNotExists(rwTransaction, datastore, childPath);
808 rwTransaction.put(datastore, childPath, nodeChild);
809 lastInsertedPosition++;
813 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
814 final LogicalDatastoreType datastore,
815 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
816 final String point, final MapNode readList, final boolean before) {
817 rwTransaction.delete(datastore, path.getParent().getParent());
818 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
819 int lastItemPosition = 0;
820 for (final MapEntryNode mapEntryNode : readList.getValue()) {
821 if (mapEntryNode.getIdentifier()
822 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
830 int lastInsertedPosition = 0;
831 final NormalizedNode<?, ?> emptySubtree =
832 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
833 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
834 for (final MapEntryNode mapEntryNode : readList.getValue()) {
835 if (lastInsertedPosition == lastItemPosition) {
836 checkItemDoesNotExists(rwTransaction, datastore, path);
837 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
839 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
840 checkItemDoesNotExists(rwTransaction, datastore, childPath);
841 rwTransaction.put(datastore, childPath, mapEntryNode);
842 lastInsertedPosition++;
846 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
847 final YangInstanceIdentifier parent = path.getParent();
848 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
849 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
851 if (dataSchemaNode instanceof ListSchemaNode) {
852 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
853 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
855 return dataSchemaNode;
857 if (dataSchemaNode instanceof LeafListSchemaNode) {
858 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
859 throw new RestconfDocumentedException(
860 "Insert parameter can be used only with ordered-by user leaf-list.");
862 return dataSchemaNode;
864 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
867 private static void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
868 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
869 final SchemaContext schemaContext) {
870 final Collection<? extends NormalizedNode<?, ?>> children;
871 if (payload instanceof MapNode) {
872 children = ((MapNode) payload).getValue();
873 } else if (payload instanceof LeafSetNode) {
874 children = ((LeafSetNode<?>) payload).getValue();
876 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
880 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
881 if (children.isEmpty()) {
882 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
883 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
887 // Kick off batch existence check first...
888 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
890 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
891 // result of the existence checks...
892 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
893 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
894 for (final NormalizedNode<?, ?> child : children) {
895 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
896 // as that would allow us to skip the existence checks
897 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
900 // ... finally collect existence checks and abort the transaction if any of them failed.
901 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
903 failure = check.getFailure();
904 } catch (InterruptedException e) {
905 rwTransaction.cancel();
906 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
909 if (failure != null) {
910 rwTransaction.cancel();
911 final ReadFailedException e = failure.getValue();
913 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
914 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
917 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
922 private static void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
923 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
924 final SchemaContext schemaContext) {
925 checkItemDoesNotExists(rwTransaction, datastore, path);
926 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
927 rwTransaction.put(datastore, path, payload);
930 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
931 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
933 return rwTransaction.exists(store, path).get();
934 } catch (InterruptedException e) {
935 rwTransaction.cancel();
936 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
937 } catch (ExecutionException e) {
938 rwTransaction.cancel();
939 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
940 Throwables.getCauseAs(e, ReadFailedException.class));
945 * Check if item already exists. Throws error if it does NOT already exist.
946 * @param rwTransaction Current transaction
947 * @param store Used datastore
948 * @param path Path to item to verify its existence
950 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
951 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
952 if (!doesItemExist(rwTransaction, store, path)) {
953 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
954 rwTransaction.cancel();
955 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
956 ErrorTag.DATA_MISSING);
961 * Check if item does NOT already exist. Throws error if it already exists.
962 * @param rwTransaction Current transaction
963 * @param store Used datastore
964 * @param path Path to item to verify its existence
966 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
967 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
968 if (doesItemExist(rwTransaction, store, path)) {
969 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
970 rwTransaction.cancel();
971 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
972 ErrorTag.DATA_EXISTS);
977 * PUT data and submit {@link DOMDataReadWriteTransaction}.
984 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
985 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
986 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
987 final String insert, final String point) {
988 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
989 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
990 return readWriteTransaction.commit();
994 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
996 private void putDataWithinTransaction(
997 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
998 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
999 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1000 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1003 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1004 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1005 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1006 final String insert, final String point) {
1007 if (insert == null) {
1008 makePut(rwTransaction, datastore, path, payload, schemaContext);
1012 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1013 checkItemDoesNotExists(rwTransaction, datastore, path);
1016 if (schemaNode instanceof ListSchemaNode) {
1017 final OrderedMapNode readList =
1018 (OrderedMapNode) this.readConfigurationData(path.getParent());
1019 if (readList == null || readList.getValue().isEmpty()) {
1020 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1022 rwTransaction.delete(datastore, path.getParent());
1023 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1024 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1027 final OrderedLeafSetNode<?> readLeafList =
1028 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1029 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1030 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1032 rwTransaction.delete(datastore, path.getParent());
1033 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1034 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1040 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1043 if (schemaNode instanceof ListSchemaNode) {
1044 final OrderedMapNode readList =
1045 (OrderedMapNode) this.readConfigurationData(path.getParent());
1046 if (readList == null || readList.getValue().isEmpty()) {
1047 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1049 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1053 final OrderedLeafSetNode<?> readLeafList =
1054 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1055 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1056 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1058 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1059 readLeafList, true);
1064 if (schemaNode instanceof ListSchemaNode) {
1065 final OrderedMapNode readList =
1066 (OrderedMapNode) this.readConfigurationData(path.getParent());
1067 if (readList == null || readList.getValue().isEmpty()) {
1068 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1070 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1074 final OrderedLeafSetNode<?> readLeafList =
1075 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1076 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1077 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1079 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1080 readLeafList, false);
1085 throw new RestconfDocumentedException(
1086 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1091 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1092 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1093 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1094 final boolean before) {
1095 tx.delete(datastore, path.getParent());
1096 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1098 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1099 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1108 final NormalizedNode<?, ?> emptySubtree =
1109 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1110 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1111 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1112 if (index2 == index1) {
1113 simplePut(datastore, path, tx, schemaContext, payload);
1115 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1116 tx.put(datastore, childPath, nodeChild);
1121 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1122 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1123 final String point, final OrderedMapNode readList, final boolean before) {
1124 tx.delete(datastore, path.getParent());
1125 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1127 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1128 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1137 final NormalizedNode<?, ?> emptySubtree =
1138 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1139 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1140 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1141 if (index2 == index1) {
1142 simplePut(datastore, path, tx, schemaContext, payload);
1144 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1145 tx.put(datastore, childPath, mapEntryNode);
1150 private static void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1151 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1152 if (payload instanceof MapNode) {
1153 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1154 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1155 ensureParentsByMerge(datastore, path, tx, schemaContext);
1156 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1157 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1158 tx.put(datastore, childPath, child);
1161 simplePut(datastore, path, tx, schemaContext, payload);
1165 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1166 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext,
1167 final NormalizedNode<?, ?> payload) {
1168 ensureParentsByMerge(datastore, path, tx, schemaContext);
1169 tx.put(datastore, path, payload);
1172 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1173 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1174 final YangInstanceIdentifier path) {
1175 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1176 checkItemExists(readWriteTransaction, datastore, path);
1177 readWriteTransaction.delete(datastore, path);
1178 return readWriteTransaction.commit();
1181 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1182 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1183 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1184 tx.delete(datastore, path);
1187 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1188 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1189 final SchemaContext schemaContext) {
1190 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1191 ensureParentsByMerge(datastore, path, tx, schemaContext);
1193 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1194 // OpenDaylight should not change it.
1195 tx.merge(datastore, path, payload);
1198 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1199 if (listener.isListening()) {
1203 final SchemaPath path = listener.getSchemaPath();
1204 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1205 .registerNotificationListener(listener, path);
1207 listener.setRegistration(registration);
1210 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1211 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1212 final SchemaContext schemaContext) {
1213 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1214 YangInstanceIdentifier rootNormalizedPath = null;
1216 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1218 while (it.hasNext()) {
1219 final PathArgument pathArgument = it.next();
1220 if (rootNormalizedPath == null) {
1221 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1225 normalizedPathWithoutChildArgs.add(pathArgument);
1229 if (normalizedPathWithoutChildArgs.isEmpty()) {
1233 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1235 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1236 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1237 tx.merge(store, rootNormalizedPath, parentStructure);
1240 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1241 LOG.warn("DOM data broker service is not available for mount point {}", path);
1242 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1245 private static final class PatchStatusContextHelper {
1246 PatchStatusContext status;
1248 public PatchStatusContext getStatus() {
1252 public void setStatus(final PatchStatusContext status) {
1253 this.status = status;