2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.sal.restconf.impl;
10 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.util.concurrent.CheckedFuture;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.ListenableFuture;
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.concurrent.CountDownLatch;
23 import javax.annotation.Nullable;
24 import javax.ws.rs.core.Response.Status;
25 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
33 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
34 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
35 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
36 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
37 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
38 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
39 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
40 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
41 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
42 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
43 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
44 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
45 import org.opendaylight.yangtools.concepts.ListenerRegistration;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
48 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
55 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
56 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
57 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
58 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
62 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 public class BrokerFacade {
67 private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
69 private final static BrokerFacade INSTANCE = new BrokerFacade();
70 private volatile DOMRpcService rpcService;
71 private volatile ConsumerSession context;
72 private DOMDataBroker domDataBroker;
73 private DOMNotificationService domNotification;
75 private BrokerFacade() {}
77 public void setRpcService(final DOMRpcService router) {
78 this.rpcService = router;
81 public void setDomNotificationService(final DOMNotificationService domNotification) {
82 this.domNotification = domNotification;
85 public void setContext(final ConsumerSession context) {
86 this.context = context;
89 public static BrokerFacade getInstance() {
90 return BrokerFacade.INSTANCE;
93 private void checkPreconditions() {
94 if ((this.context == null) || (this.domDataBroker == null)) {
95 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
100 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
101 checkPreconditions();
102 return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
105 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
106 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
107 if (domDataBrokerService.isPresent()) {
108 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
110 final String errMsg = "DOM data broker service isn't available for mount point " + path;
112 throw new RestconfDocumentedException(errMsg);
116 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
117 checkPreconditions();
118 return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
121 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
122 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
123 if (domDataBrokerService.isPresent()) {
124 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
126 final String errMsg = "DOM data broker service isn't available for mount point " + path;
128 throw new RestconfDocumentedException(errMsg);
132 * <b>PUT configuration data</b>
134 * Prepare result(status) for PUT operation and PUT data via transaction.
135 * Return wrapped status and future from PUT.
137 * @param globalSchema
138 * - used by merge parents (if contains list)
145 * @return wrapper of status and future of PUT
147 public PutResult commitConfigurationDataPut(
148 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
149 final String insert, final String point) {
150 Preconditions.checkNotNull(globalSchema);
151 Preconditions.checkNotNull(path);
152 Preconditions.checkNotNull(payload);
154 checkPreconditions();
156 final DOMDataReadWriteTransaction newReadWriteTransaction = this.domDataBroker.newReadWriteTransaction();
157 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
159 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
160 newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
161 return new PutResult(status, future);
165 * <b>PUT configuration data (Mount point)</b>
167 * Prepare result(status) for PUT operation and PUT data via transaction.
168 * Return wrapped status and future from PUT.
171 * - mount point for getting transaction for operation and schema
172 * context for merging parents(if contains list)
179 * @return wrapper of status and future of PUT
181 public PutResult commitMountPointDataPut(
182 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
183 final String insert, final String point) {
184 Preconditions.checkNotNull(mountPoint);
185 Preconditions.checkNotNull(path);
186 Preconditions.checkNotNull(payload);
188 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
189 if (domDataBrokerService.isPresent()) {
190 final DOMDataReadWriteTransaction newReadWriteTransaction = domDataBrokerService.get().newReadWriteTransaction();
191 final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
192 ? Status.OK : Status.CREATED;
193 final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
194 newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
196 return new PutResult(status, future);
198 final String errMsg = "DOM data broker service isn't available for mount point " + path;
200 throw new RestconfDocumentedException(errMsg);
203 public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext patchContext) throws Exception {
204 final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
206 // get new transaction and schema context on server or on mounted device
207 final SchemaContext schemaContext;
208 final DOMDataReadWriteTransaction patchTransaction;
209 if (mountPoint == null) {
210 schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
211 patchTransaction = this.domDataBroker.newReadWriteTransaction();
213 schemaContext = mountPoint.getSchemaContext();
215 final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
217 if (optional.isPresent()) {
218 patchTransaction = optional.get().newReadWriteTransaction();
220 // if mount point does not have broker it is not possible to continue and global error is reported
221 LOG.error("Http PATCH {} has failed - device {} does not support broker service",
222 patchContext.getPatchId(), mountPoint.getIdentifier());
223 return new PATCHStatusContext(
224 patchContext.getPatchId(),
227 ImmutableList.of(new RestconfError(
228 ErrorType.APPLICATION,
229 ErrorTag.OPERATION_FAILED,
230 "DOM data broker service isn't available for mount point "
231 + mountPoint.getIdentifier()))
236 final List<PATCHStatusEntity> editCollection = new ArrayList<>();
237 List<RestconfError> editErrors;
238 boolean withoutError = true;
240 for (final PATCHEntity patchEntity : patchContext.getData()) {
241 final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
247 postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
248 patchEntity.getNode(), schemaContext);
249 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
250 } catch (final RestconfDocumentedException e) {
251 LOG.error("Error call http PATCH operation {} on target {}",
253 patchEntity.getTargetNode().toString());
255 editErrors = new ArrayList<>();
256 editErrors.addAll(e.getErrors());
257 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
258 withoutError = false;
265 putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
266 .getTargetNode(), patchEntity.getNode(), schemaContext);
267 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
268 } catch (final RestconfDocumentedException e) {
269 LOG.error("Error call http PATCH operation {} on target {}",
271 patchEntity.getTargetNode().toString());
273 editErrors = new ArrayList<>();
274 editErrors.addAll(e.getErrors());
275 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
276 withoutError = false;
283 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
285 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
286 } catch (final RestconfDocumentedException e) {
287 LOG.error("Error call http PATCH operation {} on target {}",
289 patchEntity.getTargetNode().toString());
291 editErrors = new ArrayList<>();
292 editErrors.addAll(e.getErrors());
293 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
294 withoutError = false;
301 deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
303 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
304 } catch (final RestconfDocumentedException e) {
305 LOG.error("Error call http PATCH operation {} on target {}",
307 patchEntity.getTargetNode().toString());
309 editErrors = new ArrayList<>();
310 editErrors.addAll(e.getErrors());
311 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
312 withoutError = false;
319 mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
320 patchEntity.getNode(), schemaContext);
321 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
322 } catch (final RestconfDocumentedException e) {
323 LOG.error("Error call http PATCH operation {} on target {}",
325 patchEntity.getTargetNode().toString());
327 editErrors = new ArrayList<>();
328 editErrors.addAll(e.getErrors());
329 editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
330 withoutError = false;
335 LOG.error("Unsupported http PATCH operation {} on target {}",
337 patchEntity.getTargetNode().toString());
342 // if errors then cancel transaction and return error status
344 patchTransaction.cancel();
345 return new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
348 // if no errors commit transaction
349 final CountDownLatch waiter = new CountDownLatch(1);
350 final CheckedFuture<Void, TransactionCommitFailedException> future = patchTransaction.submit();
351 final PATCHStatusContextHelper status = new PATCHStatusContextHelper();
353 Futures.addCallback(future, new FutureCallback<Void>() {
355 public void onSuccess(@Nullable final Void result) {
356 status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
362 public void onFailure(final Throwable t) {
363 // if commit failed it is global error
364 LOG.error("Http PATCH {} transaction commit has failed", patchContext.getPatchId());
365 status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
366 false, ImmutableList.of(
367 new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, t.getMessage()))));
373 return status.getStatus();
376 // POST configuration
377 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
378 final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
379 final String insert, final String point) {
380 checkPreconditions();
381 return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
382 globalSchema, insert, point);
385 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
386 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
387 final String insert, final String point) {
388 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
389 if (domDataBrokerService.isPresent()) {
390 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
391 payload, mountPoint.getSchemaContext(), insert, point);
393 final String errMsg = "DOM data broker service isn't available for mount point " + path;
395 throw new RestconfDocumentedException(errMsg);
398 // DELETE configuration
399 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
400 final YangInstanceIdentifier path) {
401 checkPreconditions();
402 return deleteDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path);
405 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
406 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
407 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
408 if (domDataBrokerService.isPresent()) {
409 return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path);
411 final String errMsg = "DOM data broker service isn't available for mount point " + path;
413 throw new RestconfDocumentedException(errMsg);
417 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
418 checkPreconditions();
419 if (this.rpcService == null) {
420 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
422 LOG.trace("Invoke RPC {} with input: {}", type, input);
423 return this.rpcService.invokeRpc(type, input);
426 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
427 final ListenerAdapter listener) {
428 checkPreconditions();
430 if (listener.isListening()) {
434 final YangInstanceIdentifier path = listener.getPath();
435 final ListenerRegistration<DOMDataChangeListener> registration = this.domDataBroker.registerDataChangeListener(
436 datastore, path, listener, scope);
438 listener.setRegistration(registration);
441 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
442 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
443 LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
444 final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
445 final ReadDataResult<NormalizedNode<?, ?>> readData = new ReadDataResult<>();
446 final CountDownLatch responseWaiter = new CountDownLatch(1);
448 Futures.addCallback(listenableFuture, new FutureCallback<Optional<NormalizedNode<?, ?>>>() {
451 public void onSuccess(final Optional<NormalizedNode<?, ?>> result) {
452 handlingCallback(null, datastore, path, result, readData);
453 responseWaiter.countDown();
457 public void onFailure(final Throwable t) {
458 responseWaiter.countDown();
459 handlingCallback(t, datastore, path, null, null);
464 responseWaiter.await();
465 } catch (final Exception e) {
466 final String msg = "Problem while waiting for response";
468 throw new RestconfDocumentedException(msg, e);
470 return readData.getResult();
474 * POST data and submit transaction {@link DOMDataReadWriteTransaction}
476 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
477 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
478 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
479 final String insert, final String point) {
480 LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
481 postData(rWTransaction, datastore, path, payload, schemaContext, insert, point);
482 return rWTransaction.submit();
486 * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}
488 private void postDataWithinTransaction(
489 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
490 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
491 LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
492 postData(rWTransaction, datastore, path, payload, schemaContext, null, null);
495 private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
496 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
497 final SchemaContext schemaContext, final String insert, final String point) {
498 if (insert == null) {
499 makeNormalPost(rWTransaction, datastore, path, payload, schemaContext);
501 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
502 checkItemDoesNotExists(rWTransaction, datastore, path);
505 if(schemaNode instanceof ListSchemaNode){
506 final OrderedMapNode readList =
507 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
508 if ((readList == null) || readList.getValue().isEmpty()) {
509 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
511 rWTransaction.delete(datastore, path.getParent().getParent());
512 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
513 makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readList,
517 final OrderedLeafSetNode readLeafList =
518 (OrderedLeafSetNode) readConfigurationData(path.getParent());
519 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
520 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
522 rWTransaction.delete(datastore, path.getParent());
523 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
524 makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readLeafList,
530 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
533 if(schemaNode instanceof ListSchemaNode){
534 final OrderedMapNode readList =
535 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
536 if ((readList == null) || readList.getValue().isEmpty()) {
537 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
539 insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
544 final OrderedLeafSetNode<?> readLeafList =
545 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
546 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
547 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
549 insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
555 if (schemaNode instanceof ListSchemaNode) {
556 final OrderedMapNode readList =
557 (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
558 if ((readList == null) || readList.getValue().isEmpty()) {
559 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
561 insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
566 final OrderedLeafSetNode<?> readLeafList =
567 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
568 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
569 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
571 insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
572 readLeafList, false);
577 throw new RestconfDocumentedException(
578 "Used bad value of insert parameter. Possible values are first, last, before or after, "
579 + "but was: " + insert);
584 private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
585 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
586 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
587 final boolean before) {
588 rWTransaction.delete(datastore, path.getParent().getParent());
589 final InstanceIdentifierContext<?> instanceIdentifier =
590 ControllerContext.getInstance().toInstanceIdentifier(point);
592 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
593 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
602 final NormalizedNode<?, ?> emptySubtree =
603 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
604 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
605 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
607 checkItemDoesNotExists(rWTransaction, datastore, path);
608 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
610 final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
611 checkItemDoesNotExists(rWTransaction, datastore, childPath);
612 rWTransaction.put(datastore, childPath, nodeChild);
617 private void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
618 final LogicalDatastoreType datastore,
619 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
620 final String point, final MapNode readList, final boolean before) {
621 rWTransaction.delete(datastore, path.getParent().getParent());
622 final InstanceIdentifierContext<?> instanceIdentifier =
623 ControllerContext.getInstance().toInstanceIdentifier(point);
625 for (final MapEntryNode mapEntryNode : readList.getValue()) {
626 if (mapEntryNode.getIdentifier()
627 .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
636 final NormalizedNode<?, ?> emptySubtree =
637 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
638 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
639 for (final MapEntryNode mapEntryNode : readList.getValue()) {
641 checkItemDoesNotExists(rWTransaction, datastore, path);
642 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
644 final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
645 checkItemDoesNotExists(rWTransaction, datastore, childPath);
646 rWTransaction.put(datastore, childPath, mapEntryNode);
651 private DataSchemaNode checkListAndOrderedType(final SchemaContext ctx,
652 final YangInstanceIdentifier path) {
653 final YangInstanceIdentifier parent = path.getParent();
654 final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
655 final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
657 if (dataSchemaNode instanceof ListSchemaNode) {
658 if(!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
659 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
661 return dataSchemaNode;
663 if (dataSchemaNode instanceof LeafListSchemaNode) {
664 if(!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
665 throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user leaf-list.");
667 return dataSchemaNode;
669 throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
672 private void makeNormalPost(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
673 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
674 if (payload instanceof MapNode) {
675 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
676 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
677 ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
678 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
679 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
680 checkItemDoesNotExists(rWTransaction, datastore, childPath);
681 rWTransaction.put(datastore, childPath, child);
683 } else if (payload instanceof LeafSetNode) {
684 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
685 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
686 ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
687 for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
688 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
689 checkItemDoesNotExists(rWTransaction, datastore, childPath);
690 rWTransaction.put(datastore, childPath, child);
693 simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
697 private void simplePostPut(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
698 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
699 checkItemDoesNotExists(rWTransaction, datastore, path);
700 ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
701 rWTransaction.put(datastore, path, payload);
705 * Check if item already exists. Throws error if it does NOT already exist.
706 * @param rWTransaction Current transaction
707 * @param store Used datastore
708 * @param path Path to item to verify its existence
710 private void checkItemExists(final DOMDataReadWriteTransaction rWTransaction,
711 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
712 final CountDownLatch responseWaiter = new CountDownLatch(1);
713 final ReadDataResult<Boolean> readData = new ReadDataResult<>();
714 final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
716 Futures.addCallback(future, new FutureCallback<Boolean>() {
718 public void onSuccess(@Nullable final Boolean result) {
719 handlingCallback(null, store, path, Optional.of(result), readData);
720 responseWaiter.countDown();
724 public void onFailure(final Throwable t) {
725 responseWaiter.countDown();
726 handlingCallback(t, store, path, null, null);
731 responseWaiter.await();
732 } catch (final Exception e) {
733 final String msg = "Problem while waiting for response";
735 throw new RestconfDocumentedException(msg, e);
738 if ((readData.getResult() == null) || !readData.getResult()) {
739 final String errMsg = "Operation via Restconf was not executed because data does not exist";
740 LOG.trace("{}:{}", errMsg, path);
741 rWTransaction.cancel();
742 throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL,
743 ErrorTag.DATA_MISSING);
748 * Check if item does NOT already exist. Throws error if it already exists.
749 * @param rWTransaction Current transaction
750 * @param store Used datastore
751 * @param path Path to item to verify its existence
753 private void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,
754 final LogicalDatastoreType store, final YangInstanceIdentifier path) {
755 final CountDownLatch responseWaiter = new CountDownLatch(1);
756 final ReadDataResult<Boolean> readData = new ReadDataResult<>();
757 final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
759 Futures.addCallback(future, new FutureCallback<Boolean>() {
761 public void onSuccess(@Nullable final Boolean result) {
762 handlingCallback(null, store, path, Optional.of(result), readData);
763 responseWaiter.countDown();
767 public void onFailure(final Throwable t) {
768 responseWaiter.countDown();
769 handlingCallback(t, store, path, null, null);
774 responseWaiter.await();
775 } catch (final Exception e) {
776 final String msg = "Problem while waiting for response";
778 throw new RestconfDocumentedException(msg, e);
781 if (readData.getResult()) {
782 final String errMsg = "Operation via Restconf was not executed because data already exists";
783 LOG.trace("{}:{}", errMsg, path);
784 rWTransaction.cancel();
785 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
786 ErrorTag.DATA_EXISTS);
791 * PUT data and submit {@link DOMDataReadWriteTransaction}
796 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
797 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
798 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
799 final String insert, final String point) {
800 LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
801 putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
802 return readWriteTransaction.submit();
806 * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
811 private void putDataWithinTransaction(
812 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
813 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
814 LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
815 putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
818 // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
819 private void putData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
820 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
821 final String insert, final String point) {
822 if (insert == null) {
823 makePut(rWTransaction, datastore, path, payload, schemaContext);
825 final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
826 checkItemDoesNotExists(rWTransaction, datastore, path);
829 if (schemaNode instanceof ListSchemaNode) {
830 final OrderedMapNode readList =
831 (OrderedMapNode) this.readConfigurationData(path.getParent());
832 if ((readList == null) || readList.getValue().isEmpty()) {
833 simplePut(datastore, path, rWTransaction, schemaContext, payload);
835 rWTransaction.delete(datastore, path.getParent());
836 simplePut(datastore, path, rWTransaction, schemaContext, payload);
837 makePut(rWTransaction, datastore, path.getParent(), readList, schemaContext);
840 final OrderedLeafSetNode<?> readLeafList =
841 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
842 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
843 simplePut(datastore, path, rWTransaction, schemaContext, payload);
845 rWTransaction.delete(datastore, path.getParent());
846 simplePut(datastore, path, rWTransaction, schemaContext, payload);
847 makePut(rWTransaction, datastore, path.getParent(), readLeafList,
853 simplePut(datastore, path, rWTransaction, schemaContext, payload);
856 if (schemaNode instanceof ListSchemaNode) {
857 final OrderedMapNode readList =
858 (OrderedMapNode) this.readConfigurationData(path.getParent());
859 if ((readList == null) || readList.getValue().isEmpty()) {
860 simplePut(datastore, path, rWTransaction, schemaContext, payload);
862 insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
866 final OrderedLeafSetNode<?> readLeafList =
867 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
868 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
869 simplePut(datastore, path, rWTransaction, schemaContext, payload);
871 insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
877 if (schemaNode instanceof ListSchemaNode) {
878 final OrderedMapNode readList =
879 (OrderedMapNode) this.readConfigurationData(path.getParent());
880 if ((readList == null) || readList.getValue().isEmpty()) {
881 simplePut(datastore, path, rWTransaction, schemaContext, payload);
883 insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
887 final OrderedLeafSetNode<?> readLeafList =
888 (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
889 if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
890 simplePut(datastore, path, rWTransaction, schemaContext, payload);
892 insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
893 readLeafList, false);
898 throw new RestconfDocumentedException(
899 "Used bad value of insert parameter. Possible values are first, last, before or after, "
900 + "but was: " + insert);
905 private void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
906 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
907 final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
908 final boolean before) {
909 rWTransaction.delete(datastore, path.getParent());
910 final InstanceIdentifierContext<?> instanceIdentifier =
911 ControllerContext.getInstance().toInstanceIdentifier(point);
913 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
914 if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
923 final NormalizedNode<?, ?> emptySubtree =
924 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
925 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
926 for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
928 simplePut(datastore, path, rWTransaction, schemaContext, payload);
930 final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
931 rWTransaction.put(datastore, childPath, nodeChild);
936 private void insertWithPointListPut(final DOMDataReadWriteTransaction rWTransaction,
937 final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
938 final SchemaContext schemaContext, final String point, final OrderedMapNode readList,
939 final boolean before) {
940 rWTransaction.delete(datastore, path.getParent());
941 final InstanceIdentifierContext<?> instanceIdentifier =
942 ControllerContext.getInstance().toInstanceIdentifier(point);
944 for (final MapEntryNode mapEntryNode : readList.getValue()) {
945 if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
954 final NormalizedNode<?, ?> emptySubtree =
955 ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
956 rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
957 for (final MapEntryNode mapEntryNode : readList.getValue()) {
959 simplePut(datastore, path, rWTransaction, schemaContext, payload);
961 final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
962 rWTransaction.put(datastore, childPath, mapEntryNode);
967 private void makePut(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
968 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
969 if (payload instanceof MapNode) {
970 final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
971 writeTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
972 ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
973 for (final MapEntryNode child : ((MapNode) payload).getValue()) {
974 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
975 writeTransaction.put(datastore, childPath, child);
978 simplePut(datastore, path, writeTransaction, schemaContext, payload);
982 private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
983 final DOMDataReadWriteTransaction writeTransaction, final SchemaContext schemaContext,
984 final NormalizedNode<?, ?> payload) {
985 ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
986 writeTransaction.put(datastore, path, payload);
989 private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
990 final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
991 final YangInstanceIdentifier path) {
992 LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
993 checkItemExists(readWriteTransaction, datastore, path);
994 readWriteTransaction.delete(datastore, path);
995 return readWriteTransaction.submit();
998 private void deleteDataWithinTransaction(
999 final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1000 final YangInstanceIdentifier path) {
1001 LOG.trace("Delete {} within Restconf PATCH: {}", datastore.name(), path);
1002 writeTransaction.delete(datastore, path);
1005 private void mergeDataWithinTransaction(
1006 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
1007 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
1008 LOG.trace("Merge {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
1009 ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
1011 // merging is necessary only for lists otherwise we can call put method
1012 if (payload instanceof MapNode) {
1013 writeTransaction.merge(datastore, path, payload);
1015 writeTransaction.put(datastore, path, payload);
1019 public void setDomDataBroker(final DOMDataBroker domDataBroker) {
1020 this.domDataBroker = domDataBroker;
1024 * Helper class for result of transaction commit callback.
1025 * @param <T> Type of result
1027 private final class ReadDataResult<T> {
1034 void setResult(final T result) {
1035 this.result = result;
1040 * Set result from transaction commit callback.
1041 * @param t Throwable if transaction commit failed
1042 * @param datastore Datastore from which data are read
1043 * @param path Path from which data are read
1044 * @param result Result of read from {@code datastore}
1045 * @param readData Result value which will be set
1046 * @param <X> Result type
1048 protected final static <X> void handlingCallback(final Throwable t, final LogicalDatastoreType datastore,
1049 final YangInstanceIdentifier path, final Optional<X> result,
1050 final ReadDataResult<X> readData) {
1052 LOG.warn("Exception by reading {} via Restconf: {}", datastore.name(), path, t);
1053 throw new RestconfDocumentedException("Problem to get data from transaction.", t);
1055 LOG.debug("Reading result data from transaction.");
1056 if (result != null) {
1057 if (result.isPresent()) {
1058 readData.setResult(result.get());
1064 public void registerToListenNotification(final NotificationListenerAdapter listener) {
1065 checkPreconditions();
1067 if (listener.isListening()) {
1071 final SchemaPath path = listener.getSchemaPath();
1072 final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
1073 .registerNotificationListener(listener, path);
1075 listener.setRegistration(registration);
1078 private final class PATCHStatusContextHelper {
1079 PATCHStatusContext status;
1081 public PATCHStatusContext getStatus() {
1085 public void setStatus(final PATCHStatusContext status) {
1086 this.status = status;
1090 private void ensureParentsByMerge(final LogicalDatastoreType store, final YangInstanceIdentifier normalizedPath,
1091 final DOMDataReadWriteTransaction rwTx, final SchemaContext schemaContext) {
1092 final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
1093 YangInstanceIdentifier rootNormalizedPath = null;
1095 final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
1097 while (it.hasNext()) {
1098 final PathArgument pathArgument = it.next();
1099 if (rootNormalizedPath == null) {
1100 rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
1104 normalizedPathWithoutChildArgs.add(pathArgument);
1108 if (normalizedPathWithoutChildArgs.isEmpty()) {
1112 Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
1114 final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
1115 YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
1116 rwTx.merge(store, rootNormalizedPath, parentStructure);