2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.sal.restconf.impl;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
13 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
15 import com.google.common.base.Throwables;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import java.io.Closeable;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map.Entry;
27 import java.util.Optional;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutionException;
30 import javax.annotation.PreDestroy;
31 import javax.inject.Inject;
32 import javax.inject.Singleton;
33 import javax.ws.rs.core.Response.Status;
34 import org.eclipse.jdt.annotation.NonNull;
35 import org.opendaylight.mdsal.common.api.CommitInfo;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.mdsal.common.api.ReadFailedException;
38 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
45 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
46 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
47 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
48 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
49 import org.opendaylight.mdsal.dom.api.DOMRpcService;
50 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
51 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
52 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
53 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
54 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
55 import org.opendaylight.restconf.common.errors.RestconfError;
56 import org.opendaylight.restconf.common.patch.PatchContext;
57 import org.opendaylight.restconf.common.patch.PatchEditOperation;
58 import org.opendaylight.restconf.common.patch.PatchEntity;
59 import org.opendaylight.restconf.common.patch.PatchStatusContext;
60 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
61 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.CreateDataChangeEventSubscriptionInput1.Scope;
62 import org.opendaylight.yangtools.concepts.ListenerRegistration;
63 import org.opendaylight.yangtools.yang.common.ErrorTag;
64 import org.opendaylight.yangtools.yang.common.ErrorType;
65 import org.opendaylight.yangtools.yang.common.QName;
66 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
70 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
72 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
82 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
83 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
85 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
86 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
87 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
88 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
89 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
91 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
92 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
93 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
94 import org.slf4j.Logger;
95 import org.slf4j.LoggerFactory;
98 public class BrokerFacade implements Closeable {
99 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
101 private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>();
102 private final DOMNotificationService domNotification;
103 private final ControllerContext controllerContext;
104 private final DOMDataBroker domDataBroker;
106 private volatile DOMRpcService rpcService;
109 public BrokerFacade(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
110 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
111 this.rpcService = requireNonNull(rpcService);
112 this.domDataBroker = requireNonNull(domDataBroker);
113 this.domNotification = requireNonNull(domNotification);
114 this.controllerContext = requireNonNull(controllerContext);
120 * @deprecated Just use
121 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
122 * constructor instead.
125 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
126 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
127 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
132 public void close() {
136 * Read config data by path.
142 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) {
143 return readConfigurationData(path, null);
147 * Read config data by path.
152 * value of with-defaults parameter
155 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
156 try (DOMDataTreeReadTransaction tx = domDataBroker.newReadOnlyTransaction()) {
157 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
162 * Read config data from mount point by path.
165 * mount point for reading data
170 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
171 return readConfigurationData(mountPoint, path, null);
175 * Read config data from mount point by path.
178 * mount point for reading data
182 * value of with-defaults parameter
185 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
186 final String withDefa) {
187 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
188 if (domDataBrokerService.isPresent()) {
189 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
190 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
193 throw dataBrokerUnavailable(path);
197 * Read operational data by path.
203 public NormalizedNode readOperationalData(final YangInstanceIdentifier path) {
204 try (DOMDataTreeReadTransaction tx = domDataBroker.newReadOnlyTransaction()) {
205 return readDataViaTransaction(tx, OPERATIONAL, path);
210 * Read operational data from mount point by path.
213 * mount point for reading data
218 public NormalizedNode readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
219 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
220 if (domDataBrokerService.isPresent()) {
221 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
222 return readDataViaTransaction(tx, OPERATIONAL, path);
225 throw dataBrokerUnavailable(path);
229 * <b>PUT configuration data</b>
232 * Prepare result(status) for PUT operation and PUT data via transaction.
233 * Return wrapped status and future from PUT.
235 * @param globalSchema
236 * used by merge parents (if contains list)
245 * @return wrapper of status and future of PUT
247 public PutResult commitConfigurationDataPut(final EffectiveModelContext globalSchema,
248 final YangInstanceIdentifier path, final NormalizedNode payload, final String insert, final String point) {
249 requireNonNull(globalSchema);
250 requireNonNull(path);
251 requireNonNull(payload);
253 isMounted.set(false);
254 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = domDataBroker.newReadWriteTransaction();
255 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
257 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
258 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
260 return new PutResult(status, future);
264 * <b>PUT configuration data (Mount point)</b>
267 * Prepare result(status) for PUT operation and PUT data via transaction.
268 * Return wrapped status and future from PUT.
271 * mount point for getting transaction for operation and schema
272 * context for merging parents(if contains list)
281 * @return wrapper of status and future of PUT
283 public PutResult commitMountPointDataPut(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
284 final NormalizedNode payload, final String insert, final String point) {
285 requireNonNull(mountPoint);
286 requireNonNull(path);
287 requireNonNull(payload);
290 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
291 if (domDataBrokerService.isPresent()) {
292 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
293 domDataBrokerService.get().newReadWriteTransaction();
294 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
295 ? Status.OK : Status.CREATED;
296 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
297 newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point);
299 return new PutResult(status, future);
302 throw dataBrokerUnavailable(path);
305 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
307 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
309 // get new transaction and schema context on server or on mounted device
310 final EffectiveModelContext schemaContext;
311 final DOMDataTreeReadWriteTransaction patchTransaction;
312 if (mountPoint == null) {
313 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
314 patchTransaction = domDataBroker.newReadWriteTransaction();
316 schemaContext = modelContext(mountPoint);
318 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
320 if (optional.isPresent()) {
321 patchTransaction = optional.get().newReadWriteTransaction();
323 // if mount point does not have broker it is not possible to continue and global error is reported
324 LOG.error("Http Patch {} has failed - device {} does not support broker service",
325 patchContext.getPatchId(), mountPoint.getIdentifier());
326 return new PatchStatusContext(
327 patchContext.getPatchId(),
330 ImmutableList.of(new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
331 "DOM data broker service isn't available for mount point " + mountPoint.getIdentifier()))
336 final List<PatchStatusEntity> editCollection = new ArrayList<>();
337 List<RestconfError> editErrors;
338 boolean withoutError = true;
340 for (final PatchEntity patchEntity : patchContext.getData()) {
341 final PatchEditOperation operation = patchEntity.getOperation();
346 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
347 patchEntity.getNode(), schemaContext);
348 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
349 } catch (final RestconfDocumentedException e) {
350 LOG.error("Error call http Patch operation {} on target {}",
352 patchEntity.getTargetNode().toString());
354 editErrors = new ArrayList<>();
355 editErrors.addAll(e.getErrors());
356 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
357 withoutError = false;
364 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
365 .getTargetNode(), patchEntity.getNode(), schemaContext);
366 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
367 } catch (final RestconfDocumentedException e) {
368 LOG.error("Error call http Patch operation {} on target {}",
370 patchEntity.getTargetNode().toString());
372 editErrors = new ArrayList<>();
373 editErrors.addAll(e.getErrors());
374 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
375 withoutError = false;
383 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
385 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
386 } catch (final RestconfDocumentedException e) {
387 LOG.error("Error call http Patch operation {} on target {}",
389 patchEntity.getTargetNode().toString());
391 editErrors = new ArrayList<>();
392 editErrors.addAll(e.getErrors());
393 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
394 withoutError = false;
401 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
402 patchEntity.getNode(), schemaContext);
403 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
404 } catch (final RestconfDocumentedException e) {
405 LOG.error("Error call http Patch operation {} on target {}",
407 patchEntity.getTargetNode().toString());
409 editErrors = new ArrayList<>();
410 editErrors.addAll(e.getErrors());
411 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
412 withoutError = false;
417 LOG.error("Unsupported http Patch operation {} on target {}",
419 patchEntity.getTargetNode().toString());
424 // if errors then cancel transaction and return error status
426 patchTransaction.cancel();
427 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
430 // if no errors commit transaction
431 final CountDownLatch waiter = new CountDownLatch(1);
432 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
433 final PatchStatusContextHelper status = new PatchStatusContextHelper();
435 future.addCallback(new FutureCallback<CommitInfo>() {
437 public void onSuccess(final CommitInfo result) {
438 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
444 public void onFailure(final Throwable throwable) {
445 // if commit failed it is global error
446 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
447 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
448 false, ImmutableList.of(
449 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
452 }, MoreExecutors.directExecutor());
455 return status.getStatus();
458 // POST configuration
459 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
460 final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
461 final NormalizedNode payload, final String insert, final String point) {
462 isMounted.set(false);
463 FluentFuture<? extends CommitInfo> future =
464 postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
465 globalSchema, insert, point);
470 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
471 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload,
472 final String insert, final String point) {
474 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
475 if (domDataBrokerService.isPresent()) {
476 FluentFuture<? extends CommitInfo> future =
477 postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
478 payload, modelContext(mountPoint), insert, point);
483 throw dataBrokerUnavailable(path);
486 // DELETE configuration
487 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
488 return deleteDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
491 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
492 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
493 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
494 if (domDataBrokerService.isPresent()) {
495 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
497 throw dataBrokerUnavailable(path);
501 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type,
502 final @NonNull NormalizedNode input) {
503 if (rpcService == null) {
504 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
506 LOG.trace("Invoke RPC {} with input: {}", type, input);
507 return rpcService.invokeRpc(type, input);
510 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final Scope scope,
511 final ListenerAdapter listener) {
512 if (listener.isListening()) {
516 final YangInstanceIdentifier path = listener.getPath();
517 DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
518 .getInstance(DOMDataTreeChangeService.class);
519 if (changeService == null) {
520 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
523 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
524 ListenerRegistration<ListenerAdapter> registration =
525 changeService.registerDataTreeChangeListener(root, listener);
526 listener.setRegistration(registration);
529 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
530 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
531 return readDataViaTransaction(transaction, datastore, path, null);
534 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
535 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
536 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
539 final Optional<NormalizedNode> optional = transaction.read(datastore, path).get();
540 return optional.map(normalizedNode -> withDefa == null ? normalizedNode :
541 prepareDataByParamWithDef(normalizedNode, path, withDefa)).orElse(null);
542 } catch (InterruptedException e) {
543 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
544 throw new RestconfDocumentedException("Error reading data.", e);
545 } catch (ExecutionException e) {
546 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
547 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
548 ReadFailedException.class));
552 private NormalizedNode prepareDataByParamWithDef(final NormalizedNode result,
553 final YangInstanceIdentifier path, final String withDefa) {
563 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
566 final EffectiveModelContext ctx = controllerContext.getGlobalSchema();
567 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
568 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
569 if (result instanceof ContainerNode) {
570 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
571 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
572 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
573 return builder.build();
576 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
577 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
578 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
579 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
580 return builder.build();
583 private void buildMapEntryBuilder(
584 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
585 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
586 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
587 for (final DataContainerChild child : result.body()) {
588 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
589 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
590 if (child instanceof ContainerNode) {
591 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
592 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
593 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
594 builder.withChild(childBuilder.build());
595 } else if (child instanceof MapNode) {
596 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
597 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
598 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
599 ((ListSchemaNode) childSchema).getKeyDefinition());
600 builder.withChild(childBuilder.build());
601 } else if (child instanceof LeafNode) {
602 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
603 final Object nodeVal = child.body();
604 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
605 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
606 if (keys.contains(child.getIdentifier().getNodeType())) {
607 leafBuilder.withValue(child.body());
608 builder.withChild(leafBuilder.build());
611 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
612 leafBuilder.withValue(child.body());
613 builder.withChild(leafBuilder.build());
616 if (defaultVal != null && defaultVal.equals(nodeVal)) {
617 leafBuilder.withValue(child.body());
618 builder.withChild(leafBuilder.build());
626 private void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder, final MapNode result,
627 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
628 final List<QName> keys) {
629 for (final MapEntryNode mapEntryNode : result.body()) {
630 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
631 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
632 .getDataSchemaNode();
633 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
634 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema);
635 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
636 builder.withChild(mapEntryBuilder.build());
640 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
641 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
642 final YangInstanceIdentifier actualPath, final boolean trim) {
643 for (final DataContainerChild child : result.body()) {
644 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
645 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
646 if (child instanceof ContainerNode) {
647 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
648 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
649 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
650 builder.withChild(builderChild.build());
651 } else if (child instanceof MapNode) {
652 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
653 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
654 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
655 ((ListSchemaNode) childSchema).getKeyDefinition());
656 builder.withChild(childBuilder.build());
657 } else if (child instanceof LeafNode) {
658 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
659 final Object nodeVal = child.body();
660 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
661 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
663 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
664 leafBuilder.withValue(child.body());
665 builder.withChild(leafBuilder.build());
668 if (defaultVal != null && defaultVal.equals(nodeVal)) {
669 leafBuilder.withValue(child.body());
670 builder.withChild(leafBuilder.build());
678 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
680 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
681 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
682 final YangInstanceIdentifier path, final NormalizedNode payload,
683 final EffectiveModelContext schemaContext, final String insert, final String point) {
684 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
685 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
686 return rwTransaction.commit();
690 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
692 private void postDataWithinTransaction(
693 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
694 final YangInstanceIdentifier path, final NormalizedNode payload,
695 final EffectiveModelContext schemaContext) {
696 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
697 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
700 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
701 final YangInstanceIdentifier path, final NormalizedNode payload,
702 final EffectiveModelContext schemaContext, final String insert, final String point) {
703 if (insert == null) {
704 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
708 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
709 checkItemDoesNotExists(rwTransaction, datastore, path);
712 if (schemaNode instanceof ListSchemaNode) {
713 final UserMapNode readList =
714 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
715 if (readList == null || readList.isEmpty()) {
716 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
718 rwTransaction.delete(datastore, path.getParent().getParent());
719 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
720 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
724 final UserLeafSetNode<?> readLeafList =
725 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
726 if (readLeafList == null || readLeafList.isEmpty()) {
727 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
729 rwTransaction.delete(datastore, path.getParent());
730 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
731 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
737 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
740 if (schemaNode instanceof ListSchemaNode) {
741 final UserMapNode readList =
742 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
743 if (readList == null || readList.isEmpty()) {
744 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
746 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
751 final UserLeafSetNode<?> readLeafList =
752 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
753 if (readLeafList == null || readLeafList.isEmpty()) {
754 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
756 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
762 if (schemaNode instanceof ListSchemaNode) {
763 final UserMapNode readList =
764 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
765 if (readList == null || readList.isEmpty()) {
766 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
768 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
773 final UserLeafSetNode<?> readLeafList =
774 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
775 if (readLeafList == null || readLeafList.isEmpty()) {
776 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
778 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
779 readLeafList, false);
784 throw new RestconfDocumentedException(
785 "Used bad value of insert parameter. Possible values are first, last, before or after, "
786 + "but was: " + insert);
790 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
791 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
792 final EffectiveModelContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
793 final boolean before) {
794 rwTransaction.delete(datastore, path.getParent().getParent());
795 final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point);
796 int lastItemPosition = 0;
797 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
798 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
806 int lastInsertedPosition = 0;
807 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
808 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
809 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
810 if (lastInsertedPosition == lastItemPosition) {
811 checkItemDoesNotExists(rwTransaction, datastore, path);
812 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
814 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
815 checkItemDoesNotExists(rwTransaction, datastore, childPath);
816 rwTransaction.put(datastore, childPath, nodeChild);
817 lastInsertedPosition++;
821 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
822 final LogicalDatastoreType datastore,
823 final YangInstanceIdentifier path, final NormalizedNode payload, final EffectiveModelContext schemaContext,
824 final String point, final MapNode readList, final boolean before) {
825 rwTransaction.delete(datastore, path.getParent().getParent());
826 final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point);
827 int lastItemPosition = 0;
828 for (final MapEntryNode mapEntryNode : readList.body()) {
829 if (mapEntryNode.getIdentifier()
830 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
838 int lastInsertedPosition = 0;
839 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
840 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
841 for (final MapEntryNode mapEntryNode : readList.body()) {
842 if (lastInsertedPosition == lastItemPosition) {
843 checkItemDoesNotExists(rwTransaction, datastore, path);
844 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
846 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
847 checkItemDoesNotExists(rwTransaction, datastore, childPath);
848 rwTransaction.put(datastore, childPath, mapEntryNode);
849 lastInsertedPosition++;
853 private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
854 final YangInstanceIdentifier path) {
855 final YangInstanceIdentifier parent = path.getParent();
856 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
857 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
859 if (dataSchemaNode instanceof ListSchemaNode) {
860 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
861 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
863 return dataSchemaNode;
865 if (dataSchemaNode instanceof LeafListSchemaNode) {
866 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
867 throw new RestconfDocumentedException(
868 "Insert parameter can be used only with ordered-by user leaf-list.");
870 return dataSchemaNode;
872 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
875 private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
876 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
877 final EffectiveModelContext schemaContext) {
878 final Collection<? extends NormalizedNode> children;
879 if (payload instanceof MapNode) {
880 children = ((MapNode) payload).body();
881 } else if (payload instanceof LeafSetNode) {
882 children = ((LeafSetNode<?>) payload).body();
884 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
888 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
889 if (children.isEmpty()) {
890 if (isMounted != null && !isMounted.get()) {
892 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
894 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
899 // Kick off batch existence check first...
900 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
902 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
903 // result of the existence checks...
904 if (isMounted != null && !isMounted.get()) {
906 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
907 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
909 for (final NormalizedNode child : children) {
910 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
911 // as that would allow us to skip the existence checks
912 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
915 // ... finally collect existence checks and abort the transaction if any of them failed.
916 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
918 failure = check.getFailure();
919 } catch (InterruptedException e) {
920 rwTransaction.cancel();
921 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
924 if (failure != null) {
925 rwTransaction.cancel();
926 final ReadFailedException e = failure.getValue();
928 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
929 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
932 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
937 private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
938 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
939 final EffectiveModelContext schemaContext) {
940 checkItemDoesNotExists(rwTransaction, datastore, path);
941 if (isMounted != null && !isMounted.get()) {
942 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
944 rwTransaction.put(datastore, path, payload);
947 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
948 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
950 return rwTransaction.exists(store, path).get();
951 } catch (InterruptedException e) {
952 rwTransaction.cancel();
953 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
954 } catch (ExecutionException e) {
955 rwTransaction.cancel();
956 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
957 Throwables.getCauseAs(e, ReadFailedException.class));
962 * Check if item already exists. Throws error if it does NOT already exist.
963 * @param rwTransaction Current transaction
964 * @param store Used datastore
965 * @param path Path to item to verify its existence
967 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
968 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
969 if (!doesItemExist(rwTransaction, store, path)) {
970 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
971 rwTransaction.cancel();
972 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
973 ErrorTag.DATA_MISSING);
978 * Check if item does NOT already exist. Throws error if it already exists.
979 * @param rwTransaction Current transaction
980 * @param store Used datastore
981 * @param path Path to item to verify its existence
983 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
984 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
985 if (doesItemExist(rwTransaction, store, path)) {
986 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
987 rwTransaction.cancel();
988 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
989 ErrorTag.DATA_EXISTS);
994 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1001 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
1002 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1003 final YangInstanceIdentifier path, final NormalizedNode payload,
1004 final EffectiveModelContext schemaContext, final String insert, final String point) {
1005 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1006 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1007 return readWriteTransaction.commit();
1011 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1013 private void putDataWithinTransaction(
1014 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1015 final YangInstanceIdentifier path, final NormalizedNode payload,
1016 final EffectiveModelContext schemaContext) {
1017 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1018 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1021 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1022 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1023 final YangInstanceIdentifier path, final NormalizedNode payload,
1024 final EffectiveModelContext schemaContext, final String insert, final String point) {
1025 if (insert == null) {
1026 makePut(rwTransaction, datastore, path, payload, schemaContext);
1030 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1031 checkItemDoesNotExists(rwTransaction, datastore, path);
1034 if (schemaNode instanceof ListSchemaNode) {
1035 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1036 if (readList == null || readList.isEmpty()) {
1037 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1039 rwTransaction.delete(datastore, path.getParent());
1040 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1041 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1044 final UserLeafSetNode<?> readLeafList =
1045 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1046 if (readLeafList == null || readLeafList.isEmpty()) {
1047 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1049 rwTransaction.delete(datastore, path.getParent());
1050 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1051 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1057 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1060 if (schemaNode instanceof ListSchemaNode) {
1061 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1062 if (readList == null || readList.isEmpty()) {
1063 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1065 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1069 final UserLeafSetNode<?> readLeafList =
1070 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1071 if (readLeafList == null || readLeafList.isEmpty()) {
1072 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1074 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1075 readLeafList, true);
1080 if (schemaNode instanceof ListSchemaNode) {
1081 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1082 if (readList == null || readList.isEmpty()) {
1083 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1085 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1089 final UserLeafSetNode<?> readLeafList =
1090 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1091 if (readLeafList == null || readLeafList.isEmpty()) {
1092 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1094 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1095 readLeafList, false);
1100 throw new RestconfDocumentedException(
1101 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1106 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1107 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1108 final EffectiveModelContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
1109 final boolean before) {
1110 tx.delete(datastore, path.getParent());
1111 final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1113 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1114 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1123 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1124 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1125 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1126 if (index2 == index1) {
1127 simplePut(datastore, path, tx, schemaContext, payload);
1129 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1130 tx.put(datastore, childPath, nodeChild);
1135 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1136 final YangInstanceIdentifier path, final NormalizedNode payload, final EffectiveModelContext schemaContext,
1137 final String point, final UserMapNode readList, final boolean before) {
1138 tx.delete(datastore, path.getParent());
1139 final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1141 for (final MapEntryNode mapEntryNode : readList.body()) {
1142 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1151 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1152 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1153 for (final MapEntryNode mapEntryNode : readList.body()) {
1154 if (index2 == index1) {
1155 simplePut(datastore, path, tx, schemaContext, payload);
1157 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1158 tx.put(datastore, childPath, mapEntryNode);
1163 private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1164 final YangInstanceIdentifier path, final NormalizedNode payload,
1165 final EffectiveModelContext schemaContext) {
1166 if (payload instanceof MapNode) {
1167 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1168 if (isMounted != null && !isMounted.get()) {
1169 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1170 ensureParentsByMerge(datastore, path, tx, schemaContext);
1172 for (final MapEntryNode child : ((MapNode) payload).body()) {
1173 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1174 tx.put(datastore, childPath, child);
1177 simplePut(datastore, path, tx, schemaContext, payload);
1181 private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1182 final DOMDataTreeWriteTransaction tx, final EffectiveModelContext schemaContext,
1183 final NormalizedNode payload) {
1184 if (isMounted != null && !isMounted.get()) {
1185 ensureParentsByMerge(datastore, path, tx, schemaContext);
1187 tx.put(datastore, path, payload);
1190 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1191 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1192 final YangInstanceIdentifier path) {
1193 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1194 checkItemExists(readWriteTransaction, datastore, path);
1195 readWriteTransaction.delete(datastore, path);
1196 return readWriteTransaction.commit();
1199 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1200 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1201 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1202 tx.delete(datastore, path);
1205 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1206 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1207 final EffectiveModelContext schemaContext) {
1208 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1209 ensureParentsByMerge(datastore, path, tx, schemaContext);
1211 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1212 // OpenDaylight should not change it.
1213 tx.merge(datastore, path, payload);
1216 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1217 if (listener.isListening()) {
1221 final ListenerRegistration<DOMNotificationListener> registration = domNotification
1222 .registerNotificationListener(listener, listener.getSchemaPath());
1224 listener.setRegistration(registration);
1227 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1228 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1229 final EffectiveModelContext schemaContext) {
1230 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1231 YangInstanceIdentifier rootNormalizedPath = null;
1233 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1235 while (it.hasNext()) {
1236 final PathArgument pathArgument = it.next();
1237 if (rootNormalizedPath == null) {
1238 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1242 normalizedPathWithoutChildArgs.add(pathArgument);
1246 if (normalizedPathWithoutChildArgs.isEmpty()) {
1250 checkArgument(rootNormalizedPath != null, "Empty path received");
1251 tx.merge(store, rootNormalizedPath, ImmutableNodes.fromInstanceId(schemaContext,
1252 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)));
1255 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1256 LOG.warn("DOM data broker service is not available for mount point {}", path);
1257 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1260 private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
1261 return mountPoint.getService(DOMSchemaService.class)
1262 .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
1266 private static final class PatchStatusContextHelper {
1267 PatchStatusContext status;
1269 public PatchStatusContext getStatus() {
1273 public void setStatus(final PatchStatusContext status) {
1274 this.status = status;