2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.sal.restconf.impl;
10 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Throwables;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.util.concurrent.FluentFuture;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.MoreExecutors;
19 import java.io.Closeable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map.Entry;
25 import java.util.Objects;
26 import java.util.Optional;
27 import java.util.concurrent.CountDownLatch;
28 import java.util.concurrent.ExecutionException;
29 import javax.ws.rs.core.Response.Status;
30 import org.opendaylight.mdsal.common.api.CommitInfo;
31 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
32 import org.opendaylight.mdsal.common.api.ReadFailedException;
33 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
39 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
40 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
41 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
42 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
43 import org.opendaylight.mdsal.dom.api.DOMRpcService;
44 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
45 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
46 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
47 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
48 import org.opendaylight.restconf.common.errors.RestconfError;
49 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
50 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
51 import org.opendaylight.restconf.common.patch.PatchContext;
52 import org.opendaylight.restconf.common.patch.PatchEditOperation;
53 import org.opendaylight.restconf.common.patch.PatchEntity;
54 import org.opendaylight.restconf.common.patch.PatchStatusContext;
55 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
56 import org.opendaylight.restconf.common.util.DataChangeScope;
57 import org.opendaylight.yangtools.concepts.ListenerRegistration;
58 import org.opendaylight.yangtools.yang.common.QName;
59 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
60 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
63 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
64 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
65 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
66 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
69 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
73 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
74 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
75 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
76 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
77 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
78 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
79 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
80 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
81 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
82 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
83 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
85 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
86 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
90 @SuppressWarnings("checkstyle:FinalClass")
91 public class BrokerFacade implements Closeable {
92 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
94 private volatile DOMRpcService rpcService;
96 private final DOMDataBroker domDataBroker;
97 private final DOMNotificationService domNotification;
98 private final ControllerContext controllerContext;
100 private BrokerFacade(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
101 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
102 this.rpcService = Objects.requireNonNull(rpcService);
103 this.domDataBroker = Objects.requireNonNull(domDataBroker);
104 this.domNotification = Objects.requireNonNull(domNotification);
105 this.controllerContext = Objects.requireNonNull(controllerContext);
108 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
109 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
110 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
114 public void close() {
118 * Read config data by path.
124 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
125 return readConfigurationData(path, null);
129 * Read config data by path.
134 * value of with-defaults parameter
137 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
138 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
139 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
144 * Read config data from mount point by path.
147 * mount point for reading data
152 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
153 final YangInstanceIdentifier path) {
154 return readConfigurationData(mountPoint, path, null);
158 * Read config data from mount point by path.
161 * mount point for reading data
165 * value of with-defaults parameter
168 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
169 final String withDefa) {
170 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
171 if (domDataBrokerService.isPresent()) {
172 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
173 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
176 throw dataBrokerUnavailable(path);
180 * Read operational data by path.
186 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
187 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
188 return readDataViaTransaction(tx, OPERATIONAL, path);
193 * Read operational data from mount point by path.
196 * mount point for reading data
201 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
202 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
203 if (domDataBrokerService.isPresent()) {
204 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
205 return readDataViaTransaction(tx, OPERATIONAL, path);
208 throw dataBrokerUnavailable(path);
212 * <b>PUT configuration data</b>
215 * Prepare result(status) for PUT operation and PUT data via transaction.
216 * Return wrapped status and future from PUT.
218 * @param globalSchema
219 * used by merge parents (if contains list)
228 * @return wrapper of status and future of PUT
230 public PutResult commitConfigurationDataPut(
231 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
232 final String insert, final String point) {
233 Preconditions.checkNotNull(globalSchema);
234 Preconditions.checkNotNull(path);
235 Preconditions.checkNotNull(payload);
237 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
238 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
240 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
241 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
242 return new PutResult(status, future);
246 * <b>PUT configuration data (Mount point)</b>
249 * Prepare result(status) for PUT operation and PUT data via transaction.
250 * Return wrapped status and future from PUT.
253 * mount point for getting transaction for operation and schema
254 * context for merging parents(if contains list)
263 * @return wrapper of status and future of PUT
265 public PutResult commitMountPointDataPut(
266 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
267 final String insert, final String point) {
268 Preconditions.checkNotNull(mountPoint);
269 Preconditions.checkNotNull(path);
270 Preconditions.checkNotNull(payload);
272 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
273 if (domDataBrokerService.isPresent()) {
274 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
275 domDataBrokerService.get().newReadWriteTransaction();
276 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
277 ? Status.OK : Status.CREATED;
278 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
279 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
281 return new PutResult(status, future);
283 throw dataBrokerUnavailable(path);
286 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
288 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
290 // get new transaction and schema context on server or on mounted device
291 final SchemaContext schemaContext;
292 final DOMDataTreeReadWriteTransaction patchTransaction;
293 if (mountPoint == null) {
294 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
295 patchTransaction = this.domDataBroker.newReadWriteTransaction();
297 schemaContext = mountPoint.getSchemaContext();
299 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
301 if (optional.isPresent()) {
302 patchTransaction = optional.get().newReadWriteTransaction();
304 // if mount point does not have broker it is not possible to continue and global error is reported
305 LOG.error("Http Patch {} has failed - device {} does not support broker service",
306 patchContext.getPatchId(), mountPoint.getIdentifier());
307 return new PatchStatusContext(
308 patchContext.getPatchId(),
311 ImmutableList.of(new RestconfError(
312 ErrorType.APPLICATION,
313 ErrorTag.OPERATION_FAILED,
314 "DOM data broker service isn't available for mount point "
315 + mountPoint.getIdentifier()))
320 final List<PatchStatusEntity> editCollection = new ArrayList<>();
321 List<RestconfError> editErrors;
322 boolean withoutError = true;
324 for (final PatchEntity patchEntity : patchContext.getData()) {
325 final PatchEditOperation operation = patchEntity.getOperation();
330 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
331 patchEntity.getNode(), schemaContext);
332 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
333 } catch (final RestconfDocumentedException e) {
334 LOG.error("Error call http Patch operation {} on target {}",
336 patchEntity.getTargetNode().toString());
338 editErrors = new ArrayList<>();
339 editErrors.addAll(e.getErrors());
340 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
341 withoutError = false;
348 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
349 .getTargetNode(), patchEntity.getNode(), schemaContext);
350 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
351 } catch (final RestconfDocumentedException e) {
352 LOG.error("Error call http Patch operation {} on target {}",
354 patchEntity.getTargetNode().toString());
356 editErrors = new ArrayList<>();
357 editErrors.addAll(e.getErrors());
358 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
359 withoutError = false;
367 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
369 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
370 } catch (final RestconfDocumentedException e) {
371 LOG.error("Error call http Patch operation {} on target {}",
373 patchEntity.getTargetNode().toString());
375 editErrors = new ArrayList<>();
376 editErrors.addAll(e.getErrors());
377 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
378 withoutError = false;
385 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
386 patchEntity.getNode(), schemaContext);
387 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
388 } catch (final RestconfDocumentedException e) {
389 LOG.error("Error call http Patch operation {} on target {}",
391 patchEntity.getTargetNode().toString());
393 editErrors = new ArrayList<>();
394 editErrors.addAll(e.getErrors());
395 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
396 withoutError = false;
401 LOG.error("Unsupported http Patch operation {} on target {}",
403 patchEntity.getTargetNode().toString());
408 // if errors then cancel transaction and return error status
410 patchTransaction.cancel();
411 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
414 // if no errors commit transaction
415 final CountDownLatch waiter = new CountDownLatch(1);
416 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
417 final PatchStatusContextHelper status = new PatchStatusContextHelper();
419 future.addCallback(new FutureCallback<CommitInfo>() {
421 public void onSuccess(final CommitInfo result) {
422 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
428 public void onFailure(final Throwable throwable) {
429 // if commit failed it is global error
430 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
431 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
432 false, ImmutableList.of(
433 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
436 }, MoreExecutors.directExecutor());
439 return status.getStatus();
442 // POST configuration
443 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
444 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
445 final String insert, final String point) {
446 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
447 globalSchema, insert, point);
450 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
451 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
452 final String insert, final String point) {
453 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
454 if (domDataBrokerService.isPresent()) {
455 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
456 payload, mountPoint.getSchemaContext(), insert, point);
458 throw dataBrokerUnavailable(path);
461 // DELETE configuration
462 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
463 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
466 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
467 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
468 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
469 if (domDataBrokerService.isPresent()) {
470 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
472 throw dataBrokerUnavailable(path);
476 public FluentFuture<DOMRpcResult> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
477 if (this.rpcService == null) {
478 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
480 LOG.trace("Invoke RPC {} with input: {}", type, input);
481 return this.rpcService.invokeRpc(type, input);
484 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
485 final ListenerAdapter listener) {
486 if (listener.isListening()) {
490 final YangInstanceIdentifier path = listener.getPath();
491 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
492 .getInstance(DOMDataTreeChangeService.class);
493 if (changeService == null) {
494 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
495 + this.domDataBroker);
497 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
498 ListenerRegistration<ListenerAdapter> registration =
499 changeService.registerDataTreeChangeListener(root, listener);
500 listener.setRegistration(registration);
503 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadTransaction transaction,
504 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
505 return readDataViaTransaction(transaction, datastore, path, null);
508 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataTreeReadTransaction transaction,
509 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
510 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
513 final Optional<NormalizedNode<?, ?>> optional = transaction.read(datastore, path).get();
514 return !optional.isPresent() ? null : withDefa == null ? optional.get() :
515 prepareDataByParamWithDef(optional.get(), path, withDefa);
516 } catch (InterruptedException e) {
517 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
518 throw new RestconfDocumentedException("Error reading data.", e);
519 } catch (ExecutionException e) {
520 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
521 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
522 ReadFailedException.class));
526 private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
527 final YangInstanceIdentifier path, final String withDefa) {
537 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
540 final SchemaContext ctx = controllerContext.getGlobalSchema();
541 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
542 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
543 if (result instanceof ContainerNode) {
544 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
545 Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
546 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
547 return builder.build();
550 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
551 Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
552 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
553 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
554 return builder.build();
557 private void buildMapEntryBuilder(
558 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
559 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
560 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
561 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
562 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
563 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
564 if (child instanceof ContainerNode) {
565 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
566 Builders.containerBuilder((ContainerSchemaNode) childSchema);
567 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
568 builder.withChild(childBuilder.build());
569 } else if (child instanceof MapNode) {
570 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
571 Builders.mapBuilder((ListSchemaNode) childSchema);
572 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
573 ((ListSchemaNode) childSchema).getKeyDefinition());
574 builder.withChild(childBuilder.build());
575 } else if (child instanceof LeafNode) {
576 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
577 final Object nodeVal = child.getValue();
578 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
579 Builders.leafBuilder((LeafSchemaNode) childSchema);
580 if (keys.contains(child.getNodeType())) {
581 leafBuilder.withValue(child.getValue());
582 builder.withChild(leafBuilder.build());
585 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
586 leafBuilder.withValue(child.getValue());
587 builder.withChild(leafBuilder.build());
590 if (defaultVal != null && defaultVal.equals(nodeVal)) {
591 leafBuilder.withValue(child.getValue());
592 builder.withChild(leafBuilder.build());
600 private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
601 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
602 final List<QName> keys) {
603 for (final MapEntryNode mapEntryNode : result.getValue()) {
604 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
605 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
606 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
607 Builders.mapEntryBuilder((ListSchemaNode) childSchema);
608 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
609 builder.withChild(mapEntryBuilder.build());
613 private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
614 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
615 final YangInstanceIdentifier actualPath, final boolean trim) {
616 for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
617 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
618 final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
619 if (child instanceof ContainerNode) {
620 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
621 Builders.containerBuilder((ContainerSchemaNode) childSchema);
622 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
623 builder.withChild(builderChild.build());
624 } else if (child instanceof MapNode) {
625 final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
626 Builders.mapBuilder((ListSchemaNode) childSchema);
627 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
628 ((ListSchemaNode) childSchema).getKeyDefinition());
629 builder.withChild(childBuilder.build());
630 } else if (child instanceof LeafNode) {
631 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
632 final Object nodeVal = child.getValue();
633 final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
634 Builders.leafBuilder((LeafSchemaNode) childSchema);
636 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
637 leafBuilder.withValue(child.getValue());
638 builder.withChild(leafBuilder.build());
641 if (defaultVal != null && defaultVal.equals(nodeVal)) {
642 leafBuilder.withValue(child.getValue());
643 builder.withChild(leafBuilder.build());
651 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
653 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
654 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
655 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
656 final String insert, final String point) {
657 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
658 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
659 return rwTransaction.commit();
663 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
665 private void postDataWithinTransaction(
666 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
667 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
668 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
669 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
672 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
673 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
674 final SchemaContext schemaContext, final String insert, final String point) {
675 if (insert == null) {
676 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
680 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
681 checkItemDoesNotExists(rwTransaction, datastore, path);
684 if (schemaNode instanceof ListSchemaNode) {
685 final OrderedMapNode readList =
686 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
687 if (readList == null || readList.getValue().isEmpty()) {
688 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
690 rwTransaction.delete(datastore, path.getParent().getParent());
691 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
692 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
696 final OrderedLeafSetNode<?> readLeafList =
697 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
698 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
699 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
701 rwTransaction.delete(datastore, path.getParent());
702 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
703 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
709 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
712 if (schemaNode instanceof ListSchemaNode) {
713 final OrderedMapNode readList =
714 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
715 if (readList == null || readList.getValue().isEmpty()) {
716 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
718 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
723 final OrderedLeafSetNode<?> readLeafList =
724 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
725 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
726 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
728 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
734 if (schemaNode instanceof ListSchemaNode) {
735 final OrderedMapNode readList =
736 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
737 if (readList == null || readList.getValue().isEmpty()) {
738 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
740 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
745 final OrderedLeafSetNode<?> readLeafList =
746 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
747 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
748 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
750 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
751 readLeafList, false);
756 throw new RestconfDocumentedException(
757 "Used bad value of insert parameter. Possible values are first, last, before or after, "
758 + "but was: " + insert);
762 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
763 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
764 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
765 final boolean before) {
766 rwTransaction.delete(datastore, path.getParent().getParent());
767 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
768 int lastItemPosition = 0;
769 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
770 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
778 int lastInsertedPosition = 0;
779 final NormalizedNode<?, ?> emptySubtree =
780 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
781 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
782 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
783 if (lastInsertedPosition == lastItemPosition) {
784 checkItemDoesNotExists(rwTransaction, datastore, path);
785 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
787 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
788 checkItemDoesNotExists(rwTransaction, datastore, childPath);
789 rwTransaction.put(datastore, childPath, nodeChild);
790 lastInsertedPosition++;
794 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
795 final LogicalDatastoreType datastore,
796 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
797 final String point, final MapNode readList, final boolean before) {
798 rwTransaction.delete(datastore, path.getParent().getParent());
799 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
800 int lastItemPosition = 0;
801 for (final MapEntryNode mapEntryNode : readList.getValue()) {
802 if (mapEntryNode.getIdentifier()
803 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
811 int lastInsertedPosition = 0;
812 final NormalizedNode<?, ?> emptySubtree =
813 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
814 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
815 for (final MapEntryNode mapEntryNode : readList.getValue()) {
816 if (lastInsertedPosition == lastItemPosition) {
817 checkItemDoesNotExists(rwTransaction, datastore, path);
818 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
820 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
821 checkItemDoesNotExists(rwTransaction, datastore, childPath);
822 rwTransaction.put(datastore, childPath, mapEntryNode);
823 lastInsertedPosition++;
827 private static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
828 final YangInstanceIdentifier parent = path.getParent();
829 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
830 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
832 if (dataSchemaNode instanceof ListSchemaNode) {
833 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
834 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
836 return dataSchemaNode;
838 if (dataSchemaNode instanceof LeafListSchemaNode) {
839 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
840 throw new RestconfDocumentedException(
841 "Insert parameter can be used only with ordered-by user leaf-list.");
843 return dataSchemaNode;
845 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
848 private static void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
849 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
850 final SchemaContext schemaContext) {
851 final Collection<? extends NormalizedNode<?, ?>> children;
852 if (payload instanceof MapNode) {
853 children = ((MapNode) payload).getValue();
854 } else if (payload instanceof LeafSetNode) {
855 children = ((LeafSetNode<?>) payload).getValue();
857 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
861 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
862 if (children.isEmpty()) {
863 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
864 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
868 // Kick off batch existence check first...
869 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
871 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
872 // result of the existence checks...
873 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
874 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
875 for (final NormalizedNode<?, ?> child : children) {
876 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
877 // as that would allow us to skip the existence checks
878 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
881 // ... finally collect existence checks and abort the transaction if any of them failed.
882 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
884 failure = check.getFailure();
885 } catch (InterruptedException e) {
886 rwTransaction.cancel();
887 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
890 if (failure != null) {
891 rwTransaction.cancel();
892 final ReadFailedException e = failure.getValue();
894 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
895 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
898 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
903 private static void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
904 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
905 final SchemaContext schemaContext) {
906 checkItemDoesNotExists(rwTransaction, datastore, path);
907 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
908 rwTransaction.put(datastore, path, payload);
911 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
912 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
914 return rwTransaction.exists(store, path).get();
915 } catch (InterruptedException e) {
916 rwTransaction.cancel();
917 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
918 } catch (ExecutionException e) {
919 rwTransaction.cancel();
920 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
921 Throwables.getCauseAs(e, ReadFailedException.class));
926 * Check if item already exists. Throws error if it does NOT already exist.
927 * @param rwTransaction Current transaction
928 * @param store Used datastore
929 * @param path Path to item to verify its existence
931 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
932 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
933 if (!doesItemExist(rwTransaction, store, path)) {
934 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
935 rwTransaction.cancel();
936 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
937 ErrorTag.DATA_MISSING);
942 * Check if item does NOT already exist. Throws error if it already exists.
943 * @param rwTransaction Current transaction
944 * @param store Used datastore
945 * @param path Path to item to verify its existence
947 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
948 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
949 if (doesItemExist(rwTransaction, store, path)) {
950 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
951 rwTransaction.cancel();
952 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
953 ErrorTag.DATA_EXISTS);
958 * PUT data and submit {@link DOMDataReadWriteTransaction}.
965 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
966 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
967 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
968 final String insert, final String point) {
969 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
970 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
971 return readWriteTransaction.commit();
975 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
977 private void putDataWithinTransaction(
978 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
979 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
980 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
981 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
984 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
985 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
986 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
987 final String insert, final String point) {
988 if (insert == null) {
989 makePut(rwTransaction, datastore, path, payload, schemaContext);
993 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
994 checkItemDoesNotExists(rwTransaction, datastore, path);
997 if (schemaNode instanceof ListSchemaNode) {
998 final OrderedMapNode readList =
999 (OrderedMapNode) this.readConfigurationData(path.getParent());
1000 if (readList == null || readList.getValue().isEmpty()) {
1001 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1003 rwTransaction.delete(datastore, path.getParent());
1004 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1005 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1008 final OrderedLeafSetNode<?> readLeafList =
1009 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1010 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1011 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1013 rwTransaction.delete(datastore, path.getParent());
1014 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1015 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1021 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1024 if (schemaNode instanceof ListSchemaNode) {
1025 final OrderedMapNode readList =
1026 (OrderedMapNode) this.readConfigurationData(path.getParent());
1027 if (readList == null || readList.getValue().isEmpty()) {
1028 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1030 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1034 final OrderedLeafSetNode<?> readLeafList =
1035 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1036 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1037 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1039 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1040 readLeafList, true);
1045 if (schemaNode instanceof ListSchemaNode) {
1046 final OrderedMapNode readList =
1047 (OrderedMapNode) this.readConfigurationData(path.getParent());
1048 if (readList == null || readList.getValue().isEmpty()) {
1049 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1051 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1055 final OrderedLeafSetNode<?> readLeafList =
1056 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
1057 if (readLeafList == null || readLeafList.getValue().isEmpty()) {
1058 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1060 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1061 readLeafList, false);
1066 throw new RestconfDocumentedException(
1067 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1072 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1073 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1074 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
1075 final boolean before) {
1076 tx.delete(datastore, path.getParent());
1077 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1079 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1080 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1089 final NormalizedNode<?, ?> emptySubtree =
1090 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1091 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1092 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
1093 if (index2 == index1) {
1094 simplePut(datastore, path, tx, schemaContext, payload);
1096 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1097 tx.put(datastore, childPath, nodeChild);
1102 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1103 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
1104 final String point, final OrderedMapNode readList, final boolean before) {
1105 tx.delete(datastore, path.getParent());
1106 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1108 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1109 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1118 final NormalizedNode<?, ?> emptySubtree =
1119 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1120 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1121 for (final MapEntryNode mapEntryNode : readList.getValue()) {
1122 if (index2 == index1) {
1123 simplePut(datastore, path, tx, schemaContext, payload);
1125 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1126 tx.put(datastore, childPath, mapEntryNode);
1131 private static void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1132 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1133 if (payload instanceof MapNode) {
1134 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1135 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1136 ensureParentsByMerge(datastore, path, tx, schemaContext);
1137 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
1138 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1139 tx.put(datastore, childPath, child);
1142 simplePut(datastore, path, tx, schemaContext, payload);
1146 private static void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1147 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext,
1148 final NormalizedNode<?, ?> payload) {
1149 ensureParentsByMerge(datastore, path, tx, schemaContext);
1150 tx.put(datastore, path, payload);
1153 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1154 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1155 final YangInstanceIdentifier path) {
1156 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1157 checkItemExists(readWriteTransaction, datastore, path);
1158 readWriteTransaction.delete(datastore, path);
1159 return readWriteTransaction.commit();
1162 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1163 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1164 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1165 tx.delete(datastore, path);
1168 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1169 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
1170 final SchemaContext schemaContext) {
1171 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1172 ensureParentsByMerge(datastore, path, tx, schemaContext);
1174 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1175 // OpenDaylight should not change it.
1176 tx.merge(datastore, path, payload);
1179 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1180 if (listener.isListening()) {
1184 final SchemaPath path = listener.getSchemaPath();
1185 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1186 .registerNotificationListener(listener, path);
1188 listener.setRegistration(registration);
1191 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1192 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1193 final SchemaContext schemaContext) {
1194 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1195 YangInstanceIdentifier rootNormalizedPath = null;
1197 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1199 while (it.hasNext()) {
1200 final PathArgument pathArgument = it.next();
1201 if (rootNormalizedPath == null) {
1202 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1206 normalizedPathWithoutChildArgs.add(pathArgument);
1210 if (normalizedPathWithoutChildArgs.isEmpty()) {
1214 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1216 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1217 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1218 tx.merge(store, rootNormalizedPath, parentStructure);
1221 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1222 LOG.warn("DOM data broker service is not available for mount point {}", path);
1223 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1226 private static final class PatchStatusContextHelper {
1227 PatchStatusContext status;
1229 public PatchStatusContext getStatus() {
1233 public void setStatus(final PatchStatusContext status) {
1234 this.status = status;