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.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.mdsal.dom.api.DOMSchemaService;
52 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
53 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
54 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
55 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
56 import org.opendaylight.restconf.common.errors.RestconfError;
57 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
58 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
59 import org.opendaylight.restconf.common.patch.PatchContext;
60 import org.opendaylight.restconf.common.patch.PatchEditOperation;
61 import org.opendaylight.restconf.common.patch.PatchEntity;
62 import org.opendaylight.restconf.common.patch.PatchStatusContext;
63 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
64 import org.opendaylight.restconf.common.util.DataChangeScope;
65 import org.opendaylight.yangtools.concepts.ListenerRegistration;
66 import org.opendaylight.yangtools.yang.common.QName;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
71 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
73 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
81 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
82 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
83 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
85 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
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 @SuppressWarnings("checkstyle:FinalClass")
102 public class BrokerFacade implements Closeable {
103 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
105 private volatile DOMRpcService rpcService;
107 private final DOMDataBroker domDataBroker;
108 private final DOMNotificationService domNotification;
109 private final ControllerContext controllerContext;
111 private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>();
114 public BrokerFacade(final @Reference DOMRpcService rpcService, final DOMDataBroker domDataBroker,
115 final @Reference DOMNotificationService domNotification, final ControllerContext controllerContext) {
116 this.rpcService = requireNonNull(rpcService);
117 this.domDataBroker = requireNonNull(domDataBroker);
118 this.domNotification = requireNonNull(domNotification);
119 this.controllerContext = requireNonNull(controllerContext);
125 * @deprecated Just use
126 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
127 * constructor instead.
130 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
131 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
132 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
137 public void close() {
141 * Read config data by path.
147 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
148 return readConfigurationData(path, null);
152 * Read config data by path.
157 * value of with-defaults parameter
160 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
161 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
162 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
167 * Read config data from mount point by path.
170 * mount point for reading data
175 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
176 final YangInstanceIdentifier path) {
177 return readConfigurationData(mountPoint, path, null);
181 * Read config data from mount point by path.
184 * mount point for reading data
188 * value of with-defaults parameter
191 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
192 final String withDefa) {
193 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
194 if (domDataBrokerService.isPresent()) {
195 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
196 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
199 throw dataBrokerUnavailable(path);
203 * Read operational data by path.
209 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
210 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
211 return readDataViaTransaction(tx, OPERATIONAL, path);
216 * Read operational data from mount point by path.
219 * mount point for reading data
224 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
225 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
226 if (domDataBrokerService.isPresent()) {
227 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
228 return readDataViaTransaction(tx, OPERATIONAL, path);
231 throw dataBrokerUnavailable(path);
235 * <b>PUT configuration data</b>
238 * Prepare result(status) for PUT operation and PUT data via transaction.
239 * Return wrapped status and future from PUT.
241 * @param globalSchema
242 * used by merge parents (if contains list)
251 * @return wrapper of status and future of PUT
253 public PutResult commitConfigurationDataPut(
254 final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
255 final NormalizedNode<?, ?> payload, final String insert, final String point) {
256 requireNonNull(globalSchema);
257 requireNonNull(path);
258 requireNonNull(payload);
260 isMounted.set(false);
261 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
262 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
264 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
265 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
267 return new PutResult(status, future);
271 * <b>PUT configuration data (Mount point)</b>
274 * Prepare result(status) for PUT operation and PUT data via transaction.
275 * Return wrapped status and future from PUT.
278 * mount point for getting transaction for operation and schema
279 * context for merging parents(if contains list)
288 * @return wrapper of status and future of PUT
290 public PutResult commitMountPointDataPut(
291 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
292 final String insert, final String point) {
293 requireNonNull(mountPoint);
294 requireNonNull(path);
295 requireNonNull(payload);
298 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
299 if (domDataBrokerService.isPresent()) {
300 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
301 domDataBrokerService.get().newReadWriteTransaction();
302 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
303 ? Status.OK : Status.CREATED;
304 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
305 newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point);
307 return new PutResult(status, future);
310 throw dataBrokerUnavailable(path);
313 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
315 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
317 // get new transaction and schema context on server or on mounted device
318 final EffectiveModelContext schemaContext;
319 final DOMDataTreeReadWriteTransaction patchTransaction;
320 if (mountPoint == null) {
321 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
322 patchTransaction = this.domDataBroker.newReadWriteTransaction();
324 schemaContext = modelContext(mountPoint);
326 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
328 if (optional.isPresent()) {
329 patchTransaction = optional.get().newReadWriteTransaction();
331 // if mount point does not have broker it is not possible to continue and global error is reported
332 LOG.error("Http Patch {} has failed - device {} does not support broker service",
333 patchContext.getPatchId(), mountPoint.getIdentifier());
334 return new PatchStatusContext(
335 patchContext.getPatchId(),
338 ImmutableList.of(new RestconfError(
339 ErrorType.APPLICATION,
340 ErrorTag.OPERATION_FAILED,
341 "DOM data broker service isn't available for mount point "
342 + mountPoint.getIdentifier()))
347 final List<PatchStatusEntity> editCollection = new ArrayList<>();
348 List<RestconfError> editErrors;
349 boolean withoutError = true;
351 for (final PatchEntity patchEntity : patchContext.getData()) {
352 final PatchEditOperation operation = patchEntity.getOperation();
357 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
358 patchEntity.getNode(), schemaContext);
359 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
360 } catch (final RestconfDocumentedException e) {
361 LOG.error("Error call http Patch operation {} on target {}",
363 patchEntity.getTargetNode().toString());
365 editErrors = new ArrayList<>();
366 editErrors.addAll(e.getErrors());
367 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
368 withoutError = false;
375 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
376 .getTargetNode(), patchEntity.getNode(), schemaContext);
377 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
378 } catch (final RestconfDocumentedException e) {
379 LOG.error("Error call http Patch operation {} on target {}",
381 patchEntity.getTargetNode().toString());
383 editErrors = new ArrayList<>();
384 editErrors.addAll(e.getErrors());
385 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
386 withoutError = false;
394 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
396 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
397 } catch (final RestconfDocumentedException e) {
398 LOG.error("Error call http Patch operation {} on target {}",
400 patchEntity.getTargetNode().toString());
402 editErrors = new ArrayList<>();
403 editErrors.addAll(e.getErrors());
404 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
405 withoutError = false;
412 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
413 patchEntity.getNode(), schemaContext);
414 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
415 } catch (final RestconfDocumentedException e) {
416 LOG.error("Error call http Patch operation {} on target {}",
418 patchEntity.getTargetNode().toString());
420 editErrors = new ArrayList<>();
421 editErrors.addAll(e.getErrors());
422 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
423 withoutError = false;
428 LOG.error("Unsupported http Patch operation {} on target {}",
430 patchEntity.getTargetNode().toString());
435 // if errors then cancel transaction and return error status
437 patchTransaction.cancel();
438 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
441 // if no errors commit transaction
442 final CountDownLatch waiter = new CountDownLatch(1);
443 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
444 final PatchStatusContextHelper status = new PatchStatusContextHelper();
446 future.addCallback(new FutureCallback<CommitInfo>() {
448 public void onSuccess(final CommitInfo result) {
449 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
455 public void onFailure(final Throwable throwable) {
456 // if commit failed it is global error
457 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
458 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
459 false, ImmutableList.of(
460 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
463 }, MoreExecutors.directExecutor());
466 return status.getStatus();
469 // POST configuration
470 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
471 final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
472 final NormalizedNode<?, ?> payload, final String insert, final String point) {
473 isMounted.set(false);
474 FluentFuture<? extends CommitInfo> future =
475 postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
476 globalSchema, insert, point);
481 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
482 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
483 final String insert, final String point) {
485 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
486 if (domDataBrokerService.isPresent()) {
487 FluentFuture<? extends CommitInfo> future =
488 postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
489 payload, modelContext(mountPoint), insert, point);
494 throw dataBrokerUnavailable(path);
497 // DELETE configuration
498 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
499 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
502 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
503 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
504 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
505 if (domDataBrokerService.isPresent()) {
506 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
508 throw dataBrokerUnavailable(path);
512 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type,
513 final @NonNull NormalizedNode<?, ?> input) {
514 if (this.rpcService == null) {
515 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
517 LOG.trace("Invoke RPC {} with input: {}", type, input);
518 return this.rpcService.invokeRpc(type, input);
521 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
522 final ListenerAdapter listener) {
523 if (listener.isListening()) {
527 final YangInstanceIdentifier path = listener.getPath();
528 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
529 .getInstance(DOMDataTreeChangeService.class);
530 if (changeService == null) {
531 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
532 + this.domDataBroker);
534 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
535 ListenerRegistration<ListenerAdapter> registration =
536 changeService.registerDataTreeChangeListener(root, listener);
537 listener.setRegistration(registration);
540 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadOperations transaction,
541 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
542 return readDataViaTransaction(transaction, datastore, path, null);
545 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadOperations transaction,
546 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
547 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
550 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).get();
551 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
552 prepareDataByParamWithDef(optional.get(), path, withDefa);
553 } catch (InterruptedException e) {
554 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
555 throw new RestconfDocumentedException("Error reading data.", e);
556 } catch (ExecutionException e) {
557 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
558 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
559 ReadFailedException.class));
563 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
564 final YangInstanceIdentifier path, final String withDefa) {
574 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
577 final EffectiveModelContext ctx = controllerContext.getGlobalSchema();
578 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
579 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
580 if (result instanceof ContainerNode) {
581 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
582 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
583 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
584 return builder.build();
587 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
588 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
589 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
590 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
591 return builder.build();
594 private void buildMapEntryBuilder(
595 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
596 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
597 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
598 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
599 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
600 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
601 if (child instanceof ContainerNode) {
602 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
603 Builders.containerBuilder((ContainerSchemaNode) childSchema);
604 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
605 builder.withChild(childBuilder.build());
606 } else if (child instanceof MapNode) {
607 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
608 Builders.mapBuilder((ListSchemaNode) childSchema);
609 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
610 ((ListSchemaNode) childSchema).getKeyDefinition());
611 builder.withChild(childBuilder.build());
612 } else if (child instanceof LeafNode) {
613 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
614 final Object nodeVal = child.getValue();
615 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
616 Builders.leafBuilder((LeafSchemaNode) childSchema);
617 if (keys.contains(child.getNodeType())) {
618 leafBuilder.withValue(child.getValue());
619 builder.withChild(leafBuilder.build());
622 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
623 leafBuilder.withValue(child.getValue());
624 builder.withChild(leafBuilder.build());
627 if (defaultVal != null && defaultVal.equals(nodeVal)) {
628 leafBuilder.withValue(child.getValue());
629 builder.withChild(leafBuilder.build());
637 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
638 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
639 final List<QName> keys) {
640 for (final MapEntryNode mapEntryNode : result.getValue()) {
641 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
642 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
643 .getDataSchemaNode();
644 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
645 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
646 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
647 builder.withChild(mapEntryBuilder.build());
651 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
652 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
653 final YangInstanceIdentifier actualPath, final boolean trim) {
654 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
655 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
656 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
657 if (child instanceof ContainerNode) {
658 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
659 Builders.containerBuilder((ContainerSchemaNode) childSchema);
660 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
661 builder.withChild(builderChild.build());
662 } else if (child instanceof MapNode) {
663 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
664 Builders.mapBuilder((ListSchemaNode) childSchema);
665 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
666 ((ListSchemaNode) childSchema).getKeyDefinition());
667 builder.withChild(childBuilder.build());
668 } else if (child instanceof LeafNode) {
669 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
670 final Object nodeVal = child.getValue();
671 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
672 Builders.leafBuilder((LeafSchemaNode) childSchema);
674 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
675 leafBuilder.withValue(child.getValue());
676 builder.withChild(leafBuilder.build());
679 if (defaultVal != null && defaultVal.equals(nodeVal)) {
680 leafBuilder.withValue(child.getValue());
681 builder.withChild(leafBuilder.build());
689 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
691 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
692 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
693 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
694 final EffectiveModelContext schemaContext, final String insert, final String point) {
695 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
696 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
697 return rwTransaction.commit();
701 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
703 private void postDataWithinTransaction(
704 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
705 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
706 final EffectiveModelContext schemaContext) {
707 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
708 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
711 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
712 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
713 final EffectiveModelContext schemaContext, final String insert, final String point) {
714 if (insert == null) {
715 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
719 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
720 checkItemDoesNotExists(rwTransaction, datastore, path);
723 if (schemaNode instanceof ListSchemaNode) {
724 final OrderedMapNode readList =
725 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
726 if (readList == null || readList.getValue().isEmpty()) {
727 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
729 rwTransaction.delete(datastore, path.getParent().getParent());
730 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
731 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
735 final OrderedLeafSetNode<?> readLeafList =
736 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
737 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
738 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
740 rwTransaction.delete(datastore, path.getParent());
741 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
742 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
748 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
751 if (schemaNode instanceof ListSchemaNode) {
752 final OrderedMapNode readList =
753 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
754 if (readList == null || readList.getValue().isEmpty()) {
755 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
757 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
762 final OrderedLeafSetNode<?> readLeafList =
763 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
764 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
765 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
767 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
773 if (schemaNode instanceof ListSchemaNode) {
774 final OrderedMapNode readList =
775 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
776 if (readList == null || readList.getValue().isEmpty()) {
777 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
779 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
784 final OrderedLeafSetNode<?> readLeafList =
785 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
786 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
787 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
789 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
790 readLeafList, false);
795 throw new RestconfDocumentedException(
796 "Used bad value of insert parameter. Possible values are first, last, before or after, "
797 + "but was: " + insert);
801 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
802 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
803 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
804 final boolean before) {
805 rwTransaction.delete(datastore, path.getParent().getParent());
806 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
807 int lastItemPosition = 0;
808 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
809 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
817 int lastInsertedPosition = 0;
818 final NormalizedNode<?, ?> emptySubtree =
819 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
820 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
821 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
822 if (lastInsertedPosition == lastItemPosition) {
823 checkItemDoesNotExists(rwTransaction, datastore, path);
824 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
826 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
827 checkItemDoesNotExists(rwTransaction, datastore, childPath);
828 rwTransaction.put(datastore, childPath, nodeChild);
829 lastInsertedPosition++;
833 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
834 final LogicalDatastoreType datastore,
835 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
836 final String point, final MapNode readList, final boolean before) {
837 rwTransaction.delete(datastore, path.getParent().getParent());
838 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
839 int lastItemPosition = 0;
840 for (final MapEntryNode mapEntryNode : readList.getValue()) {
841 if (mapEntryNode.getIdentifier()
842 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
850 int lastInsertedPosition = 0;
851 final NormalizedNode<?, ?> emptySubtree =
852 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
853 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
854 for (final MapEntryNode mapEntryNode : readList.getValue()) {
855 if (lastInsertedPosition == lastItemPosition) {
856 checkItemDoesNotExists(rwTransaction, datastore, path);
857 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
859 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
860 checkItemDoesNotExists(rwTransaction, datastore, childPath);
861 rwTransaction.put(datastore, childPath, mapEntryNode);
862 lastInsertedPosition++;
866 private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
867 final YangInstanceIdentifier path) {
868 final YangInstanceIdentifier parent = path.getParent();
869 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
870 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
872 if (dataSchemaNode instanceof ListSchemaNode) {
873 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
874 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
876 return dataSchemaNode;
878 if (dataSchemaNode instanceof LeafListSchemaNode) {
879 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
880 throw new RestconfDocumentedException(
881 "Insert parameter can be used only with ordered-by user leaf-list.");
883 return dataSchemaNode;
885 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
888 private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
889 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
890 final SchemaContext schemaContext) {
891 final Collection<? extends NormalizedNode<?, ?>> children;
892 if (payload instanceof MapNode) {
893 children = ((MapNode) payload).getValue();
894 } else if (payload instanceof LeafSetNode) {
895 children = ((LeafSetNode<?>) payload).getValue();
897 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
901 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
902 if (children.isEmpty()) {
903 if (isMounted != null && !isMounted.get()) {
905 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
907 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
912 // Kick off batch existence check first...
913 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
915 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
916 // result of the existence checks...
917 if (isMounted != null && !isMounted.get()) {
919 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
920 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
922 for (final NormalizedNode<?, ?> child : children) {
923 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
924 // as that would allow us to skip the existence checks
925 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
928 // ... finally collect existence checks and abort the transaction if any of them failed.
929 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
931 failure = check.getFailure();
932 } catch (InterruptedException e) {
933 rwTransaction.cancel();
934 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
937 if (failure != null) {
938 rwTransaction.cancel();
939 final ReadFailedException e = failure.getValue();
941 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
942 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
945 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
950 private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
951 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
952 final SchemaContext schemaContext) {
953 checkItemDoesNotExists(rwTransaction, datastore, path);
954 if (isMounted != null && !isMounted.get()) {
955 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
957 rwTransaction.put(datastore, path, payload);
960 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
961 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
963 return rwTransaction.exists(store, path).get();
964 } catch (InterruptedException e) {
965 rwTransaction.cancel();
966 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
967 } catch (ExecutionException e) {
968 rwTransaction.cancel();
969 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
970 Throwables.getCauseAs(e, ReadFailedException.class));
975 * Check if item already exists. Throws error if it does NOT already exist.
976 * @param rwTransaction Current transaction
977 * @param store Used datastore
978 * @param path Path to item to verify its existence
980 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
981 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
982 if (!doesItemExist(rwTransaction, store, path)) {
983 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
984 rwTransaction.cancel();
985 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
986 ErrorTag.DATA_MISSING);
991 * Check if item does NOT already exist. Throws error if it already exists.
992 * @param rwTransaction Current transaction
993 * @param store Used datastore
994 * @param path Path to item to verify its existence
996 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
997 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
998 if (doesItemExist(rwTransaction, store, path)) {
999 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
1000 rwTransaction.cancel();
1001 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
1002 ErrorTag.DATA_EXISTS);
1007 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1014 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
1015 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1016 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1017 final EffectiveModelContext schemaContext, final String insert, final String point) {
1018 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1019 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1020 return readWriteTransaction.commit();
1024 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1026 private void putDataWithinTransaction(
1027 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1028 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1029 final EffectiveModelContext schemaContext) {
1030 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1031 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1034 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1035 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1036 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1037 final EffectiveModelContext schemaContext, final String insert, final String point) {
1038 if (insert == null) {
1039 makePut(rwTransaction, datastore, path, payload, schemaContext);
1043 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1044 checkItemDoesNotExists(rwTransaction, datastore, path);
1047 if (schemaNode instanceof ListSchemaNode) {
1048 final OrderedMapNode readList =
1049 (OrderedMapNode) this.readConfigurationData(path.getParent());
1050 if (readList == null || readList.getValue().isEmpty()) {
1051 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1053 rwTransaction.delete(datastore, path.getParent());
1054 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1055 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1058 final OrderedLeafSetNode<?> readLeafList =
1059 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1060 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1061 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1063 rwTransaction.delete(datastore, path.getParent());
1064 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1065 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1071 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1074 if (schemaNode instanceof ListSchemaNode) {
1075 final OrderedMapNode readList =
1076 (OrderedMapNode) this.readConfigurationData(path.getParent());
1077 if (readList == null || readList.getValue().isEmpty()) {
1078 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1080 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1084 final OrderedLeafSetNode<?> readLeafList =
1085 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1086 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1087 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1089 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1090 readLeafList, true);
1095 if (schemaNode instanceof ListSchemaNode) {
1096 final OrderedMapNode readList =
1097 (OrderedMapNode) this.readConfigurationData(path.getParent());
1098 if (readList == null || readList.getValue().isEmpty()) {
1099 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1101 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1105 final OrderedLeafSetNode<?> readLeafList =
1106 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1107 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1108 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1110 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1111 readLeafList, false);
1116 throw new RestconfDocumentedException(
1117 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1122 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1123 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1124 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1125 final boolean before) {
1126 tx.delete(datastore, path.getParent());
1127 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1129 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1130 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1139 final NormalizedNode<?, ?> emptySubtree =
1140 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1141 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1142 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1143 if (index2 == index1) {
1144 simplePut(datastore, path, tx, schemaContext, payload);
1146 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1147 tx.put(datastore, childPath, nodeChild);
1152 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1153 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1154 final String point, final OrderedMapNode readList, final boolean before) {
1155 tx.delete(datastore, path.getParent());
1156 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1158 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1159 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1168 final NormalizedNode<?, ?> emptySubtree =
1169 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1170 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1171 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1172 if (index2 == index1) {
1173 simplePut(datastore, path, tx, schemaContext, payload);
1175 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1176 tx.put(datastore, childPath, mapEntryNode);
1181 private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1182 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1183 if (payload instanceof MapNode) {
1184 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1185 if (isMounted != null && !isMounted.get()) {
1186 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1187 ensureParentsByMerge(datastore, path, tx, schemaContext);
1189 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1190 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1191 tx.put(datastore, childPath, child);
1194 simplePut(datastore, path, tx, schemaContext, payload);
1198 private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1199 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext,
1200 final NormalizedNode<?, ?> payload) {
1201 if (isMounted != null && !isMounted.get()) {
1202 ensureParentsByMerge(datastore, path, tx, schemaContext);
1204 tx.put(datastore, path, payload);
1207 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1208 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1209 final YangInstanceIdentifier path) {
1210 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1211 checkItemExists(readWriteTransaction, datastore, path);
1212 readWriteTransaction.delete(datastore, path);
1213 return readWriteTransaction.commit();
1216 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1217 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1218 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1219 tx.delete(datastore, path);
1222 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1223 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1224 final SchemaContext schemaContext) {
1225 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1226 ensureParentsByMerge(datastore, path, tx, schemaContext);
1228 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1229 // OpenDaylight should not change it.
1230 tx.merge(datastore, path, payload);
1233 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1234 if (listener.isListening()) {
1238 final SchemaPath path = listener.getSchemaPath();
1239 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1240 .registerNotificationListener(listener, Absolute.of(ImmutableList.copyOf(path.getPathFromRoot())));
1242 listener.setRegistration(registration);
1245 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1246 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1247 final SchemaContext schemaContext) {
1248 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1249 YangInstanceIdentifier rootNormalizedPath = null;
1251 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1253 while (it.hasNext()) {
1254 final PathArgument pathArgument = it.next();
1255 if (rootNormalizedPath == null) {
1256 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1260 normalizedPathWithoutChildArgs.add(pathArgument);
1264 if (normalizedPathWithoutChildArgs.isEmpty()) {
1268 checkArgument(rootNormalizedPath != null, "Empty path received");
1270 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1271 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1272 tx.merge(store, rootNormalizedPath, parentStructure);
1275 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1276 LOG.warn("DOM data broker service is not available for mount point {}", path);
1277 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1280 private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
1281 return mountPoint.getService(DOMSchemaService.class)
1282 .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
1286 private static final class PatchStatusContextHelper {
1287 PatchStatusContext status;
1289 public PatchStatusContext getStatus() {
1293 public void setStatus(final PatchStatusContext status) {
1294 this.status = status;