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 com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
13 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
15 import com.google.common.base.Throwables;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import java.io.Closeable;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map.Entry;
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.eclipse.jdt.annotation.NonNull;
35 import org.opendaylight.mdsal.common.api.CommitInfo;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.mdsal.common.api.ReadFailedException;
38 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
45 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
46 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
47 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
48 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
49 import org.opendaylight.mdsal.dom.api.DOMRpcService;
50 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
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.SystemMapNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
82 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
83 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
85 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
86 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
87 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
88 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
89 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
91 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
92 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
93 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
94 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
95 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
96 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
97 import org.slf4j.Logger;
98 import org.slf4j.LoggerFactory;
101 public class BrokerFacade implements Closeable {
102 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
104 private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>();
105 private final DOMNotificationService domNotification;
106 private final ControllerContext controllerContext;
107 private final DOMDataBroker domDataBroker;
109 private volatile DOMRpcService rpcService;
112 public BrokerFacade(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
113 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
114 this.rpcService = requireNonNull(rpcService);
115 this.domDataBroker = requireNonNull(domDataBroker);
116 this.domNotification = requireNonNull(domNotification);
117 this.controllerContext = requireNonNull(controllerContext);
123 * @deprecated Just use
124 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
125 * constructor instead.
128 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
129 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
130 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
135 public void close() {
139 * Read config data by path.
145 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) {
146 return readConfigurationData(path, null);
150 * Read config data by path.
155 * value of with-defaults parameter
158 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
159 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
160 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
165 * Read config data from mount point by path.
168 * mount point for reading data
173 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
174 return readConfigurationData(mountPoint, path, null);
178 * Read config data from mount point by path.
181 * mount point for reading data
185 * value of with-defaults parameter
188 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
189 final String withDefa) {
190 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
191 if (domDataBrokerService.isPresent()) {
192 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
193 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
196 throw dataBrokerUnavailable(path);
200 * Read operational data by path.
206 public NormalizedNode readOperationalData(final YangInstanceIdentifier path) {
207 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
208 return readDataViaTransaction(tx, OPERATIONAL, path);
213 * Read operational data from mount point by path.
216 * mount point for reading data
221 public NormalizedNode readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
222 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
223 if (domDataBrokerService.isPresent()) {
224 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
225 return readDataViaTransaction(tx, OPERATIONAL, path);
228 throw dataBrokerUnavailable(path);
232 * <b>PUT configuration data</b>
235 * Prepare result(status) for PUT operation and PUT data via transaction.
236 * Return wrapped status and future from PUT.
238 * @param globalSchema
239 * used by merge parents (if contains list)
248 * @return wrapper of status and future of PUT
250 public PutResult commitConfigurationDataPut(final EffectiveModelContext globalSchema,
251 final YangInstanceIdentifier path, final NormalizedNode payload, final String insert, final String point) {
252 requireNonNull(globalSchema);
253 requireNonNull(path);
254 requireNonNull(payload);
256 isMounted.set(false);
257 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
258 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
260 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
261 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
263 return new PutResult(status, future);
267 * <b>PUT configuration data (Mount point)</b>
270 * Prepare result(status) for PUT operation and PUT data via transaction.
271 * Return wrapped status and future from PUT.
274 * mount point for getting transaction for operation and schema
275 * context for merging parents(if contains list)
284 * @return wrapper of status and future of PUT
286 public PutResult commitMountPointDataPut(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
287 final NormalizedNode payload, final String insert, final String point) {
288 requireNonNull(mountPoint);
289 requireNonNull(path);
290 requireNonNull(payload);
293 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
294 if (domDataBrokerService.isPresent()) {
295 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
296 domDataBrokerService.get().newReadWriteTransaction();
297 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
298 ? Status.OK : Status.CREATED;
299 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
300 newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point);
302 return new PutResult(status, future);
305 throw dataBrokerUnavailable(path);
308 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
310 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
312 // get new transaction and schema context on server or on mounted device
313 final EffectiveModelContext schemaContext;
314 final DOMDataTreeReadWriteTransaction patchTransaction;
315 if (mountPoint == null) {
316 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
317 patchTransaction = this.domDataBroker.newReadWriteTransaction();
319 schemaContext = modelContext(mountPoint);
321 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
323 if (optional.isPresent()) {
324 patchTransaction = optional.get().newReadWriteTransaction();
326 // if mount point does not have broker it is not possible to continue and global error is reported
327 LOG.error("Http Patch {} has failed - device {} does not support broker service",
328 patchContext.getPatchId(), mountPoint.getIdentifier());
329 return new PatchStatusContext(
330 patchContext.getPatchId(),
333 ImmutableList.of(new RestconfError(
334 ErrorType.APPLICATION,
335 ErrorTag.OPERATION_FAILED,
336 "DOM data broker service isn't available for mount point "
337 + mountPoint.getIdentifier()))
342 final List<PatchStatusEntity> editCollection = new ArrayList<>();
343 List<RestconfError> editErrors;
344 boolean withoutError = true;
346 for (final PatchEntity patchEntity : patchContext.getData()) {
347 final PatchEditOperation operation = patchEntity.getOperation();
352 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
353 patchEntity.getNode(), schemaContext);
354 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
355 } catch (final RestconfDocumentedException e) {
356 LOG.error("Error call http Patch operation {} on target {}",
358 patchEntity.getTargetNode().toString());
360 editErrors = new ArrayList<>();
361 editErrors.addAll(e.getErrors());
362 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
363 withoutError = false;
370 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
371 .getTargetNode(), patchEntity.getNode(), schemaContext);
372 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
373 } catch (final RestconfDocumentedException e) {
374 LOG.error("Error call http Patch operation {} on target {}",
376 patchEntity.getTargetNode().toString());
378 editErrors = new ArrayList<>();
379 editErrors.addAll(e.getErrors());
380 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
381 withoutError = false;
389 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
391 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
392 } catch (final RestconfDocumentedException e) {
393 LOG.error("Error call http Patch operation {} on target {}",
395 patchEntity.getTargetNode().toString());
397 editErrors = new ArrayList<>();
398 editErrors.addAll(e.getErrors());
399 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
400 withoutError = false;
407 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
408 patchEntity.getNode(), schemaContext);
409 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
410 } catch (final RestconfDocumentedException e) {
411 LOG.error("Error call http Patch operation {} on target {}",
413 patchEntity.getTargetNode().toString());
415 editErrors = new ArrayList<>();
416 editErrors.addAll(e.getErrors());
417 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
418 withoutError = false;
423 LOG.error("Unsupported http Patch operation {} on target {}",
425 patchEntity.getTargetNode().toString());
430 // if errors then cancel transaction and return error status
432 patchTransaction.cancel();
433 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
436 // if no errors commit transaction
437 final CountDownLatch waiter = new CountDownLatch(1);
438 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
439 final PatchStatusContextHelper status = new PatchStatusContextHelper();
441 future.addCallback(new FutureCallback<CommitInfo>() {
443 public void onSuccess(final CommitInfo result) {
444 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
450 public void onFailure(final Throwable throwable) {
451 // if commit failed it is global error
452 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
453 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
454 false, ImmutableList.of(
455 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
458 }, MoreExecutors.directExecutor());
461 return status.getStatus();
464 // POST configuration
465 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
466 final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
467 final NormalizedNode payload, final String insert, final String point) {
468 isMounted.set(false);
469 FluentFuture<? extends CommitInfo> future =
470 postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
471 globalSchema, insert, point);
476 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
477 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload,
478 final String insert, final String point) {
480 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
481 if (domDataBrokerService.isPresent()) {
482 FluentFuture<? extends CommitInfo> future =
483 postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
484 payload, modelContext(mountPoint), insert, point);
489 throw dataBrokerUnavailable(path);
492 // DELETE configuration
493 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
494 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
497 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
498 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
499 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
500 if (domDataBrokerService.isPresent()) {
501 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
503 throw dataBrokerUnavailable(path);
507 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type,
508 final @NonNull NormalizedNode input) {
509 if (this.rpcService == null) {
510 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
512 LOG.trace("Invoke RPC {} with input: {}", type, input);
513 return this.rpcService.invokeRpc(type, input);
516 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
517 final ListenerAdapter listener) {
518 if (listener.isListening()) {
522 final YangInstanceIdentifier path = listener.getPath();
523 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
524 .getInstance(DOMDataTreeChangeService.class);
525 if (changeService == null) {
526 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
527 + this.domDataBroker);
529 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
530 ListenerRegistration<ListenerAdapter> registration =
531 changeService.registerDataTreeChangeListener(root, listener);
532 listener.setRegistration(registration);
535 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
536 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
537 return readDataViaTransaction(transaction, datastore, path, null);
540 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
541 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
542 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
545 final Optional<NormalizedNode> optional = transaction.read(datastore, path).get();
546 return optional.map(normalizedNode -> withDefa == null ? normalizedNode :
547 prepareDataByParamWithDef(normalizedNode, path, withDefa)).orElse(null);
548 } catch (InterruptedException e) {
549 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
550 throw new RestconfDocumentedException("Error reading data.", e);
551 } catch (ExecutionException e) {
552 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
553 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
554 ReadFailedException.class));
558 private NormalizedNode prepareDataByParamWithDef(final NormalizedNode result,
559 final YangInstanceIdentifier path, final String withDefa) {
569 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
572 final EffectiveModelContext ctx = controllerContext.getGlobalSchema();
573 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
574 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
575 if (result instanceof ContainerNode) {
576 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
577 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
578 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
579 return builder.build();
582 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
583 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
584 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
585 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
586 return builder.build();
589 private void buildMapEntryBuilder(
590 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
591 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
592 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
593 for (final DataContainerChild child : result.body()) {
594 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
595 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
596 if (child instanceof ContainerNode) {
597 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
598 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
599 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
600 builder.withChild(childBuilder.build());
601 } else if (child instanceof MapNode) {
602 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
603 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
604 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
605 ((ListSchemaNode) childSchema).getKeyDefinition());
606 builder.withChild(childBuilder.build());
607 } else if (child instanceof LeafNode) {
608 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
609 final Object nodeVal = child.body();
610 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
611 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
612 if (keys.contains(child.getIdentifier().getNodeType())) {
613 leafBuilder.withValue(child.body());
614 builder.withChild(leafBuilder.build());
617 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
618 leafBuilder.withValue(child.body());
619 builder.withChild(leafBuilder.build());
622 if (defaultVal != null && defaultVal.equals(nodeVal)) {
623 leafBuilder.withValue(child.body());
624 builder.withChild(leafBuilder.build());
632 private void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder, final MapNode result,
633 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
634 final List<QName> keys) {
635 for (final MapEntryNode mapEntryNode : result.body()) {
636 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
637 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
638 .getDataSchemaNode();
639 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
640 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema);
641 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
642 builder.withChild(mapEntryBuilder.build());
646 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
647 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
648 final YangInstanceIdentifier actualPath, final boolean trim) {
649 for (final DataContainerChild child : result.body()) {
650 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
651 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
652 if (child instanceof ContainerNode) {
653 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
654 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
655 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
656 builder.withChild(builderChild.build());
657 } else if (child instanceof MapNode) {
658 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
659 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
660 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
661 ((ListSchemaNode) childSchema).getKeyDefinition());
662 builder.withChild(childBuilder.build());
663 } else if (child instanceof LeafNode) {
664 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
665 final Object nodeVal = child.body();
666 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
667 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
669 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
670 leafBuilder.withValue(child.body());
671 builder.withChild(leafBuilder.build());
674 if (defaultVal != null && defaultVal.equals(nodeVal)) {
675 leafBuilder.withValue(child.body());
676 builder.withChild(leafBuilder.build());
684 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
686 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
687 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
688 final YangInstanceIdentifier path, final NormalizedNode payload,
689 final EffectiveModelContext schemaContext, final String insert, final String point) {
690 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
691 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
692 return rwTransaction.commit();
696 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
698 private void postDataWithinTransaction(
699 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
700 final YangInstanceIdentifier path, final NormalizedNode payload,
701 final EffectiveModelContext schemaContext) {
702 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
703 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
706 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
707 final YangInstanceIdentifier path, final NormalizedNode payload,
708 final EffectiveModelContext schemaContext, final String insert, final String point) {
709 if (insert == null) {
710 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
714 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
715 checkItemDoesNotExists(rwTransaction, datastore, path);
718 if (schemaNode instanceof ListSchemaNode) {
719 final UserMapNode readList =
720 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
721 if (readList == null || readList.isEmpty()) {
722 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
724 rwTransaction.delete(datastore, path.getParent().getParent());
725 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
726 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
730 final UserLeafSetNode<?> readLeafList =
731 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
732 if (readLeafList == null || readLeafList.isEmpty()) {
733 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
735 rwTransaction.delete(datastore, path.getParent());
736 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
737 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
743 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
746 if (schemaNode instanceof ListSchemaNode) {
747 final UserMapNode readList =
748 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
749 if (readList == null || readList.isEmpty()) {
750 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
752 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
757 final UserLeafSetNode<?> readLeafList =
758 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
759 if (readLeafList == null || readLeafList.isEmpty()) {
760 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
762 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
768 if (schemaNode instanceof ListSchemaNode) {
769 final UserMapNode readList =
770 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
771 if (readList == null || readList.isEmpty()) {
772 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
774 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
779 final UserLeafSetNode<?> readLeafList =
780 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
781 if (readLeafList == null || readLeafList.isEmpty()) {
782 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
784 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
785 readLeafList, false);
790 throw new RestconfDocumentedException(
791 "Used bad value of insert parameter. Possible values are first, last, before or after, "
792 + "but was: " + insert);
796 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
797 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
798 final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
799 final boolean before) {
800 rwTransaction.delete(datastore, path.getParent().getParent());
801 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
802 int lastItemPosition = 0;
803 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
804 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
812 int lastInsertedPosition = 0;
813 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
814 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
815 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
816 if (lastInsertedPosition == lastItemPosition) {
817 checkItemDoesNotExists(rwTransaction, datastore, path);
818 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
820 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
821 checkItemDoesNotExists(rwTransaction, datastore, childPath);
822 rwTransaction.put(datastore, childPath, nodeChild);
823 lastInsertedPosition++;
827 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
828 final LogicalDatastoreType datastore,
829 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
830 final String point, final MapNode readList, final boolean before) {
831 rwTransaction.delete(datastore, path.getParent().getParent());
832 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
833 int lastItemPosition = 0;
834 for (final MapEntryNode mapEntryNode : readList.body()) {
835 if (mapEntryNode.getIdentifier()
836 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
844 int lastInsertedPosition = 0;
845 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
846 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
847 for (final MapEntryNode mapEntryNode : readList.body()) {
848 if (lastInsertedPosition == lastItemPosition) {
849 checkItemDoesNotExists(rwTransaction, datastore, path);
850 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
852 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
853 checkItemDoesNotExists(rwTransaction, datastore, childPath);
854 rwTransaction.put(datastore, childPath, mapEntryNode);
855 lastInsertedPosition++;
859 private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
860 final YangInstanceIdentifier path) {
861 final YangInstanceIdentifier parent = path.getParent();
862 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
863 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
865 if (dataSchemaNode instanceof ListSchemaNode) {
866 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
867 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
869 return dataSchemaNode;
871 if (dataSchemaNode instanceof LeafListSchemaNode) {
872 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
873 throw new RestconfDocumentedException(
874 "Insert parameter can be used only with ordered-by user leaf-list.");
876 return dataSchemaNode;
878 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
881 private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
882 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
883 final SchemaContext schemaContext) {
884 final Collection<? extends NormalizedNode> children;
885 if (payload instanceof MapNode) {
886 children = ((MapNode) payload).body();
887 } else if (payload instanceof LeafSetNode) {
888 children = ((LeafSetNode<?>) payload).body();
890 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
894 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
895 if (children.isEmpty()) {
896 if (isMounted != null && !isMounted.get()) {
898 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
900 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
905 // Kick off batch existence check first...
906 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
908 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
909 // result of the existence checks...
910 if (isMounted != null && !isMounted.get()) {
912 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
913 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
915 for (final NormalizedNode child : children) {
916 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
917 // as that would allow us to skip the existence checks
918 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
921 // ... finally collect existence checks and abort the transaction if any of them failed.
922 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
924 failure = check.getFailure();
925 } catch (InterruptedException e) {
926 rwTransaction.cancel();
927 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
930 if (failure != null) {
931 rwTransaction.cancel();
932 final ReadFailedException e = failure.getValue();
934 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
935 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
938 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
943 private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
944 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
945 final SchemaContext schemaContext) {
946 checkItemDoesNotExists(rwTransaction, datastore, path);
947 if (isMounted != null && !isMounted.get()) {
948 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
950 rwTransaction.put(datastore, path, payload);
953 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
954 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
956 return rwTransaction.exists(store, path).get();
957 } catch (InterruptedException e) {
958 rwTransaction.cancel();
959 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
960 } catch (ExecutionException e) {
961 rwTransaction.cancel();
962 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
963 Throwables.getCauseAs(e, ReadFailedException.class));
968 * Check if item already exists. Throws error if it does NOT already exist.
969 * @param rwTransaction Current transaction
970 * @param store Used datastore
971 * @param path Path to item to verify its existence
973 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
974 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
975 if (!doesItemExist(rwTransaction, store, path)) {
976 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
977 rwTransaction.cancel();
978 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
979 ErrorTag.DATA_MISSING);
984 * Check if item does NOT already exist. Throws error if it already exists.
985 * @param rwTransaction Current transaction
986 * @param store Used datastore
987 * @param path Path to item to verify its existence
989 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
990 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
991 if (doesItemExist(rwTransaction, store, path)) {
992 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
993 rwTransaction.cancel();
994 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
995 ErrorTag.DATA_EXISTS);
1000 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1007 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
1008 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1009 final YangInstanceIdentifier path, final NormalizedNode payload,
1010 final EffectiveModelContext schemaContext, final String insert, final String point) {
1011 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1012 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1013 return readWriteTransaction.commit();
1017 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1019 private void putDataWithinTransaction(
1020 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1021 final YangInstanceIdentifier path, final NormalizedNode payload,
1022 final EffectiveModelContext schemaContext) {
1023 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1024 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1027 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1028 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1029 final YangInstanceIdentifier path, final NormalizedNode payload,
1030 final EffectiveModelContext schemaContext, final String insert, final String point) {
1031 if (insert == null) {
1032 makePut(rwTransaction, datastore, path, payload, schemaContext);
1036 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1037 checkItemDoesNotExists(rwTransaction, datastore, path);
1040 if (schemaNode instanceof ListSchemaNode) {
1041 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1042 if (readList == null || readList.isEmpty()) {
1043 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1045 rwTransaction.delete(datastore, path.getParent());
1046 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1047 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1050 final UserLeafSetNode<?> readLeafList =
1051 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1052 if (readLeafList == null || readLeafList.isEmpty()) {
1053 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1055 rwTransaction.delete(datastore, path.getParent());
1056 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1057 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1063 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1066 if (schemaNode instanceof ListSchemaNode) {
1067 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1068 if (readList == null || readList.isEmpty()) {
1069 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1071 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1075 final UserLeafSetNode<?> readLeafList =
1076 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1077 if (readLeafList == null || readLeafList.isEmpty()) {
1078 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1080 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1081 readLeafList, true);
1086 if (schemaNode instanceof ListSchemaNode) {
1087 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1088 if (readList == null || readList.isEmpty()) {
1089 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1091 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1095 final UserLeafSetNode<?> readLeafList =
1096 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1097 if (readLeafList == null || readLeafList.isEmpty()) {
1098 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1100 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1101 readLeafList, false);
1106 throw new RestconfDocumentedException(
1107 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1112 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1113 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1114 final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
1115 final boolean before) {
1116 tx.delete(datastore, path.getParent());
1117 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1119 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1120 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1129 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1130 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1131 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1132 if (index2 == index1) {
1133 simplePut(datastore, path, tx, schemaContext, payload);
1135 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1136 tx.put(datastore, childPath, nodeChild);
1141 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1142 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
1143 final String point, final UserMapNode readList, final boolean before) {
1144 tx.delete(datastore, path.getParent());
1145 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1147 for (final MapEntryNode mapEntryNode : readList.body()) {
1148 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1157 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1158 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1159 for (final MapEntryNode mapEntryNode : readList.body()) {
1160 if (index2 == index1) {
1161 simplePut(datastore, path, tx, schemaContext, payload);
1163 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1164 tx.put(datastore, childPath, mapEntryNode);
1169 private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1170 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext) {
1171 if (payload instanceof MapNode) {
1172 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1173 if (isMounted != null && !isMounted.get()) {
1174 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1175 ensureParentsByMerge(datastore, path, tx, schemaContext);
1177 for (final MapEntryNode child : ((MapNode) payload).body()) {
1178 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1179 tx.put(datastore, childPath, child);
1182 simplePut(datastore, path, tx, schemaContext, payload);
1186 private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1187 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode payload) {
1188 if (isMounted != null && !isMounted.get()) {
1189 ensureParentsByMerge(datastore, path, tx, schemaContext);
1191 tx.put(datastore, path, payload);
1194 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1195 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1196 final YangInstanceIdentifier path) {
1197 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1198 checkItemExists(readWriteTransaction, datastore, path);
1199 readWriteTransaction.delete(datastore, path);
1200 return readWriteTransaction.commit();
1203 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1204 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1205 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1206 tx.delete(datastore, path);
1209 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1210 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1211 final SchemaContext schemaContext) {
1212 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1213 ensureParentsByMerge(datastore, path, tx, schemaContext);
1215 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1216 // OpenDaylight should not change it.
1217 tx.merge(datastore, path, payload);
1220 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1221 if (listener.isListening()) {
1225 final SchemaPath path = listener.getSchemaPath();
1226 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1227 .registerNotificationListener(listener, Absolute.of(ImmutableList.copyOf(path.getPathFromRoot())));
1229 listener.setRegistration(registration);
1232 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1233 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1234 final SchemaContext schemaContext) {
1235 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1236 YangInstanceIdentifier rootNormalizedPath = null;
1238 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1240 while (it.hasNext()) {
1241 final PathArgument pathArgument = it.next();
1242 if (rootNormalizedPath == null) {
1243 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1247 normalizedPathWithoutChildArgs.add(pathArgument);
1251 if (normalizedPathWithoutChildArgs.isEmpty()) {
1255 checkArgument(rootNormalizedPath != null, "Empty path received");
1256 tx.merge(store, rootNormalizedPath, ImmutableNodes.fromInstanceId(schemaContext,
1257 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)));
1260 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1261 LOG.warn("DOM data broker service is not available for mount point {}", path);
1262 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1265 private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
1266 return mountPoint.getService(DOMSchemaService.class)
1267 .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
1271 private static final class PatchStatusContextHelper {
1272 PatchStatusContext status;
1274 public PatchStatusContext getStatus() {
1278 public void setStatus(final PatchStatusContext status) {
1279 this.status = status;