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.opendaylight.yangtools.yang.model.api.SchemaContext;
95 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
96 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
97 import org.slf4j.Logger;
98 import org.slf4j.LoggerFactory;
101 public class BrokerFacade implements Closeable {
102 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
104 private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>();
105 private final DOMNotificationService domNotification;
106 private final ControllerContext controllerContext;
107 private final DOMDataBroker domDataBroker;
109 private volatile DOMRpcService rpcService;
112 public BrokerFacade(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
113 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
114 this.rpcService = requireNonNull(rpcService);
115 this.domDataBroker = requireNonNull(domDataBroker);
116 this.domNotification = requireNonNull(domNotification);
117 this.controllerContext = requireNonNull(controllerContext);
123 * @deprecated Just use
124 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
125 * constructor instead.
128 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
129 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
130 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
135 public void close() {
139 * Read config data by path.
145 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) {
146 return readConfigurationData(path, null);
150 * Read config data by path.
155 * value of with-defaults parameter
158 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
159 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
160 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
165 * Read config data from mount point by path.
168 * mount point for reading data
173 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
174 return readConfigurationData(mountPoint, path, null);
178 * Read config data from mount point by path.
181 * mount point for reading data
185 * value of with-defaults parameter
188 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
189 final String withDefa) {
190 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
191 if (domDataBrokerService.isPresent()) {
192 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
193 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
196 throw dataBrokerUnavailable(path);
200 * Read operational data by path.
206 public NormalizedNode readOperationalData(final YangInstanceIdentifier path) {
207 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
208 return readDataViaTransaction(tx, OPERATIONAL, path);
213 * Read operational data from mount point by path.
216 * mount point for reading data
221 public NormalizedNode readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
222 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
223 if (domDataBrokerService.isPresent()) {
224 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
225 return readDataViaTransaction(tx, OPERATIONAL, path);
228 throw dataBrokerUnavailable(path);
232 * <b>PUT configuration data</b>
235 * Prepare result(status) for PUT operation and PUT data via transaction.
236 * Return wrapped status and future from PUT.
238 * @param globalSchema
239 * used by merge parents (if contains list)
248 * @return wrapper of status and future of PUT
250 public PutResult commitConfigurationDataPut(final EffectiveModelContext globalSchema,
251 final YangInstanceIdentifier path, final NormalizedNode payload, final String insert, final String point) {
252 requireNonNull(globalSchema);
253 requireNonNull(path);
254 requireNonNull(payload);
256 isMounted.set(false);
257 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
258 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
260 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
261 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
263 return new PutResult(status, future);
267 * <b>PUT configuration data (Mount point)</b>
270 * Prepare result(status) for PUT operation and PUT data via transaction.
271 * Return wrapped status and future from PUT.
274 * mount point for getting transaction for operation and schema
275 * context for merging parents(if contains list)
284 * @return wrapper of status and future of PUT
286 public PutResult commitMountPointDataPut(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
287 final NormalizedNode payload, final String insert, final String point) {
288 requireNonNull(mountPoint);
289 requireNonNull(path);
290 requireNonNull(payload);
293 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
294 if (domDataBrokerService.isPresent()) {
295 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
296 domDataBrokerService.get().newReadWriteTransaction();
297 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
298 ? Status.OK : Status.CREATED;
299 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
300 newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point);
302 return new PutResult(status, future);
305 throw dataBrokerUnavailable(path);
308 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
310 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
312 // get new transaction and schema context on server or on mounted device
313 final EffectiveModelContext schemaContext;
314 final DOMDataTreeReadWriteTransaction patchTransaction;
315 if (mountPoint == null) {
316 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
317 patchTransaction = this.domDataBroker.newReadWriteTransaction();
319 schemaContext = modelContext(mountPoint);
321 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
323 if (optional.isPresent()) {
324 patchTransaction = optional.get().newReadWriteTransaction();
326 // if mount point does not have broker it is not possible to continue and global error is reported
327 LOG.error("Http Patch {} has failed - device {} does not support broker service",
328 patchContext.getPatchId(), mountPoint.getIdentifier());
329 return new PatchStatusContext(
330 patchContext.getPatchId(),
333 ImmutableList.of(new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
334 "DOM data broker service isn't available for mount point " + mountPoint.getIdentifier()))
339 final List<PatchStatusEntity> editCollection = new ArrayList<>();
340 List<RestconfError> editErrors;
341 boolean withoutError = true;
343 for (final PatchEntity patchEntity : patchContext.getData()) {
344 final PatchEditOperation operation = patchEntity.getOperation();
349 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
350 patchEntity.getNode(), schemaContext);
351 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
352 } catch (final RestconfDocumentedException e) {
353 LOG.error("Error call http Patch operation {} on target {}",
355 patchEntity.getTargetNode().toString());
357 editErrors = new ArrayList<>();
358 editErrors.addAll(e.getErrors());
359 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
360 withoutError = false;
367 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
368 .getTargetNode(), patchEntity.getNode(), schemaContext);
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;
386 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
388 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
389 } catch (final RestconfDocumentedException e) {
390 LOG.error("Error call http Patch operation {} on target {}",
392 patchEntity.getTargetNode().toString());
394 editErrors = new ArrayList<>();
395 editErrors.addAll(e.getErrors());
396 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
397 withoutError = false;
404 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
405 patchEntity.getNode(), schemaContext);
406 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
407 } catch (final RestconfDocumentedException e) {
408 LOG.error("Error call http Patch operation {} on target {}",
410 patchEntity.getTargetNode().toString());
412 editErrors = new ArrayList<>();
413 editErrors.addAll(e.getErrors());
414 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
415 withoutError = false;
420 LOG.error("Unsupported http Patch operation {} on target {}",
422 patchEntity.getTargetNode().toString());
427 // if errors then cancel transaction and return error status
429 patchTransaction.cancel();
430 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
433 // if no errors commit transaction
434 final CountDownLatch waiter = new CountDownLatch(1);
435 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
436 final PatchStatusContextHelper status = new PatchStatusContextHelper();
438 future.addCallback(new FutureCallback<CommitInfo>() {
440 public void onSuccess(final CommitInfo result) {
441 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
447 public void onFailure(final Throwable throwable) {
448 // if commit failed it is global error
449 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
450 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
451 false, ImmutableList.of(
452 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
455 }, MoreExecutors.directExecutor());
458 return status.getStatus();
461 // POST configuration
462 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
463 final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
464 final NormalizedNode payload, final String insert, final String point) {
465 isMounted.set(false);
466 FluentFuture<? extends CommitInfo> future =
467 postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
468 globalSchema, insert, point);
473 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
474 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload,
475 final String insert, final String point) {
477 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
478 if (domDataBrokerService.isPresent()) {
479 FluentFuture<? extends CommitInfo> future =
480 postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
481 payload, modelContext(mountPoint), insert, point);
486 throw dataBrokerUnavailable(path);
489 // DELETE configuration
490 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
491 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
494 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
495 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
496 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
497 if (domDataBrokerService.isPresent()) {
498 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
500 throw dataBrokerUnavailable(path);
504 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type,
505 final @NonNull NormalizedNode input) {
506 if (this.rpcService == null) {
507 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
509 LOG.trace("Invoke RPC {} with input: {}", type, input);
510 return this.rpcService.invokeRpc(type, input);
513 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final Scope scope,
514 final ListenerAdapter listener) {
515 if (listener.isListening()) {
519 final YangInstanceIdentifier path = listener.getPath();
520 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
521 .getInstance(DOMDataTreeChangeService.class);
522 if (changeService == null) {
523 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
524 + this.domDataBroker);
526 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
527 ListenerRegistration<ListenerAdapter> registration =
528 changeService.registerDataTreeChangeListener(root, listener);
529 listener.setRegistration(registration);
532 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
533 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
534 return readDataViaTransaction(transaction, datastore, path, null);
537 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
538 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
539 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
542 final Optional<NormalizedNode> optional = transaction.read(datastore, path).get();
543 return optional.map(normalizedNode -> withDefa == null ? normalizedNode :
544 prepareDataByParamWithDef(normalizedNode, path, withDefa)).orElse(null);
545 } catch (InterruptedException e) {
546 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
547 throw new RestconfDocumentedException("Error reading data.", e);
548 } catch (ExecutionException e) {
549 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
550 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
551 ReadFailedException.class));
555 private NormalizedNode prepareDataByParamWithDef(final NormalizedNode result,
556 final YangInstanceIdentifier path, final String withDefa) {
566 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
569 final EffectiveModelContext ctx = controllerContext.getGlobalSchema();
570 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
571 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
572 if (result instanceof ContainerNode) {
573 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
574 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
575 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
576 return builder.build();
579 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
580 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
581 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
582 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
583 return builder.build();
586 private void buildMapEntryBuilder(
587 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
588 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
589 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
590 for (final DataContainerChild child : result.body()) {
591 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
592 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
593 if (child instanceof ContainerNode) {
594 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
595 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
596 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
597 builder.withChild(childBuilder.build());
598 } else if (child instanceof MapNode) {
599 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
600 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
601 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
602 ((ListSchemaNode) childSchema).getKeyDefinition());
603 builder.withChild(childBuilder.build());
604 } else if (child instanceof LeafNode) {
605 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
606 final Object nodeVal = child.body();
607 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
608 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
609 if (keys.contains(child.getIdentifier().getNodeType())) {
610 leafBuilder.withValue(child.body());
611 builder.withChild(leafBuilder.build());
614 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
615 leafBuilder.withValue(child.body());
616 builder.withChild(leafBuilder.build());
619 if (defaultVal != null && defaultVal.equals(nodeVal)) {
620 leafBuilder.withValue(child.body());
621 builder.withChild(leafBuilder.build());
629 private void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder, final MapNode result,
630 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
631 final List<QName> keys) {
632 for (final MapEntryNode mapEntryNode : result.body()) {
633 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
634 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
635 .getDataSchemaNode();
636 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
637 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema);
638 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
639 builder.withChild(mapEntryBuilder.build());
643 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
644 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
645 final YangInstanceIdentifier actualPath, final boolean trim) {
646 for (final DataContainerChild child : result.body()) {
647 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
648 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
649 if (child instanceof ContainerNode) {
650 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
651 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
652 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
653 builder.withChild(builderChild.build());
654 } else if (child instanceof MapNode) {
655 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
656 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
657 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
658 ((ListSchemaNode) childSchema).getKeyDefinition());
659 builder.withChild(childBuilder.build());
660 } else if (child instanceof LeafNode) {
661 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
662 final Object nodeVal = child.body();
663 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
664 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
666 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
667 leafBuilder.withValue(child.body());
668 builder.withChild(leafBuilder.build());
671 if (defaultVal != null && defaultVal.equals(nodeVal)) {
672 leafBuilder.withValue(child.body());
673 builder.withChild(leafBuilder.build());
681 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
683 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
684 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
685 final YangInstanceIdentifier path, final NormalizedNode payload,
686 final EffectiveModelContext schemaContext, final String insert, final String point) {
687 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
688 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
689 return rwTransaction.commit();
693 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
695 private void postDataWithinTransaction(
696 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
697 final YangInstanceIdentifier path, final NormalizedNode payload,
698 final EffectiveModelContext schemaContext) {
699 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
700 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
703 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
704 final YangInstanceIdentifier path, final NormalizedNode payload,
705 final EffectiveModelContext schemaContext, final String insert, final String point) {
706 if (insert == null) {
707 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
711 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
712 checkItemDoesNotExists(rwTransaction, datastore, path);
715 if (schemaNode instanceof ListSchemaNode) {
716 final UserMapNode readList =
717 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
718 if (readList == null || readList.isEmpty()) {
719 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
721 rwTransaction.delete(datastore, path.getParent().getParent());
722 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
723 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
727 final UserLeafSetNode<?> readLeafList =
728 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
729 if (readLeafList == null || readLeafList.isEmpty()) {
730 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
732 rwTransaction.delete(datastore, path.getParent());
733 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
734 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
740 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
743 if (schemaNode instanceof ListSchemaNode) {
744 final UserMapNode readList =
745 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
746 if (readList == null || readList.isEmpty()) {
747 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
749 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
754 final UserLeafSetNode<?> readLeafList =
755 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
756 if (readLeafList == null || readLeafList.isEmpty()) {
757 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
759 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
765 if (schemaNode instanceof ListSchemaNode) {
766 final UserMapNode readList =
767 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
768 if (readList == null || readList.isEmpty()) {
769 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
771 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
776 final UserLeafSetNode<?> readLeafList =
777 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
778 if (readLeafList == null || readLeafList.isEmpty()) {
779 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
781 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
782 readLeafList, false);
787 throw new RestconfDocumentedException(
788 "Used bad value of insert parameter. Possible values are first, last, before or after, "
789 + "but was: " + insert);
793 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
794 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
795 final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
796 final boolean before) {
797 rwTransaction.delete(datastore, path.getParent().getParent());
798 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
799 int lastItemPosition = 0;
800 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
801 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
809 int lastInsertedPosition = 0;
810 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
811 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
812 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
813 if (lastInsertedPosition == lastItemPosition) {
814 checkItemDoesNotExists(rwTransaction, datastore, path);
815 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
817 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
818 checkItemDoesNotExists(rwTransaction, datastore, childPath);
819 rwTransaction.put(datastore, childPath, nodeChild);
820 lastInsertedPosition++;
824 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
825 final LogicalDatastoreType datastore,
826 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
827 final String point, final MapNode readList, final boolean before) {
828 rwTransaction.delete(datastore, path.getParent().getParent());
829 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
830 int lastItemPosition = 0;
831 for (final MapEntryNode mapEntryNode : readList.body()) {
832 if (mapEntryNode.getIdentifier()
833 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
841 int lastInsertedPosition = 0;
842 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
843 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
844 for (final MapEntryNode mapEntryNode : readList.body()) {
845 if (lastInsertedPosition == lastItemPosition) {
846 checkItemDoesNotExists(rwTransaction, datastore, path);
847 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
849 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
850 checkItemDoesNotExists(rwTransaction, datastore, childPath);
851 rwTransaction.put(datastore, childPath, mapEntryNode);
852 lastInsertedPosition++;
856 private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
857 final YangInstanceIdentifier path) {
858 final YangInstanceIdentifier parent = path.getParent();
859 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
860 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
862 if (dataSchemaNode instanceof ListSchemaNode) {
863 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
864 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
866 return dataSchemaNode;
868 if (dataSchemaNode instanceof LeafListSchemaNode) {
869 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
870 throw new RestconfDocumentedException(
871 "Insert parameter can be used only with ordered-by user leaf-list.");
873 return dataSchemaNode;
875 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
878 private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
879 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
880 final SchemaContext schemaContext) {
881 final Collection<? extends NormalizedNode> children;
882 if (payload instanceof MapNode) {
883 children = ((MapNode) payload).body();
884 } else if (payload instanceof LeafSetNode) {
885 children = ((LeafSetNode<?>) payload).body();
887 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
891 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
892 if (children.isEmpty()) {
893 if (isMounted != null && !isMounted.get()) {
895 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
897 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
902 // Kick off batch existence check first...
903 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
905 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
906 // result of the existence checks...
907 if (isMounted != null && !isMounted.get()) {
909 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
910 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
912 for (final NormalizedNode child : children) {
913 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
914 // as that would allow us to skip the existence checks
915 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
918 // ... finally collect existence checks and abort the transaction if any of them failed.
919 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
921 failure = check.getFailure();
922 } catch (InterruptedException e) {
923 rwTransaction.cancel();
924 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
927 if (failure != null) {
928 rwTransaction.cancel();
929 final ReadFailedException e = failure.getValue();
931 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
932 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
935 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
940 private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
941 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
942 final SchemaContext schemaContext) {
943 checkItemDoesNotExists(rwTransaction, datastore, path);
944 if (isMounted != null && !isMounted.get()) {
945 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
947 rwTransaction.put(datastore, path, payload);
950 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
951 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
953 return rwTransaction.exists(store, path).get();
954 } catch (InterruptedException e) {
955 rwTransaction.cancel();
956 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
957 } catch (ExecutionException e) {
958 rwTransaction.cancel();
959 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
960 Throwables.getCauseAs(e, ReadFailedException.class));
965 * Check if item already exists. Throws error if it does NOT already exist.
966 * @param rwTransaction Current transaction
967 * @param store Used datastore
968 * @param path Path to item to verify its existence
970 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
971 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
972 if (!doesItemExist(rwTransaction, store, path)) {
973 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
974 rwTransaction.cancel();
975 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
976 ErrorTag.DATA_MISSING);
981 * Check if item does NOT already exist. Throws error if it already exists.
982 * @param rwTransaction Current transaction
983 * @param store Used datastore
984 * @param path Path to item to verify its existence
986 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
987 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
988 if (doesItemExist(rwTransaction, store, path)) {
989 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
990 rwTransaction.cancel();
991 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
992 ErrorTag.DATA_EXISTS);
997 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1004 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
1005 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1006 final YangInstanceIdentifier path, final NormalizedNode payload,
1007 final EffectiveModelContext schemaContext, final String insert, final String point) {
1008 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1009 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1010 return readWriteTransaction.commit();
1014 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1016 private void putDataWithinTransaction(
1017 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1018 final YangInstanceIdentifier path, final NormalizedNode payload,
1019 final EffectiveModelContext schemaContext) {
1020 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1021 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1024 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1025 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1026 final YangInstanceIdentifier path, final NormalizedNode payload,
1027 final EffectiveModelContext schemaContext, final String insert, final String point) {
1028 if (insert == null) {
1029 makePut(rwTransaction, datastore, path, payload, schemaContext);
1033 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1034 checkItemDoesNotExists(rwTransaction, datastore, path);
1037 if (schemaNode instanceof ListSchemaNode) {
1038 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1039 if (readList == null || readList.isEmpty()) {
1040 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1042 rwTransaction.delete(datastore, path.getParent());
1043 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1044 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1047 final UserLeafSetNode<?> readLeafList =
1048 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1049 if (readLeafList == null || readLeafList.isEmpty()) {
1050 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1052 rwTransaction.delete(datastore, path.getParent());
1053 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1054 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1060 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1063 if (schemaNode instanceof ListSchemaNode) {
1064 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1065 if (readList == null || readList.isEmpty()) {
1066 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1068 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1072 final UserLeafSetNode<?> readLeafList =
1073 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1074 if (readLeafList == null || readLeafList.isEmpty()) {
1075 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1077 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1078 readLeafList, true);
1083 if (schemaNode instanceof ListSchemaNode) {
1084 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1085 if (readList == null || readList.isEmpty()) {
1086 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1088 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1092 final UserLeafSetNode<?> readLeafList =
1093 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1094 if (readLeafList == null || readLeafList.isEmpty()) {
1095 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1097 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1098 readLeafList, false);
1103 throw new RestconfDocumentedException(
1104 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1109 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1110 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1111 final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
1112 final boolean before) {
1113 tx.delete(datastore, path.getParent());
1114 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1116 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1117 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1126 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1127 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1128 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1129 if (index2 == index1) {
1130 simplePut(datastore, path, tx, schemaContext, payload);
1132 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1133 tx.put(datastore, childPath, nodeChild);
1138 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1139 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
1140 final String point, final UserMapNode readList, final boolean before) {
1141 tx.delete(datastore, path.getParent());
1142 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1144 for (final MapEntryNode mapEntryNode : readList.body()) {
1145 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1154 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1155 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1156 for (final MapEntryNode mapEntryNode : readList.body()) {
1157 if (index2 == index1) {
1158 simplePut(datastore, path, tx, schemaContext, payload);
1160 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1161 tx.put(datastore, childPath, mapEntryNode);
1166 private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1167 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext) {
1168 if (payload instanceof MapNode) {
1169 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1170 if (isMounted != null && !isMounted.get()) {
1171 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1172 ensureParentsByMerge(datastore, path, tx, schemaContext);
1174 for (final MapEntryNode child : ((MapNode) payload).body()) {
1175 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1176 tx.put(datastore, childPath, child);
1179 simplePut(datastore, path, tx, schemaContext, payload);
1183 private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1184 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode payload) {
1185 if (isMounted != null && !isMounted.get()) {
1186 ensureParentsByMerge(datastore, path, tx, schemaContext);
1188 tx.put(datastore, path, payload);
1191 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1192 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1193 final YangInstanceIdentifier path) {
1194 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1195 checkItemExists(readWriteTransaction, datastore, path);
1196 readWriteTransaction.delete(datastore, path);
1197 return readWriteTransaction.commit();
1200 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1201 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1202 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1203 tx.delete(datastore, path);
1206 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1207 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1208 final SchemaContext schemaContext) {
1209 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1210 ensureParentsByMerge(datastore, path, tx, schemaContext);
1212 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1213 // OpenDaylight should not change it.
1214 tx.merge(datastore, path, payload);
1217 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1218 if (listener.isListening()) {
1222 final SchemaPath path = listener.getSchemaPath();
1223 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1224 .registerNotificationListener(listener, Absolute.of(ImmutableList.copyOf(path.getPathFromRoot())));
1226 listener.setRegistration(registration);
1229 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1230 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1231 final SchemaContext schemaContext) {
1232 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1233 YangInstanceIdentifier rootNormalizedPath = null;
1235 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1237 while (it.hasNext()) {
1238 final PathArgument pathArgument = it.next();
1239 if (rootNormalizedPath == null) {
1240 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1244 normalizedPathWithoutChildArgs.add(pathArgument);
1248 if (normalizedPathWithoutChildArgs.isEmpty()) {
1252 checkArgument(rootNormalizedPath != null, "Empty path received");
1253 tx.merge(store, rootNormalizedPath, ImmutableNodes.fromInstanceId(schemaContext,
1254 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)));
1257 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1258 LOG.warn("DOM data broker service is not available for mount point {}", path);
1259 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1262 private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
1263 return mountPoint.getService(DOMSchemaService.class)
1264 .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
1268 private static final class PatchStatusContextHelper {
1269 PatchStatusContext status;
1271 public PatchStatusContext getStatus() {
1275 public void setStatus(final PatchStatusContext status) {
1276 this.status = status;