*/
package org.opendaylight.controller.sal.restconf.impl;
-import com.google.common.util.concurrent.Futures;
-
-import java.util.concurrent.Future;
-
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
import javax.ws.rs.core.Response.Status;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BrokerFacade implements DataReader<YangInstanceIdentifier, CompositeNode> {
+public class BrokerFacade {
private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
private final static BrokerFacade INSTANCE = new BrokerFacade();
-
- private volatile DataBrokerService dataService;
+ private volatile DOMRpcService rpcService;
private volatile ConsumerSession context;
+ private DOMDataBroker domDataBroker;
private BrokerFacade() {
}
- public void setContext(final ConsumerSession context) {
- this.context = context;
+ public void setRpcService(final DOMRpcService router) {
+ rpcService = router;
}
- public void setDataService(final DataBrokerService dataService) {
- this.dataService = dataService;
+ public void setContext(final ConsumerSession context) {
+ this.context = context;
}
public static BrokerFacade getInstance() {
}
private void checkPreconditions() {
- if (context == null || dataService == null) {
+ if (context == null || domDataBroker == null) {
throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
}
}
- @Override
- public CompositeNode readConfigurationData(final YangInstanceIdentifier path) {
- this.checkPreconditions();
-
- LOG.trace("Read Configuration via Restconf: {}", path);
+ // READ configuration
+ public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
+ checkPreconditions();
+ return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
+ }
- return dataService.readConfigurationData(path);
+ public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
}
- public CompositeNode readConfigurationDataBehindMountPoint(final MountInstance mountPoint,
- final YangInstanceIdentifier path) {
- this.checkPreconditions();
+ // READ operational
+ public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
+ checkPreconditions();
+ return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
+ }
- LOG.trace("Read Configuration via Restconf: {}", path);
+ public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
+ }
- return mountPoint.readConfigurationData(path);
+ // PUT configuration
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ checkPreconditions();
+ final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
+ return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
}
- @Override
- public CompositeNode readOperationalData(final YangInstanceIdentifier path) {
- this.checkPreconditions();
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
+ return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
+ payload, rootOp);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
+ }
- BrokerFacade.LOG.trace("Read Operational via Restconf: {}", path);
+ // POST configuration
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ checkPreconditions();
+ final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
+ return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
+ }
- return dataService.readOperationalData(path);
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
+ return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
+ payload, rootOp);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
}
- public CompositeNode readOperationalDataBehindMountPoint(final MountInstance mountPoint,
+ // DELETE configuration
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
final YangInstanceIdentifier path) {
- this.checkPreconditions();
-
- BrokerFacade.LOG.trace("Read Operational via Restconf: {}", path);
-
- return mountPoint.readOperationalData(path);
+ checkPreconditions();
+ return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
}
- public Future<RpcResult<CompositeNode>> invokeRpc(final QName type, final CompositeNode payload) {
- this.checkPreconditions();
-
- return context.rpc(type, payload);
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPut(final YangInstanceIdentifier path,
- final CompositeNode payload) {
- this.checkPreconditions();
-
- final DataModificationTransaction transaction = dataService.beginTransaction();
- BrokerFacade.LOG.trace("Put Configuration via Restconf: {}", path);
- transaction.putConfigurationData(path, payload);
- return transaction.commit();
+ // RPC
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+ checkPreconditions();
+ if (rpcService == null) {
+ throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
+ }
+ return rpcService.invokeRpc(type, input);
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPutBehindMountPoint(
- final MountInstance mountPoint, final YangInstanceIdentifier path, final CompositeNode payload) {
- this.checkPreconditions();
+ public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
+ final ListenerAdapter listener) {
+ checkPreconditions();
- final DataModificationTransaction transaction = mountPoint.beginTransaction();
- BrokerFacade.LOG.trace("Put Configuration via Restconf: {}", path);
- transaction.putConfigurationData(path, payload);
- return transaction.commit();
- }
+ if (listener.isListening()) {
+ return;
+ }
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPost(final YangInstanceIdentifier path,
- final CompositeNode payload) {
- this.checkPreconditions();
+ final YangInstanceIdentifier path = listener.getPath();
+ final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
+ datastore, path, listener, scope);
- final DataModificationTransaction transaction = dataService.beginTransaction();
- /* check for available Node in Configuration DataStore by path */
- CompositeNode availableNode = transaction.readConfigurationData(path);
- if (availableNode != null) {
- String errMsg = "Post Configuration via Restconf was not executed because data already exists";
- BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
+ listener.setRegistration(registration);
+ }
- throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
- ErrorTag.DATA_EXISTS);
+ private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
+ final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
+ LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
+ if (listenableFuture != null) {
+ Optional<NormalizedNode<?, ?>> optional;
+ try {
+ LOG.debug("Reading result data from transaction.");
+ optional = listenableFuture.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
+
+ }
+ if (optional != null) {
+ if (optional.isPresent()) {
+ return optional.get();
+ }
+ }
}
- BrokerFacade.LOG.trace("Post Configuration via Restconf: {}", path);
- transaction.putConfigurationData(path, payload);
- return transaction.commit();
+ return null;
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPostBehindMountPoint(
- final MountInstance mountPoint, final YangInstanceIdentifier path, final CompositeNode payload) {
- this.checkPreconditions();
-
- final DataModificationTransaction transaction = mountPoint.beginTransaction();
- /* check for available Node in Configuration DataStore by path */
- CompositeNode availableNode = transaction.readConfigurationData(path);
- if (availableNode != null) {
- String errMsg = "Post Configuration via Restconf was not executed because data already exists";
- BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
+ private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
+ final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier parentPath, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
+ // FIXME: This is doing correct post for container and list children
+ // not sure if this will work for choice case
+ final YangInstanceIdentifier path;
+ if(payload instanceof MapEntryNode) {
+ path = parentPath.node(payload.getNodeType()).node(payload.getIdentifier());
+ } else {
+ path = parentPath.node(payload.getIdentifier());
+ }
- throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
- ErrorTag.DATA_EXISTS);
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
+ try {
+ final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
+ if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
+ final String errMsg = "Post Configuration via Restconf was not executed because data already exists";
+ LOG.trace(errMsg + ":{}", path);
+ rWTransaction.cancel();
+ throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
+ ErrorTag.DATA_EXISTS);
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
}
- BrokerFacade.LOG.trace("Post Configuration via Restconf: {}", path);
- transaction.putConfigurationData(path, payload);
- return transaction.commit();
- }
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataDelete(final YangInstanceIdentifier path) {
- this.checkPreconditions();
- return deleteDataAtTarget(path, dataService.beginTransaction());
+ ensureParentsByMerge(datastore, path, rWTransaction, root);
+ rWTransaction.merge(datastore, path, payload);
+ LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
+ return rWTransaction.submit();
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataDeleteBehindMountPoint(
- final MountInstance mountPoint, final YangInstanceIdentifier path) {
- this.checkPreconditions();
- return deleteDataAtTarget(path, mountPoint.beginTransaction());
+ private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
+ final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
+ LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
+ ensureParentsByMerge(datastore, path, writeTransaction, root);
+ writeTransaction.put(datastore, path, payload);
+ return writeTransaction.submit();
}
- private Future<RpcResult<TransactionStatus>> deleteDataAtTarget(final YangInstanceIdentifier path,
- final DataModificationTransaction transaction) {
- LOG.info("Delete Configuration via Restconf: {}", path);
- CompositeNode redDataAtPath = transaction.readConfigurationData(path);
- if (redDataAtPath == null) {
- return Futures.immediateFuture(RpcResultBuilder.<TransactionStatus>
- success(TransactionStatus.COMMITED).build());
- }
- transaction.removeConfigurationData(path);
- return transaction.commit();
+ private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
+ final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path) {
+ LOG.trace("Delete " + datastore.name() + " via Restconf: {}", path);
+ writeTransaction.delete(datastore, path);
+ return writeTransaction.submit();
}
- public void registerToListenDataChanges(final ListenerAdapter listener) {
- this.checkPreconditions();
+ public void setDomDataBroker(final DOMDataBroker domDataBroker) {
+ this.domDataBroker = domDataBroker;
+ }
- if (listener.isListening()) {
- return;
+ private final void ensureParentsByMerge(final LogicalDatastoreType store,
+ final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx,
+ final DataNormalizationOperation<?> root) {
+ final List<PathArgument> currentArguments = new ArrayList<>();
+ final Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ DataNormalizationOperation<?> currentOp = root;
+ while (iterator.hasNext()) {
+ final PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (final DataNormalizationException e) {
+ rwTx.cancel();
+ throw new IllegalArgumentException(
+ String.format("Invalid child encountered in path %s", normalizedPath), e);
+ }
+ currentArguments.add(currentArg);
+ final YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
+
+ final Boolean exists;
+
+ try {
+
+ final CheckedFuture<Boolean, ReadFailedException> future = rwTx.exists(store, currentPath);
+ exists = future.checkedGet();
+ } catch (final ReadFailedException e) {
+ LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
+ rwTx.cancel();
+ throw new IllegalStateException("Failed to read pre-existing data", e);
+ }
+
+ if (!exists && iterator.hasNext()) {
+ rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
}
-
- YangInstanceIdentifier path = listener.getPath();
- final ListenerRegistration<DataChangeListener> registration = dataService.registerDataChangeListener(path,
- listener);
-
- listener.setRegistration(registration);
}
}