/*
* Copyright (c) 2008-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.server.geo;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.coordinator.client.model.Constants;
import com.emc.storageos.coordinator.client.model.DbVersionInfo;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.db.client.impl.CompositeColumnName;
import com.emc.storageos.db.client.impl.DataObjectType;
import com.emc.storageos.db.client.impl.DbClientContext;
import com.emc.storageos.db.client.impl.DbClientImpl;
import com.emc.storageos.db.client.impl.EncryptionProviderImpl;
import com.emc.storageos.db.client.impl.IndexCleanupList;
import com.emc.storageos.db.client.impl.TypeMap;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.DbKeyspace;
import com.emc.storageos.db.client.model.DbKeyspace.Keyspaces;
import com.emc.storageos.db.common.VdcUtil;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.emc.storageos.services.util.LoggingUtils;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.Row;
import com.netflix.astyanax.model.Rows;
//Suppress Sonar violation of Lazy initialization of static fields should be synchronized
//Junit test will be called in single thread by default, it's safe to ignore this violation
@SuppressWarnings("squid:S2444")
public class DbsvcGeoTestBase {
static {
LoggingUtils.configureIfNecessary("geodbtest-log4j.properties");
}
private static final Logger log = LoggerFactory.getLogger(DbsvcGeoTestBase.class);
protected static CoordinatorClient _coordinator;
protected static EncryptionProviderImpl _encryptionProvider = new EncryptionProviderImpl();
protected static EncryptionProviderImpl _geoEncryptionProvider = new EncryptionProviderImpl();
protected static DbVersionInfo _dbVersionInfo;
protected static TestGeoDbClientImpl _dbClient;
protected static DbSvcRunner geoRunner;
protected static DbSvcRunner localRunner;
@BeforeClass
public static void setup() {
_dbVersionInfo = new DbVersionInfo();
_dbVersionInfo.setSchemaVersion(DbSvcRunner.SVC_VERSION);
geoRunner = new DbSvcRunner(DbSvcRunner.GEODBSVC_CONFIG, Constants.GEODBSVC_NAME);
geoRunner.startCoordinator();
geoRunner.start();
localRunner = new DbSvcRunner(DbSvcRunner.DBSVC_CONFIG, Constants.DBSVC_NAME);
localRunner.start();
// geoRunner.waitUntilStarted(100);
try {
configDbClient();
} catch (Exception e) {
log.error("Error configuring dbclient", e);
return;
}
}
@AfterClass
public static void stop() {
localRunner.stop();
geoRunner.stop();
}
protected static TestGeoDbClientImpl getDbClient() {
try {
TestGeoDbClientImpl dbClient = getDbClientBase();
DbClientContext geoCtx = new DbClientContext();
geoCtx.setClusterName("GeoStorageOS");
geoCtx.setKeyspaceName("GeoStorageOS");
dbClient.setGeoContext(geoCtx);
DbClientContext localCtx = new DbClientContext();
localCtx.setClusterName("StorageOS");
localCtx.setKeyspaceName("StorageOS");
dbClient.setLocalContext(localCtx);
// dbClient.setGeoContext(new DbClientContext());
dbClient.start();
VdcUtil.setDbClient(dbClient);
return dbClient;
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
/**
* Create DbClient to embedded DB
*
* @return
* @throws IOException
* @throws URISyntaxException
*/
protected static void configDbClient() throws URISyntaxException, IOException {
if (_dbClient == null) {
_dbClient = getDbClient();
log.info("DB client started OK");
}
}
protected static TestGeoDbClientImpl getDbClientBase() throws URISyntaxException, IOException {
_coordinator = geoRunner.getCoordinator();
TestGeoDbClientImpl dbClient = new TestGeoDbClientImpl();
dbClient.setCoordinatorClient(_coordinator);
dbClient.setDbVersionInfo(_dbVersionInfo);
dbClient.setBypassMigrationLock(true);
_encryptionProvider.setCoordinator(_coordinator);
dbClient.setEncryptionProvider(_encryptionProvider);
_geoEncryptionProvider.setCoordinator(_coordinator);
_geoEncryptionProvider.setEncryptId("geoid");
dbClient.setGeoEncryptionProvider(_geoEncryptionProvider);
return dbClient;
}
protected static class TestGeoDbClientImpl extends DbClientImpl {
public boolean isItWhereItShouldBe(DataObject dbObj) {
if ((dbObj.getClass().isAnnotationPresent(DbKeyspace.class) &&
Arrays.asList(dbObj.getClass().getAnnotation(DbKeyspace.class).value()).contains(Keyspaces.GLOBAL) && Arrays.asList(
dbObj.getClass().getAnnotation(DbKeyspace.class).value()).contains(Keyspaces.LOCAL))) {
// it's hybrid, check the id
if (dbObj.isGlobal()) {
return (queryObject(geoContext.getKeyspace(), dbObj.getClass(), dbObj.getId()) != null);
} else {
return (queryObject(localContext.getKeyspace(), dbObj.getClass(), dbObj.getId()) != null);
}
} else if (dbObj.getClass().isAnnotationPresent(DbKeyspace.class) &&
Arrays.asList(dbObj.getClass().getAnnotation(DbKeyspace.class).value()).contains(Keyspaces.GLOBAL)) {
return (queryObject(geoContext.getKeyspace(), dbObj.getClass(), dbObj.getId()) != null);
} else {
return (queryObject(localContext.getKeyspace(), dbObj.getClass(), dbObj.getId()) != null);
}
}
private <T extends DataObject> T queryObject(Keyspace ks, Class<T> clazz, URI id)
throws DatabaseException {
DataObjectType doType = TypeMap.getDoType(clazz);
if (doType == null) {
throw new IllegalArgumentException();
}
Row<String, CompositeColumnName> row = queryRowWithAllColumns(ks, clazz, id, doType.getCF());
if (row == null) {
return null;
}
IndexCleanupList cleanList = new IndexCleanupList();
T dataObject = doType.deserialize(clazz, row, cleanList);
return dataObject;
}
private <T> Row<String, CompositeColumnName> queryRowWithAllColumns(Keyspace ks, Class<T> clazz, URI id,
ColumnFamily<String, CompositeColumnName> cf) throws DatabaseException {
List<URI> collection = new ArrayList<URI>(1);
collection.add(id);
Rows<String, CompositeColumnName> result = queryRowsWithAllColumns(ks, clazz, collection, cf);
Row<String, CompositeColumnName> row = result.iterator().next();
if (row.getColumns().size() == 0) {
return null;
}
return row;
}
private <T> Rows<String, CompositeColumnName> queryRowsWithAllColumns(Keyspace ks, Class<T> clazz,
Collection<URI> id, ColumnFamily<String, CompositeColumnName> cf)
throws DatabaseException {
try {
OperationResult<Rows<String, CompositeColumnName>> result =
ks.prepareQuery(cf)
.getKeySlice(convertUriCollection(id))
.execute();
return result.getResult();
} catch (ConnectionException e) {
throw DatabaseException.retryables.connectionFailed(e);
}
}
private Collection<String> convertUriCollection(Collection<URI> uriList) {
List<String> idList = new ArrayList<String>();
Iterator<URI> it = uriList.iterator();
while (it.hasNext()) {
idList.add(it.next().toString());
if (idList.size() > DEFAULT_PAGE_SIZE) {
log.warn("Unbounded database query, request size is over allowed limit({}), " +
"please use corresponding iterative API.", DEFAULT_PAGE_SIZE);
}
}
return idList;
}
}
protected boolean isItWhereItShouldBe(DataObject dbObj) {
return _dbClient.isItWhereItShouldBe(dbObj);
}
}