X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frestconf%2Fimpl%2FBrokerFacade.java;h=0378ae40ee39aa09a2c2a40199b4a5161964bd50;hb=c8023e980b7f421d0af06b99fd66380b72d3a8e0;hp=861aaac3d81552957b5798c3b775501d830fadce;hpb=de3e413b633b7555ae8f3fe2ec163dbb7dda5da8;p=controller.git diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java index 861aaac3d8..0378ae40ee 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java @@ -7,48 +7,62 @@ */ 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.base.Preconditions; +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.TransactionCommitFailedException; +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.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BrokerFacade implements DataReader { +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() { @@ -56,145 +70,216 @@ public class BrokerFacade implements DataReader 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 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 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 commitConfigurationDataPut( + final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode payload) { + checkPreconditions(); + return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema); } - @Override - public CompositeNode readOperationalData(final YangInstanceIdentifier path) { - this.checkPreconditions(); + public CheckedFuture commitConfigurationDataPut( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload) { + final Optional domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path, + payload, mountPoint.getSchemaContext()); + } + 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 commitConfigurationDataPost( + final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode payload) { + checkPreconditions(); + return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema); + } - return dataService.readOperationalData(path); + public CheckedFuture commitConfigurationDataPost( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload) { + final Optional domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path, + payload, mountPoint.getSchemaContext()); + } + throw new RestconfDocumentedException("DOM data broker service isn't available for mount point."); } - public CompositeNode readOperationalDataBehindMountPoint(final MountInstance mountPoint, + // DELETE configuration + public CheckedFuture 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> invokeRpc(final QName type, final CompositeNode payload) { - this.checkPreconditions(); - - return context.rpc(type, payload); + public CheckedFuture commitConfigurationDataDelete( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path) { + final Optional 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> 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 invokeRpc(final SchemaPath type, final NormalizedNode input) { + checkPreconditions(); + if (rpcService == null) { + throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE); + } + return rpcService.invokeRpc(type, input); } - public Future> 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> commitConfigurationDataPost(final YangInstanceIdentifier path, - final CompositeNode payload) { - this.checkPreconditions(); + final YangInstanceIdentifier path = listener.getPath(); + final ListenerRegistration 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>> listenableFuture = transaction.read(datastore, path); + if (listenableFuture != null) { + Optional> 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> 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 postDataViaTransaction( + final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier parentPath, final NormalizedNode payload, final SchemaContext schemaContext) { + // 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>> futureDatastoreData = rWTransaction.read(datastore, path); + try { + final Optional> 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(); + + ensureParentsByMerge(datastore, path, rWTransaction, schemaContext); + rWTransaction.merge(datastore, path, payload); + LOG.trace("Post " + datastore.name() + " via Restconf: {}", path); + return rWTransaction.submit(); } - public Future> commitConfigurationDataDelete(final YangInstanceIdentifier path) { - this.checkPreconditions(); - return deleteDataAtTarget(path, dataService.beginTransaction()); + private CheckedFuture putDataViaTransaction( + final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, final SchemaContext schemaContext) { + LOG.trace("Put " + datastore.name() + " via Restconf: {}", path); + ensureParentsByMerge(datastore, path, writeTransaction, schemaContext); + writeTransaction.put(datastore, path, payload); + return writeTransaction.submit(); } - public Future> commitConfigurationDataDeleteBehindMountPoint( - final MountInstance mountPoint, final YangInstanceIdentifier path) { - this.checkPreconditions(); - return deleteDataAtTarget(path, mountPoint.beginTransaction()); + private CheckedFuture 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(); } - private Future> 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. - success(TransactionStatus.COMMITED).build()); - } - transaction.removeConfigurationData(path); - return transaction.commit(); + public void setDomDataBroker(final DOMDataBroker domDataBroker) { + this.domDataBroker = domDataBroker; } - public void registerToListenDataChanges(final ListenerAdapter listener) { - this.checkPreconditions(); + private void ensureParentsByMerge(final LogicalDatastoreType store, + final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx, final SchemaContext schemaContext) { + final List normalizedPathWithoutChildArgs = new ArrayList<>(); + YangInstanceIdentifier rootNormalizedPath = null; - if (listener.isListening()) { + final Iterator it = normalizedPath.getPathArguments().iterator(); + + while(it.hasNext()) { + final PathArgument pathArgument = it.next(); + if(rootNormalizedPath == null) { + rootNormalizedPath = YangInstanceIdentifier.create(pathArgument); + } + + // Skip last element, its not a parent + if(it.hasNext()) { + normalizedPathWithoutChildArgs.add(pathArgument); + } + } + + // No parent structure involved, no need to ensure parents + if(normalizedPathWithoutChildArgs.isEmpty()) { return; } - YangInstanceIdentifier path = listener.getPath(); - final ListenerRegistration registration = dataService.registerDataChangeListener(path, - listener); + Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received"); - listener.setRegistration(registration); + final NormalizedNode parentStructure = + ImmutableNodes.fromInstanceId(schemaContext, YangInstanceIdentifier.create(normalizedPathWithoutChildArgs)); + rwTx.merge(store, rootNormalizedPath, parentStructure); } }