2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.sal.restconf.impl;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
13 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
15 import com.google.common.base.Throwables;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.util.concurrent.FluentFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import java.io.Closeable;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map.Entry;
27 import java.util.Optional;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutionException;
30 import javax.annotation.PreDestroy;
31 import javax.inject.Inject;
32 import javax.inject.Singleton;
33 import javax.ws.rs.core.Response.Status;
34 import org.apache.aries.blueprint.annotation.service.Reference;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.opendaylight.mdsal.common.api.CommitInfo;
37 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
38 import org.opendaylight.mdsal.common.api.ReadFailedException;
39 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
41 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
42 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
43 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
44 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
45 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
46 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
47 import org.opendaylight.mdsal.dom.api.DOMNotificationListener;
48 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
49 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
50 import org.opendaylight.mdsal.dom.api.DOMRpcService;
51 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
52 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
53 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
54 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
55 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
56 import org.opendaylight.restconf.common.errors.RestconfError;
57 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
58 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
59 import org.opendaylight.restconf.common.patch.PatchContext;
60 import org.opendaylight.restconf.common.patch.PatchEditOperation;
61 import org.opendaylight.restconf.common.patch.PatchEntity;
62 import org.opendaylight.restconf.common.patch.PatchStatusContext;
63 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
64 import org.opendaylight.restconf.common.util.DataChangeScope;
65 import org.opendaylight.yangtools.concepts.ListenerRegistration;
66 import org.opendaylight.yangtools.yang.common.QName;
67 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
68 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
69 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
70 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
71 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
73 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
74 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
75 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
76 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
82 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
83 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
84 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
85 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
86 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders;
87 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
88 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
89 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
91 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
92 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
93 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
94 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
95 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
96 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
97 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
102 public class BrokerFacade implements Closeable {
103 private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
105 private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>();
106 private final DOMNotificationService domNotification;
107 private final ControllerContext controllerContext;
108 private final DOMDataBroker domDataBroker;
110 private volatile DOMRpcService rpcService;
113 public BrokerFacade(@Reference final DOMRpcService rpcService, @Reference final DOMDataBroker domDataBroker,
114 @Reference final DOMNotificationService domNotification, final ControllerContext controllerContext) {
115 this.rpcService = requireNonNull(rpcService);
116 this.domDataBroker = requireNonNull(domDataBroker);
117 this.domNotification = requireNonNull(domNotification);
118 this.controllerContext = requireNonNull(controllerContext);
124 * @deprecated Just use
125 * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)}
126 * constructor instead.
129 public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker,
130 final DOMNotificationService domNotification, final ControllerContext controllerContext) {
131 return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext);
136 public void close() {
140 * Read config data by path.
146 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) {
147 return readConfigurationData(path, null);
151 * Read config data by path.
156 * value of with-defaults parameter
159 public NormalizedNode readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
160 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
161 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
166 * Read config data from mount point by path.
169 * mount point for reading data
174 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
175 return readConfigurationData(mountPoint, path, null);
179 * Read config data from mount point by path.
182 * mount point for reading data
186 * value of with-defaults parameter
189 public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
190 final String withDefa) {
191 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
192 if (domDataBrokerService.isPresent()) {
193 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
194 return readDataViaTransaction(tx, CONFIGURATION, path, withDefa);
197 throw dataBrokerUnavailable(path);
201 * Read operational data by path.
207 public NormalizedNode readOperationalData(final YangInstanceIdentifier path) {
208 try (DOMDataTreeReadTransaction tx = this.domDataBroker.newReadOnlyTransaction()) {
209 return readDataViaTransaction(tx, OPERATIONAL, path);
214 * Read operational data from mount point by path.
217 * mount point for reading data
222 public NormalizedNode readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
223 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
224 if (domDataBrokerService.isPresent()) {
225 try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) {
226 return readDataViaTransaction(tx, OPERATIONAL, path);
229 throw dataBrokerUnavailable(path);
233 * <b>PUT configuration data</b>
236 * Prepare result(status) for PUT operation and PUT data via transaction.
237 * Return wrapped status and future from PUT.
239 * @param globalSchema
240 * used by merge parents (if contains list)
249 * @return wrapper of status and future of PUT
251 public PutResult commitConfigurationDataPut(final EffectiveModelContext globalSchema,
252 final YangInstanceIdentifier path, final NormalizedNode payload, final String insert, final String point) {
253 requireNonNull(globalSchema);
254 requireNonNull(path);
255 requireNonNull(payload);
257 isMounted.set(false);
258 final DOMDataTreeReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
259 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
261 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
262 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
264 return new PutResult(status, future);
268 * <b>PUT configuration data (Mount point)</b>
271 * Prepare result(status) for PUT operation and PUT data via transaction.
272 * Return wrapped status and future from PUT.
275 * mount point for getting transaction for operation and schema
276 * context for merging parents(if contains list)
285 * @return wrapper of status and future of PUT
287 public PutResult commitMountPointDataPut(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
288 final NormalizedNode payload, final String insert, final String point) {
289 requireNonNull(mountPoint);
290 requireNonNull(path);
291 requireNonNull(payload);
294 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
295 if (domDataBrokerService.isPresent()) {
296 final DOMDataTreeReadWriteTransaction newReadWriteTransaction =
297 domDataBrokerService.get().newReadWriteTransaction();
298 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
299 ? Status.OK : Status.CREATED;
300 final FluentFuture<? extends CommitInfo> future = putDataViaTransaction(
301 newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point);
303 return new PutResult(status, future);
306 throw dataBrokerUnavailable(path);
309 public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext)
311 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
313 // get new transaction and schema context on server or on mounted device
314 final EffectiveModelContext schemaContext;
315 final DOMDataTreeReadWriteTransaction patchTransaction;
316 if (mountPoint == null) {
317 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
318 patchTransaction = this.domDataBroker.newReadWriteTransaction();
320 schemaContext = modelContext(mountPoint);
322 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
324 if (optional.isPresent()) {
325 patchTransaction = optional.get().newReadWriteTransaction();
327 // if mount point does not have broker it is not possible to continue and global error is reported
328 LOG.error("Http Patch {} has failed - device {} does not support broker service",
329 patchContext.getPatchId(), mountPoint.getIdentifier());
330 return new PatchStatusContext(
331 patchContext.getPatchId(),
334 ImmutableList.of(new RestconfError(
335 ErrorType.APPLICATION,
336 ErrorTag.OPERATION_FAILED,
337 "DOM data broker service isn't available for mount point "
338 + mountPoint.getIdentifier()))
343 final List<PatchStatusEntity> editCollection = new ArrayList<>();
344 List<RestconfError> editErrors;
345 boolean withoutError = true;
347 for (final PatchEntity patchEntity : patchContext.getData()) {
348 final PatchEditOperation operation = patchEntity.getOperation();
353 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
354 patchEntity.getNode(), schemaContext);
355 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
356 } catch (final RestconfDocumentedException e) {
357 LOG.error("Error call http Patch operation {} on target {}",
359 patchEntity.getTargetNode().toString());
361 editErrors = new ArrayList<>();
362 editErrors.addAll(e.getErrors());
363 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
364 withoutError = false;
371 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
372 .getTargetNode(), patchEntity.getNode(), schemaContext);
373 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
374 } catch (final RestconfDocumentedException e) {
375 LOG.error("Error call http Patch operation {} on target {}",
377 patchEntity.getTargetNode().toString());
379 editErrors = new ArrayList<>();
380 editErrors.addAll(e.getErrors());
381 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
382 withoutError = false;
390 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
392 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
393 } catch (final RestconfDocumentedException e) {
394 LOG.error("Error call http Patch operation {} on target {}",
396 patchEntity.getTargetNode().toString());
398 editErrors = new ArrayList<>();
399 editErrors.addAll(e.getErrors());
400 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
401 withoutError = false;
408 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
409 patchEntity.getNode(), schemaContext);
410 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
411 } catch (final RestconfDocumentedException e) {
412 LOG.error("Error call http Patch operation {} on target {}",
414 patchEntity.getTargetNode().toString());
416 editErrors = new ArrayList<>();
417 editErrors.addAll(e.getErrors());
418 editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors));
419 withoutError = false;
424 LOG.error("Unsupported http Patch operation {} on target {}",
426 patchEntity.getTargetNode().toString());
431 // if errors then cancel transaction and return error status
433 patchTransaction.cancel();
434 return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
437 // if no errors commit transaction
438 final CountDownLatch waiter = new CountDownLatch(1);
439 final FluentFuture<? extends CommitInfo> future = patchTransaction.commit();
440 final PatchStatusContextHelper status = new PatchStatusContextHelper();
442 future.addCallback(new FutureCallback<CommitInfo>() {
444 public void onSuccess(final CommitInfo result) {
445 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
451 public void onFailure(final Throwable throwable) {
452 // if commit failed it is global error
453 LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId());
454 status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
455 false, ImmutableList.of(
456 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage()))));
459 }, MoreExecutors.directExecutor());
462 return status.getStatus();
465 // POST configuration
466 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
467 final EffectiveModelContext globalSchema, final YangInstanceIdentifier path,
468 final NormalizedNode payload, final String insert, final String point) {
469 isMounted.set(false);
470 FluentFuture<? extends CommitInfo> future =
471 postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
472 globalSchema, insert, point);
477 public FluentFuture<? extends CommitInfo> commitConfigurationDataPost(
478 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload,
479 final String insert, final String point) {
481 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
482 if (domDataBrokerService.isPresent()) {
483 FluentFuture<? extends CommitInfo> future =
484 postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
485 payload, modelContext(mountPoint), insert, point);
490 throw dataBrokerUnavailable(path);
493 // DELETE configuration
494 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
495 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
498 public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(
499 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
500 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
501 if (domDataBrokerService.isPresent()) {
502 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
504 throw dataBrokerUnavailable(path);
508 public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type,
509 final @NonNull NormalizedNode input) {
510 if (this.rpcService == null) {
511 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
513 LOG.trace("Invoke RPC {} with input: {}", type, input);
514 return this.rpcService.invokeRpc(type, input);
517 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
518 final ListenerAdapter listener) {
519 if (listener.isListening()) {
523 final YangInstanceIdentifier path = listener.getPath();
524 DOMDataTreeChangeService changeService = this.domDataBroker.getExtensions()
525 .getInstance(DOMDataTreeChangeService.class);
526 if (changeService == null) {
527 throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService"
528 + this.domDataBroker);
530 DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path);
531 ListenerRegistration<ListenerAdapter> registration =
532 changeService.registerDataTreeChangeListener(root, listener);
533 listener.setRegistration(registration);
536 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
537 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
538 return readDataViaTransaction(transaction, datastore, path, null);
541 private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction,
542 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
543 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
546 final Optional<NormalizedNode> optional = transaction.read(datastore, path).get();
547 return optional.map(normalizedNode -> withDefa == null ? normalizedNode :
548 prepareDataByParamWithDef(normalizedNode, path, withDefa)).orElse(null);
549 } catch (InterruptedException e) {
550 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
551 throw new RestconfDocumentedException("Error reading data.", e);
552 } catch (ExecutionException e) {
553 LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e);
554 throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e,
555 ReadFailedException.class));
559 private NormalizedNode prepareDataByParamWithDef(final NormalizedNode result,
560 final YangInstanceIdentifier path, final String withDefa) {
570 throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
573 final EffectiveModelContext ctx = controllerContext.getGlobalSchema();
574 final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
575 final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
576 if (result instanceof ContainerNode) {
577 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder =
578 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
579 buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
580 return builder.build();
583 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
584 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
585 buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
586 ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
587 return builder.build();
590 private void buildMapEntryBuilder(
591 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
592 final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
593 final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
594 for (final DataContainerChild child : result.body()) {
595 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
596 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
597 if (child instanceof ContainerNode) {
598 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder =
599 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
600 buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
601 builder.withChild(childBuilder.build());
602 } else if (child instanceof MapNode) {
603 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
604 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
605 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
606 ((ListSchemaNode) childSchema).getKeyDefinition());
607 builder.withChild(childBuilder.build());
608 } else if (child instanceof LeafNode) {
609 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
610 final Object nodeVal = child.body();
611 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
612 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
613 if (keys.contains(child.getIdentifier().getNodeType())) {
614 leafBuilder.withValue(child.body());
615 builder.withChild(leafBuilder.build());
618 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
619 leafBuilder.withValue(child.body());
620 builder.withChild(leafBuilder.build());
623 if (defaultVal != null && defaultVal.equals(nodeVal)) {
624 leafBuilder.withValue(child.body());
625 builder.withChild(leafBuilder.build());
633 private void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder, final MapNode result,
634 final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
635 final List<QName> keys) {
636 for (final MapEntryNode mapEntryNode : result.body()) {
637 final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
638 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow()
639 .getDataSchemaNode();
640 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
641 SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema);
642 buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
643 builder.withChild(mapEntryBuilder.build());
647 private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder,
648 final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
649 final YangInstanceIdentifier actualPath, final boolean trim) {
650 for (final DataContainerChild child : result.body()) {
651 final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
652 final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode();
653 if (child instanceof ContainerNode) {
654 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild =
655 SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema);
656 buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
657 builder.withChild(builderChild.build());
658 } else if (child instanceof MapNode) {
659 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder =
660 SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema);
661 buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
662 ((ListSchemaNode) childSchema).getKeyDefinition());
663 builder.withChild(childBuilder.build());
664 } else if (child instanceof LeafNode) {
665 final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null);
666 final Object nodeVal = child.body();
667 final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
668 SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema);
670 if (defaultVal == null || !defaultVal.equals(nodeVal)) {
671 leafBuilder.withValue(child.body());
672 builder.withChild(leafBuilder.build());
675 if (defaultVal != null && defaultVal.equals(nodeVal)) {
676 leafBuilder.withValue(child.body());
677 builder.withChild(leafBuilder.build());
685 * POST data and submit transaction {@link DOMDataReadWriteTransaction}.
687 private FluentFuture<? extends CommitInfo> postDataViaTransaction(
688 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
689 final YangInstanceIdentifier path, final NormalizedNode payload,
690 final EffectiveModelContext schemaContext, final String insert, final String point) {
691 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
692 postData(rwTransaction, datastore, path, payload, schemaContext, insert, point);
693 return rwTransaction.commit();
697 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}.
699 private void postDataWithinTransaction(
700 final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
701 final YangInstanceIdentifier path, final NormalizedNode payload,
702 final EffectiveModelContext schemaContext) {
703 LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
704 postData(rwTransaction, datastore, path, payload, schemaContext, null, null);
707 private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
708 final YangInstanceIdentifier path, final NormalizedNode payload,
709 final EffectiveModelContext schemaContext, final String insert, final String point) {
710 if (insert == null) {
711 makeNormalPost(rwTransaction, datastore, path, payload, schemaContext);
715 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
716 checkItemDoesNotExists(rwTransaction, datastore, path);
719 if (schemaNode instanceof ListSchemaNode) {
720 final UserMapNode readList =
721 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
722 if (readList == null || readList.isEmpty()) {
723 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
725 rwTransaction.delete(datastore, path.getParent().getParent());
726 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
727 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList,
731 final UserLeafSetNode<?> readLeafList =
732 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
733 if (readLeafList == null || readLeafList.isEmpty()) {
734 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
736 rwTransaction.delete(datastore, path.getParent());
737 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
738 makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList,
744 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
747 if (schemaNode instanceof ListSchemaNode) {
748 final UserMapNode readList =
749 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
750 if (readList == null || readList.isEmpty()) {
751 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
753 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
758 final UserLeafSetNode<?> readLeafList =
759 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
760 if (readLeafList == null || readLeafList.isEmpty()) {
761 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
763 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
769 if (schemaNode instanceof ListSchemaNode) {
770 final UserMapNode readList =
771 (UserMapNode) this.readConfigurationData(path.getParent().getParent());
772 if (readList == null || readList.isEmpty()) {
773 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
775 insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point,
780 final UserLeafSetNode<?> readLeafList =
781 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
782 if (readLeafList == null || readLeafList.isEmpty()) {
783 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
785 insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point,
786 readLeafList, false);
791 throw new RestconfDocumentedException(
792 "Used bad value of insert parameter. Possible values are first, last, before or after, "
793 + "but was: " + insert);
797 private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
798 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
799 final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
800 final boolean before) {
801 rwTransaction.delete(datastore, path.getParent().getParent());
802 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
803 int lastItemPosition = 0;
804 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
805 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
813 int lastInsertedPosition = 0;
814 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
815 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
816 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
817 if (lastInsertedPosition == lastItemPosition) {
818 checkItemDoesNotExists(rwTransaction, datastore, path);
819 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
821 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
822 checkItemDoesNotExists(rwTransaction, datastore, childPath);
823 rwTransaction.put(datastore, childPath, nodeChild);
824 lastInsertedPosition++;
828 private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
829 final LogicalDatastoreType datastore,
830 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
831 final String point, final MapNode readList, final boolean before) {
832 rwTransaction.delete(datastore, path.getParent().getParent());
833 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
834 int lastItemPosition = 0;
835 for (final MapEntryNode mapEntryNode : readList.body()) {
836 if (mapEntryNode.getIdentifier()
837 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
845 int lastInsertedPosition = 0;
846 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
847 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
848 for (final MapEntryNode mapEntryNode : readList.body()) {
849 if (lastInsertedPosition == lastItemPosition) {
850 checkItemDoesNotExists(rwTransaction, datastore, path);
851 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
853 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
854 checkItemDoesNotExists(rwTransaction, datastore, childPath);
855 rwTransaction.put(datastore, childPath, mapEntryNode);
856 lastInsertedPosition++;
860 private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx,
861 final YangInstanceIdentifier path) {
862 final YangInstanceIdentifier parent = path.getParent();
863 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow();
864 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
866 if (dataSchemaNode instanceof ListSchemaNode) {
867 if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
868 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
870 return dataSchemaNode;
872 if (dataSchemaNode instanceof LeafListSchemaNode) {
873 if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
874 throw new RestconfDocumentedException(
875 "Insert parameter can be used only with ordered-by user leaf-list.");
877 return dataSchemaNode;
879 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
882 private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction,
883 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
884 final SchemaContext schemaContext) {
885 final Collection<? extends NormalizedNode> children;
886 if (payload instanceof MapNode) {
887 children = ((MapNode) payload).body();
888 } else if (payload instanceof LeafSetNode) {
889 children = ((LeafSetNode<?>) payload).body();
891 simplePostPut(rwTransaction, datastore, path, payload, schemaContext);
895 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
896 if (children.isEmpty()) {
897 if (isMounted != null && !isMounted.get()) {
899 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()),
901 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
906 // Kick off batch existence check first...
907 final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children);
909 // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the
910 // result of the existence checks...
911 if (isMounted != null && !isMounted.get()) {
913 rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
914 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
916 for (final NormalizedNode child : children) {
917 // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction,
918 // as that would allow us to skip the existence checks
919 rwTransaction.put(datastore, path.node(child.getIdentifier()), child);
922 // ... finally collect existence checks and abort the transaction if any of them failed.
923 final Entry<YangInstanceIdentifier, ReadFailedException> failure;
925 failure = check.getFailure();
926 } catch (InterruptedException e) {
927 rwTransaction.cancel();
928 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
931 if (failure != null) {
932 rwTransaction.cancel();
933 final ReadFailedException e = failure.getValue();
935 throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(),
936 ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS);
939 throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e,
944 private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction,
945 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
946 final SchemaContext schemaContext) {
947 checkItemDoesNotExists(rwTransaction, datastore, path);
948 if (isMounted != null && !isMounted.get()) {
949 ensureParentsByMerge(datastore, path, rwTransaction, schemaContext);
951 rwTransaction.put(datastore, path, payload);
954 private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction,
955 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
957 return rwTransaction.exists(store, path).get();
958 } catch (InterruptedException e) {
959 rwTransaction.cancel();
960 throw new RestconfDocumentedException("Could not determine the existence of path " + path, e);
961 } catch (ExecutionException e) {
962 rwTransaction.cancel();
963 throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path,
964 Throwables.getCauseAs(e, ReadFailedException.class));
969 * Check if item already exists. Throws error if it does NOT already exist.
970 * @param rwTransaction Current transaction
971 * @param store Used datastore
972 * @param path Path to item to verify its existence
974 private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction,
975 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
976 if (!doesItemExist(rwTransaction, store, path)) {
977 LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path);
978 rwTransaction.cancel();
979 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
980 ErrorTag.DATA_MISSING);
985 * Check if item does NOT already exist. Throws error if it already exists.
986 * @param rwTransaction Current transaction
987 * @param store Used datastore
988 * @param path Path to item to verify its existence
990 private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction,
991 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
992 if (doesItemExist(rwTransaction, store, path)) {
993 LOG.trace("Operation via Restconf was not executed because data at {} already exists", path);
994 rwTransaction.cancel();
995 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
996 ErrorTag.DATA_EXISTS);
1001 * PUT data and submit {@link DOMDataReadWriteTransaction}.
1008 private FluentFuture<? extends CommitInfo> putDataViaTransaction(
1009 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1010 final YangInstanceIdentifier path, final NormalizedNode payload,
1011 final EffectiveModelContext schemaContext, final String insert, final String point) {
1012 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
1013 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
1014 return readWriteTransaction.commit();
1018 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}.
1020 private void putDataWithinTransaction(
1021 final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1022 final YangInstanceIdentifier path, final NormalizedNode payload,
1023 final EffectiveModelContext schemaContext) {
1024 LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1025 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
1028 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
1029 private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore,
1030 final YangInstanceIdentifier path, final NormalizedNode payload,
1031 final EffectiveModelContext schemaContext, final String insert, final String point) {
1032 if (insert == null) {
1033 makePut(rwTransaction, datastore, path, payload, schemaContext);
1037 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
1038 checkItemDoesNotExists(rwTransaction, datastore, path);
1041 if (schemaNode instanceof ListSchemaNode) {
1042 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1043 if (readList == null || readList.isEmpty()) {
1044 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1046 rwTransaction.delete(datastore, path.getParent());
1047 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1048 makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext);
1051 final UserLeafSetNode<?> readLeafList =
1052 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1053 if (readLeafList == null || readLeafList.isEmpty()) {
1054 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1056 rwTransaction.delete(datastore, path.getParent());
1057 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1058 makePut(rwTransaction, datastore, path.getParent(), readLeafList,
1064 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1067 if (schemaNode instanceof ListSchemaNode) {
1068 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1069 if (readList == null || readList.isEmpty()) {
1070 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1072 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1076 final UserLeafSetNode<?> readLeafList =
1077 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1078 if (readLeafList == null || readLeafList.isEmpty()) {
1079 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1081 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1082 readLeafList, true);
1087 if (schemaNode instanceof ListSchemaNode) {
1088 final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent());
1089 if (readList == null || readList.isEmpty()) {
1090 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1092 insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1096 final UserLeafSetNode<?> readLeafList =
1097 (UserLeafSetNode<?>) readConfigurationData(path.getParent());
1098 if (readLeafList == null || readLeafList.isEmpty()) {
1099 simplePut(datastore, path, rwTransaction, schemaContext, payload);
1101 insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point,
1102 readLeafList, false);
1107 throw new RestconfDocumentedException(
1108 "Used bad value of insert parameter. Possible values are first, last, before or after, but was: "
1113 private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx,
1114 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1115 final SchemaContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList,
1116 final boolean before) {
1117 tx.delete(datastore, path.getParent());
1118 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1120 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1121 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1130 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1131 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1132 for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) {
1133 if (index2 == index1) {
1134 simplePut(datastore, path, tx, schemaContext, payload);
1136 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
1137 tx.put(datastore, childPath, nodeChild);
1142 private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1143 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext,
1144 final String point, final UserMapNode readList, final boolean before) {
1145 tx.delete(datastore, path.getParent());
1146 final InstanceIdentifierContext<?> instanceIdentifier = controllerContext.toInstanceIdentifier(point);
1148 for (final MapEntryNode mapEntryNode : readList.body()) {
1149 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
1158 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
1159 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1160 for (final MapEntryNode mapEntryNode : readList.body()) {
1161 if (index2 == index1) {
1162 simplePut(datastore, path, tx, schemaContext, payload);
1164 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
1165 tx.put(datastore, childPath, mapEntryNode);
1170 private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore,
1171 final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext) {
1172 if (payload instanceof MapNode) {
1173 final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
1174 if (isMounted != null && !isMounted.get()) {
1175 tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
1176 ensureParentsByMerge(datastore, path, tx, schemaContext);
1178 for (final MapEntryNode child : ((MapNode) payload).body()) {
1179 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
1180 tx.put(datastore, childPath, child);
1183 simplePut(datastore, path, tx, schemaContext, payload);
1187 private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
1188 final DOMDataTreeWriteTransaction tx, final SchemaContext schemaContext, final NormalizedNode payload) {
1189 if (isMounted != null && !isMounted.get()) {
1190 ensureParentsByMerge(datastore, path, tx, schemaContext);
1192 tx.put(datastore, path, payload);
1195 private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction(
1196 final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
1197 final YangInstanceIdentifier path) {
1198 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
1199 checkItemExists(readWriteTransaction, datastore, path);
1200 readWriteTransaction.delete(datastore, path);
1201 return readWriteTransaction.commit();
1204 private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1205 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
1206 LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path);
1207 tx.delete(datastore, path);
1210 private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx,
1211 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload,
1212 final SchemaContext schemaContext) {
1213 LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload);
1214 ensureParentsByMerge(datastore, path, tx, schemaContext);
1216 // Since YANG Patch provides the option to specify what kind of operation for each edit,
1217 // OpenDaylight should not change it.
1218 tx.merge(datastore, path, payload);
1221 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1222 if (listener.isListening()) {
1226 final SchemaPath path = listener.getSchemaPath();
1227 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1228 .registerNotificationListener(listener, Absolute.of(ImmutableList.copyOf(path.getPathFromRoot())));
1230 listener.setRegistration(registration);
1233 private static void ensureParentsByMerge(final LogicalDatastoreType store,
1234 final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx,
1235 final SchemaContext schemaContext) {
1236 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1237 YangInstanceIdentifier rootNormalizedPath = null;
1239 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1241 while (it.hasNext()) {
1242 final PathArgument pathArgument = it.next();
1243 if (rootNormalizedPath == null) {
1244 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1248 normalizedPathWithoutChildArgs.add(pathArgument);
1252 if (normalizedPathWithoutChildArgs.isEmpty()) {
1256 checkArgument(rootNormalizedPath != null, "Empty path received");
1257 tx.merge(store, rootNormalizedPath, ImmutableNodes.fromInstanceId(schemaContext,
1258 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)));
1261 private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) {
1262 LOG.warn("DOM data broker service is not available for mount point {}", path);
1263 return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path);
1266 private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) {
1267 return mountPoint.getService(DOMSchemaService.class)
1268 .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext()))
1272 private static final class PatchStatusContextHelper {
1273 PatchStatusContext status;
1275 public PatchStatusContext getStatus() {
1279 public void setStatus(final PatchStatusContext status) {
1280 this.status = status;