X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frestconf%2Fimpl%2FBrokerFacade.java;h=95fb9a4826d2b6c5115c31d6dfff066b9a0b4af5;hp=1cc1f783d676ed69f68c959d1024a58349dc51d9;hb=531621aac4cff9d39cbd8668a53bdeba8a0e6d81;hpb=803d525860fbb1974b65ba5605ba5a9dfe1928a4 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 1cc1f783d6..95fb9a4826 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,195 +7,269 @@ */ package org.opendaylight.controller.sal.restconf.impl; +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 java.util.concurrent.Future; - 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.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.rest.impl.RestconfProvider; +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.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +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.NormalizedNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class BrokerFacade implements DataReader { - private final static Logger LOG = LoggerFactory.getLogger( BrokerFacade.class ); +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 ConsumerSession context; + private DOMDataBroker domDataBroker; private BrokerFacade() { } - public void setContext( final ConsumerSession context ) { + public void setContext(final ConsumerSession context) { this.context = context; } - public void setDataService( final DataBrokerService dataService ) { - this.dataService = dataService; - } - public static BrokerFacade getInstance() { return BrokerFacade.INSTANCE; } private void checkPreconditions() { - if( context == null || dataService == null ) { - ResponseException _responseException = new ResponseException( Status.SERVICE_UNAVAILABLE, - RestconfProvider.NOT_INITALIZED_MSG ); - throw _responseException; + if (context == null || domDataBroker == null) { + throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE); } } - @Override - public CompositeNode readConfigurationData( final InstanceIdentifier path ) { - this.checkPreconditions(); - - LOG.trace( "Read Configuration via Restconf: {}", path ); - - return dataService.readConfigurationData( path ); + // READ configuration + public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) { + checkPreconditions(); + return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path); } - public CompositeNode readConfigurationDataBehindMountPoint( final MountInstance mountPoint, - final InstanceIdentifier path ) { - this.checkPreconditions(); - - LOG.trace( "Read Configuration via Restconf: {}", path ); - - return mountPoint.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."); } - @Override - public CompositeNode readOperationalData( final InstanceIdentifier path ) { - this.checkPreconditions(); - - BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path ); - - return dataService.readOperationalData( path ); + // READ operational + public NormalizedNode readOperationalData(final YangInstanceIdentifier path) { + checkPreconditions(); + return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path); } - public CompositeNode readOperationalDataBehindMountPoint( final MountInstance mountPoint, - final InstanceIdentifier path ) { - this.checkPreconditions(); - - BrokerFacade.LOG.trace( "Read Operational 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.readOperationalData( path ); + // PUT configuration + public CheckedFuture commitConfigurationDataPut( + final YangInstanceIdentifier path, final NormalizedNode payload) { + checkPreconditions(); + DataNormalizationOperation rootOp = ControllerContext.getInstance().getRootOperation(); + return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp); } - public RpcResult invokeRpc( final QName type, final CompositeNode payload ) { - this.checkPreconditions(); + public CheckedFuture commitConfigurationDataPut( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload) { + final Optional domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + 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."); + } - final Future> future = context.rpc( type, payload ); + // POST configuration + public CheckedFuture commitConfigurationDataPost( + final YangInstanceIdentifier path, final NormalizedNode payload) { + checkPreconditions(); + DataNormalizationOperation rootOp = ControllerContext.getInstance().getRootOperation(); + return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp); + } - try { - return future.get(); - } - catch( Exception e ) { - throw new ResponseException( e, "Error invoking RPC " + type ); + public CheckedFuture commitConfigurationDataPost( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload) { + final Optional domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + 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 Future> commitConfigurationDataPut( final InstanceIdentifier path, - final CompositeNode payload ) { - this.checkPreconditions(); + // DELETE configuration + public CheckedFuture commitConfigurationDataDelete( + final YangInstanceIdentifier path) { + checkPreconditions(); + return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path); + } - final DataModificationTransaction transaction = dataService.beginTransaction(); - BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path ); - transaction.putConfigurationData( path, payload ); - return transaction.commit(); + 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> commitConfigurationDataPutBehindMountPoint( - final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) { + // RPC + public Future> invokeRpc(final QName type, final CompositeNode payload) { this.checkPreconditions(); - final DataModificationTransaction transaction = mountPoint.beginTransaction(); - BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path ); - transaction.putConfigurationData( path, payload ); - return transaction.commit(); + return context.rpc(type, payload); } - public Future> commitConfigurationDataPost( final InstanceIdentifier path, - final CompositeNode payload) { + public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope, + final ListenerAdapter listener) { this.checkPreconditions(); - 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()); - // FIXME: return correct ietf-restconf:errors -> follow specification - // (http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48) - throw new ResponseException(Status.CONFLICT, errMsg); + if (listener.isListening()) { + return; } - BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path ); - transaction.putConfigurationData( path, payload ); - return transaction.commit(); - } - public Future> commitConfigurationDataPostBehindMountPoint( - final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) { - this.checkPreconditions(); + YangInstanceIdentifier path = listener.getPath(); + final ListenerRegistration registration = domDataBroker.registerDataChangeListener( + datastore, path, listener, scope); + + listener.setRegistration(registration); + } - 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()); - // FIXME: return correct ietf-restconf:errors -> follow specification - // (http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48) - throw new ResponseException(Status.CONFLICT, errMsg); + private NormalizedNode readDataViaTransaction(final DOMDataReadTransaction transaction, + LogicalDatastoreType datastore, 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> commitConfigurationDataDelete( final InstanceIdentifier path ) { - this.checkPreconditions(); + private CheckedFuture postDataViaTransaction( + final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, DataNormalizationOperation root) { + ListenableFuture>> futureDatastoreData = rWTransaction.read(datastore, path); + try { + final Optional> optionalDatastoreData = futureDatastoreData.get(); + if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) { + String errMsg = "Post Configuration via Restconf was not executed because data already exists"; + LOG.trace(errMsg + ":{}", path); + 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); + } - final DataModificationTransaction transaction = dataService.beginTransaction(); - LOG.info( "Delete Configuration via Restconf: {}", path ); - transaction.removeConfigurationData( path ); - return transaction.commit(); + ensureParentsByMerge(datastore, path, rWTransaction, root); + rWTransaction.merge(datastore, path, payload); + LOG.trace("Post " + datastore.name() + " via Restconf: {}", path); + return rWTransaction.submit(); } - public Future> commitConfigurationDataDeleteBehindMountPoint( - final MountInstance mountPoint, final InstanceIdentifier path ) { - this.checkPreconditions(); + private CheckedFuture putDataViaTransaction( + final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, DataNormalizationOperation root) { + LOG.trace("Put " + datastore.name() + " via Restconf: {}", path); + ensureParentsByMerge(datastore, path, writeTransaction, root); + writeTransaction.put(datastore, path, payload); + return writeTransaction.submit(); + } - final DataModificationTransaction transaction = mountPoint.beginTransaction(); - LOG.info( "Delete Configuration via Restconf: {}", path ); - transaction.removeConfigurationData( path ); - return transaction.commit(); + private CheckedFuture deleteDataViaTransaction( + final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore, + 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(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) { + List currentArguments = new ArrayList<>(); + Iterator iterator = normalizedPath.getPathArguments().iterator(); + DataNormalizationOperation currentOp = root; + while (iterator.hasNext()) { + PathArgument currentArg = iterator.next(); + try { + currentOp = currentOp.getChild(currentArg); + } catch (DataNormalizationException e) { + throw new IllegalArgumentException( + String.format("Invalid child encountered in path %s", normalizedPath), e); + } + currentArguments.add(currentArg); + YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments); + + final Boolean exists; + + try { + + CheckedFuture future = rwTx.exists(store, currentPath); + exists = future.checkedGet(); + } catch (ReadFailedException e) { + LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e); + throw new IllegalStateException("Failed to read pre-existing data", e); + } + + if (!exists && iterator.hasNext()) { + rwTx.merge(store, currentPath, currentOp.createDefault(currentArg)); + } } - - InstanceIdentifier path = listener.getPath(); - final ListenerRegistration registration = - dataService.registerDataChangeListener( path, listener ); - - listener.setRegistration( registration ); } }