*/
package org.opendaylight.lispflowmapping.dsbackend;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.util.concurrent.CheckedFuture;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
-import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
-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.TransactionChain;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
import org.opendaylight.lispflowmapping.lisp.util.LispAddressStringifier;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.api.Transaction;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
+import org.opendaylight.mdsal.binding.api.TransactionChainListener;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.XtrId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingDatabase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.AuthenticationKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.Mapping;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.db.instance.mapping.XtrIdMapping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.database.LastUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.database.LastUpdatedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.database.VirtualNetworkIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
* Stores data coming from the mapping database RPCs into the MD-SAL datastore.
*
* @author Lorand Jakab
- *
*/
public class DataStoreBackEnd implements TransactionChainListener {
- protected static final Logger LOG = LoggerFactory.getLogger(DataStoreBackEnd.class);
+ private static final Logger LOG = LoggerFactory.getLogger(DataStoreBackEnd.class);
private static final InstanceIdentifier<MappingDatabase> DATABASE_ROOT =
InstanceIdentifier.create(MappingDatabase.class);
- private BindingTransactionChain txChain;
+ private static final InstanceIdentifier<LastUpdated> LAST_UPDATED =
+ InstanceIdentifier.create(MappingDatabase.class).child(LastUpdated.class);
+
+ private final TransactionChain configTxChain;
+ private final TransactionChain operTxChain;
+ @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "Non-final for mocking")
public DataStoreBackEnd(DataBroker broker) {
- this.txChain = broker.createTransactionChain(this);
+ LOG.debug("Creating DataStoreBackEnd transaction chain...");
+ configTxChain = broker.createMergingTransactionChain(this);
+ operTxChain = broker.createMergingTransactionChain(this);
}
-
public void addAuthenticationKey(AuthenticationKey authenticationKey) {
if (LOG.isDebugEnabled()) {
LOG.debug("MD-SAL: Adding authentication key '{}' for {}",
// This method assumes that it is only called for southbound originated Map-Registers
public void addXtrIdMapping(XtrIdMapping mapping) {
XtrId xtrId = mapping.getMappingRecord().getXtrId();
- Preconditions.checkNotNull(xtrId, "Make sure you only call addXtrIdMapping when the MappingRecord "
- + "contains an xTR-ID");
+ requireNonNull(xtrId, "Make sure you only call addXtrIdMapping when the MappingRecord contains an xTR-ID");
if (LOG.isDebugEnabled()) {
LOG.debug("MD-SAL: Adding mapping for {}, xTR-ID {}",
LispAddressStringifier.getString(mapping.getMappingRecord().getEid()), xtrId);
public void removeXtrIdMapping(XtrIdMapping mapping) {
XtrId xtrId = mapping.getMappingRecord().getXtrId();
- Preconditions.checkNotNull(xtrId, "Make sure you only call addXtrIdMapping when the MappingRecord "
- + "contains an xTR-ID");
+ requireNonNull(xtrId, "Make sure you only call addXtrIdMapping when the MappingRecord contains an xTR-ID");
if (LOG.isDebugEnabled()) {
LOG.debug("MD-SAL: Removing mapping for {}, xTR-ID {}",
LispAddressStringifier.getString(mapping.getMappingRecord().getEid()), xtrId);
}
public List<Mapping> getAllMappings(LogicalDatastoreType logicalDataStore) {
- LOG.debug("MD-SAL: Get all mappings from datastore");
- List<Mapping> mappings = new ArrayList<Mapping>();
+ LOG.debug("MD-SAL: Get all mappings from {} datastore",
+ logicalDataStore == LogicalDatastoreType.CONFIGURATION ? "config" : "operational");
+ List<Mapping> mappings = new ArrayList<>();
MappingDatabase mdb = readTransaction(DATABASE_ROOT, logicalDataStore);
- if (mdb != null) {
- for (VirtualNetworkIdentifier id : mdb.getVirtualNetworkIdentifier()) {
- List<Mapping> ms = id.getMapping();
+ if (mdb != null && mdb.getVirtualNetworkIdentifier() != null) {
+ for (VirtualNetworkIdentifier id : mdb.nonnullVirtualNetworkIdentifier().values()) {
+ List<Mapping> ms = new ArrayList<>(id.nonnullMapping().values());
if (ms != null) {
mappings.addAll(ms);
}
public List<AuthenticationKey> getAllAuthenticationKeys() {
LOG.debug("MD-SAL: Get all authentication keys from datastore");
- List<AuthenticationKey> authKeys = new ArrayList<AuthenticationKey>();
+ List<AuthenticationKey> authKeys = new ArrayList<>();
MappingDatabase mdb = readTransaction(DATABASE_ROOT, LogicalDatastoreType.CONFIGURATION);
- if (mdb != null) {
- for (VirtualNetworkIdentifier id : mdb.getVirtualNetworkIdentifier()) {
- List<AuthenticationKey> keys = id.getAuthenticationKey();
+ if (mdb != null && mdb.getVirtualNetworkIdentifier() != null) {
+ for (VirtualNetworkIdentifier id : mdb.nonnullVirtualNetworkIdentifier().values()) {
+ List<AuthenticationKey> keys = new ArrayList<>(id.nonnullAuthenticationKey().values());
if (keys != null) {
authKeys.addAll(keys);
}
return authKeys;
}
+ public void saveLastUpdateTimestamp() {
+ Long timestamp = System.currentTimeMillis();
+ LOG.debug("MD-SAL: Saving last update timestamp to operational datastore: {}", new Date(timestamp).toString());
+ writePutTransaction(LAST_UPDATED, new LastUpdatedBuilder().setLastUpdated(timestamp).build(),
+ LogicalDatastoreType.OPERATIONAL, "Couldn't save last update timestamp to operational datastore");
+ }
+
+ public void removeLastUpdateTimestamp() {
+ LOG.debug("MD-SAL: Removing last update timestamp from operational datastore");
+ deleteTransaction(LAST_UPDATED, LogicalDatastoreType.OPERATIONAL,
+ "Couldn't remove last update timestamp from operational datastore");
+ }
+
+ public Long getLastUpdateTimestamp() {
+ LastUpdated lastUpdated = readTransaction(LAST_UPDATED, LogicalDatastoreType.OPERATIONAL);
+ if (lastUpdated != null && lastUpdated.getLastUpdated() != null) {
+ Long timestamp = lastUpdated.getLastUpdated();
+ LOG.debug("MD-SAL: Retrieved last update timestamp from operational datastore: {}",
+ new Date(timestamp).toString());
+ return timestamp;
+ } else {
+ LOG.debug("MD-SAL: Couldn't retrieve last update timestamp from operational datastore");
+ return null;
+ }
+ }
+
private static LogicalDatastoreType getDestinationDatastore(Mapping mapping) {
return mapping.getOrigin().equals(MappingOrigin.Southbound) ? LogicalDatastoreType.OPERATIONAL
: LogicalDatastoreType.CONFIGURATION;
}
+ private TransactionChain getChain(LogicalDatastoreType logicalDatastoreType) {
+ return switch (logicalDatastoreType) {
+ case CONFIGURATION -> configTxChain;
+ case OPERATIONAL -> operTxChain;
+ };
+ }
+
private <U extends org.opendaylight.yangtools.yang.binding.DataObject> void writePutTransaction(
InstanceIdentifier<U> addIID, U data, LogicalDatastoreType logicalDatastoreType, String errMsg) {
- WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
- writeTx.put(logicalDatastoreType, addIID, data, true);
- Futures.addCallback(writeTx.submit(), new FutureCallback<Void>() {
+ WriteTransaction writeTx = getChain(logicalDatastoreType).newWriteOnlyTransaction();
+ // TODO: is is a utility method, hence we do not have enough lifecycle knowledge to use plain put()
+ writeTx.mergeParentStructurePut(logicalDatastoreType, addIID, data);
+ writeTx.commit().addCallback(new FutureCallback<CommitInfo>() {
- public void onSuccess(Void result) {
+ @Override
+ public void onSuccess(CommitInfo result) {
}
+ @Override
public void onFailure(Throwable throwable) {
LOG.error("Transaction failed:", throwable);
}
- });
+ }, MoreExecutors.directExecutor());
}
private <U extends org.opendaylight.yangtools.yang.binding.DataObject> U readTransaction(
InstanceIdentifier<U> readIID, LogicalDatastoreType logicalDatastoreType) {
- ReadOnlyTransaction readTx = txChain.newReadOnlyTransaction();
- CheckedFuture<Optional<U>, ReadFailedException> readFuture = readTx.read(logicalDatastoreType, readIID);
- readTx.close();
+ final ListenableFuture<Optional<U>> readFuture;
+ try (ReadTransaction readTx = getChain(logicalDatastoreType).newReadOnlyTransaction()) {
+ readFuture = readTx.read(logicalDatastoreType, readIID);
+ }
try {
- Optional<U> optionalDataObject = readFuture.checkedGet();
+ Optional<U> optionalDataObject = readFuture.get();
if (optionalDataObject != null && optionalDataObject.isPresent()) {
- return optionalDataObject.get();
+ return optionalDataObject.orElseThrow();
} else {
LOG.debug("{}: Failed to read", Thread.currentThread().getStackTrace()[1]);
}
- } catch (ReadFailedException e) {
+ } catch (InterruptedException | ExecutionException e) {
LOG.warn("Failed to ....", e);
}
return null;
private <U extends org.opendaylight.yangtools.yang.binding.DataObject> void deleteTransaction(
InstanceIdentifier<U> deleteIID, LogicalDatastoreType logicalDatastoreType, String errMsg) {
-
- WriteTransaction writeTx = txChain.newWriteOnlyTransaction();
+ WriteTransaction writeTx = getChain(logicalDatastoreType).newWriteOnlyTransaction();
writeTx.delete(logicalDatastoreType, deleteIID);
- Futures.addCallback(writeTx.submit(), new FutureCallback<Void>() {
-
- public void onSuccess(Void result) {
+ writeTx.commit().addCallback(new FutureCallback<CommitInfo>() {
+ @Override
+ public void onSuccess(CommitInfo result) {
}
+ @Override
public void onFailure(Throwable throwable) {
LOG.error("Transaction failed:", throwable);
}
- });
+ }, MoreExecutors.directExecutor());
}
- public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction,
- Throwable cause) {
+ @Override
+ public void onTransactionChainFailed(TransactionChain chain, Transaction transaction, Throwable cause) {
LOG.error("Broken chain {} in DataStoreBackEnd, transaction {}, cause {}", chain, transaction.getIdentifier(),
- cause);
+ cause.getMessage());
}
- public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
+ @Override
+ public void onTransactionChainSuccessful(TransactionChain chain) {
LOG.info("DataStoreBackEnd closed successfully, chain {}", chain);
}
+
+ public void closeTransactionChain() {
+ LOG.debug("Closing DataStoreBackEnd transaction chain...");
+ configTxChain.close();
+ operTxChain.close();
+ }
}