/*
* Copyright (c) 2008-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.server.geo;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbAggregatorItf;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.impl.ColumnField;
import com.emc.storageos.db.client.impl.ColumnValue;
import com.emc.storageos.db.client.impl.CompositeColumnName;
import com.emc.storageos.db.client.impl.DataObjectType;
import com.emc.storageos.db.client.impl.TypeMap;
import com.emc.storageos.db.client.model.AuthnProvider;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.FileShare;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.Project;
import com.emc.storageos.db.client.model.TenantOrg;
import com.emc.storageos.db.client.model.Vcenter;
import com.emc.storageos.db.exceptions.DatabaseException;
import com.netflix.astyanax.model.Column;
import com.netflix.astyanax.model.Row;
//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 DbClientGeoTest extends DbsvcGeoTestBase {
private static final Logger _logger = LoggerFactory.getLogger(DbClientGeoTest.class);
private static List<FileShare> fsList;
private static List<Project> pjList;
private static TenantOrg rootTenant;
private static List<DataObject> dbObjList;
@Before
public void testSetup() {
DbClient dbClient = getDbClient();
boolean rootTenantExists = isRootTenantExist(dbClient);
int retryCount = 0;
while (!rootTenantExists && retryCount < 30) {
// sleep and try again
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// should be there by now
rootTenantExists = isRootTenantExist(dbClient);
retryCount++;
}
Assert.assertTrue(rootTenantExists);
rootTenant = getRootTenant();
Assert.assertFalse(rootTenant == null);
fsList = new ArrayList<FileShare>();
pjList = new ArrayList<Project>();
dbObjList = addDataObjects(rootTenant, fsList, pjList);
}
@After
public void teardown() {
DbClient dbClient = getDbClient();
for (FileShare fs : fsList) {
dbClient.removeObject(fs);
}
for (Project pj : pjList) {
dbClient.removeObject(pj);
}
}
/**
* Root tenant is moved from localdb to geodb, so we need to unit test here.
*
* @throws Exception
*/
@Test
public void testRootTenantExists() throws Exception {
_logger.info("Starting testRootTenantExists test");
URIQueryResultList tenants = new URIQueryResultList();
getDbClient().queryByConstraint(
ContainmentConstraint.Factory.getTenantOrgSubTenantConstraint(URI.create(TenantOrg.NO_PARENT)),
tenants);
Assert.assertTrue(tenants.iterator().hasNext());
}
@Test
public void testLocalKeyspace() throws Exception {
_logger.info("Starting testLocalKeyspace");
DbClient dbClient = getDbClient();
String altId = UUID.randomUUID().toString();
// persist
FileShare fs = new FileShare();
fs.setId(URIUtil.createId(FileShare.class));
fs.setLabel("test");
fs.setNativeGuid(altId);
dbClient.createObject(fs);
// verify
FileShare fsQuery = dbClient.queryObject(FileShare.class, fs.getId());
Assert.assertNotNull(fsQuery);
Assert.assertTrue(fsQuery.getId().equals(fs.getId()));
Assert.assertTrue(isItWhereItShouldBe(fsQuery));
Assert.assertTrue(getDbClient().queryByType(FileShare.class, true).iterator().hasNext());
dbClient.removeObject(fs);
Assert.assertNull(dbClient.queryObject(FileShare.class, fs.getId()));
}
@Test
public void testGlobalOnlyKeyspace() {
_logger.info("Starting testGlobalOnlyKeyspace");
DbClient dbClient = getDbClient();
Project pj = new Project();
pj.setId(URIUtil.createId(Project.class));
Assert.assertTrue(pj.getId().toString().contains("global"));
String label = String.format("Test Label");
pj.setLabel(label);
pj.setTenantOrg(new NamedURI(rootTenant.getId(), label));
dbClient.createObject(pj);
Project queriedPj = dbClient.queryObject(Project.class, pj.getId());
Assert.assertNotNull(queriedPj);
Assert.assertTrue(queriedPj.getId().equals(pj.getId()));
Assert.assertTrue(isItWhereItShouldBe(queriedPj));
Assert.assertTrue(getDbClient().queryByType(Project.class, true).iterator().hasNext());
dbClient.removeObject(pj);
Assert.assertNull(dbClient.queryObject(Project.class, pj.getId()));
}
@Test
public void testCreateRemoveObject() {
_logger.info("Starting testCreateRemoveObject");
for (DataObject obj : dbObjList) {
Assert.assertTrue(isItWhereItShouldBe(obj));
}
List<DataObject> dbObjList = addDataObjects(rootTenant, null, null);
DbClient dbClient = getDbClient();
for (DataObject obj : dbObjList) {
Assert.assertNotNull(dbClient.queryObject(obj.getClass(), obj.getId()));
}
for (FileShare fs : fsList) {
Assert.assertFalse(dbClient.queryObject(FileShare.class, fs.getId()).isGlobal());
}
for (Project pj : pjList) {
Assert.assertTrue(dbClient.queryObject(Project.class, pj.getId()).isGlobal());
}
dbClient.removeObject(dbObjList.toArray(new DataObject[dbObjList.size()]));
for (DataObject obj : dbObjList) {
Assert.assertNull(dbClient.queryObject(obj.getClass(), obj.getId()));
}
}
@Test
public void testQueryByType() {
_logger.info("Starting testQueryByType");
DbClient dbClient = getDbClient();
internalTestQueryByType(FileShare.class, dbClient, fsList.size(), false);
internalTestQueryByType(Project.class, dbClient, pjList.size(), false);
}
private <T extends DataObject> void internalTestQueryByType(Class<T> clazz, DbClient dbClient, int expectedCount, boolean geo) {
List<URI> queryiedObjList = geo ? dbClient.queryByType(clazz, true) : dbClient.queryByType(clazz, true);
Assert.assertEquals(expectedCount, count(queryiedObjList));
}
@Test
public void testQueryObject() {
_logger.info("Starting testQueryObject");
DbClient dbClient = getDbClient();
internalTestQueryObject(FileShare.class, dbClient, fsList);
internalTestQueryObject(Project.class, dbClient, pjList);
}
private <T extends DataObject> void internalTestQueryObject(Class<T> clazz, DbClient dbClient, Collection<T> objList) {
List<URI> idList = new ArrayList<URI>();
for (T fs : objList) {
idList.add(fs.getId());
}
// add a fake id to the list
idList.add(URIUtil.createId(clazz));
List<T> queryiedObjList = dbClient.queryObject(clazz, idList);
Assert.assertTrue(count(queryiedObjList) == objList.size());
}
@Test
public void testQueryObjectField() {
_logger.info("Starting testQueryObjectField");
DbClient dbClient = getDbClient();
internalTestQueryObjectField(FileShare.class, dbClient, fsList);
internalTestQueryObjectField(Project.class, dbClient, pjList);
}
private <T extends DataObject> void internalTestQueryObjectField(Class<T> clazz, DbClient dbClient, Collection<T> objList) {
String fieldName = "label";
List<URI> idList = new ArrayList<URI>();
for (T fs : objList) {
idList.add(fs.getId());
}
// add a fake id to the list
idList.add(URIUtil.createId(clazz));
List<T> queryiedObjList = dbClient.queryObjectField(clazz, fieldName, idList);
Assert.assertTrue(count(queryiedObjList) == objList.size());
for (T obj : queryiedObjList) {
Assert.assertFalse(obj.getLabel() == null);
}
}
private <T> int count(List<T> queryiedObjList) {
int count = 0;
Iterator<T> itr = queryiedObjList.iterator();
while (itr.hasNext()) {
itr.next();
count++;
}
return count;
}
@Test
public void testQueryObjectFields() {
_logger.info("Starting testQueryObjectField");
DbClient dbClient = getDbClient();
internalTestQueryObjectFields(FileShare.class, dbClient, fsList);
internalTestQueryObjectFields(Project.class, dbClient, pjList);
}
private <T extends DataObject> void internalTestQueryObjectFields(Class<T> clazz, DbClient dbClient, Collection<T> objList) {
Set<String> fieldNames = new HashSet<String>();
fieldNames.add("label");
fieldNames.add("creationTime");
List<URI> idList = new ArrayList<URI>();
for (T fs : objList) {
idList.add(fs.getId());
}
// add a fake id to the list
idList.add(URIUtil.createId(clazz));
List<T> queryiedObjList = new ArrayList<T>();
queryiedObjList.addAll(dbClient.queryObjectFields(clazz, fieldNames, idList));
Assert.assertTrue(count(queryiedObjList) == objList.size());
for (T obj : queryiedObjList) {
Assert.assertFalse(obj.getLabel() == null);
Assert.assertFalse(obj.getCreationTime() == null);
}
}
@Test
public void testAggregateObjectField() {
internalTestAggregateObjectField(FileShare.class, fsList);
internalTestAggregateObjectField(Project.class, pjList);
}
private <T extends DataObject> void internalTestAggregateObjectField(Class<T> clazz, List<T> objs) {
DbClient dbClient = getDbClient();
List<URI> ids = new ArrayList<URI>();
for (DataObject obj : objs) {
ids.add(obj.getId());
}
TestAggregator tstAggr = new TestAggregator(clazz, "label");
dbClient.aggregateObjectField(clazz, ids.iterator(), tstAggr);
List<String> results = tstAggr.getResults();
Assert.assertTrue(results.size() == objs.size());
for (DataObject obj : objs) {
Assert.assertTrue(results.contains(obj.getLabel()));
}
}
public static class TestAggregator implements DbAggregatorItf {
private List<String> _list;
private String _field;
private Class<? extends DataObject> _clazz;
public TestAggregator(Class<? extends DataObject> clazz, String field) {
_field = field;
_clazz = clazz;
_list = new ArrayList<String>();
}
@Override
public void aggregate(Row<String, CompositeColumnName> row) {
if (row.getColumns().size() == 0) {
return;
}
DataObjectType doType = TypeMap.getDoType(_clazz);
if (doType == null) {
throw new IllegalArgumentException();
}
ColumnField columnField = doType.getColumnField(_field);
if (columnField == null) {
throw new IllegalArgumentException();
}
Column<CompositeColumnName> column = row.getColumns().iterator().next();
if (column.getName().getOne().equals(_field)) {
String value = ColumnValue.getPrimitiveColumnValue(column,
columnField.getPropertyDescriptor()).toString();
_list.add(value);
}
}
@Override
public String[] getAggregatedFields() {
return new String[] { _field };
}
public List<String> getResults() {
return _list;
}
}
@Test
public void testLocalRefToGeo() {
_logger.info("Starting testLocalRefToGeo");
// FileShare is local
FileShare fs1 = new FileShare();
fs1.setId(URIUtil.createId(FileShare.class));
fs1.setLabel("testfs1");
fs1.setNativeGuid(UUID.randomUUID().toString());
// project is geo
Project pj1 = new Project();
pj1.setId(URIUtil.createId(Project.class));
pj1.setLabel("testpj1");
pj1.setTenantOrg(new NamedURI(rootTenant.getId(), "testpj1"));
fs1.setProject(new NamedURI(pj1.getId(), "project"));
DbClient dbClient = getDbClient();
dbClient.createObject(pj1);
dbClient.createObject(fs1);
Project queriedPj = dbClient.queryObject(Project.class, pj1.getId());
Assert.assertNotNull(queriedPj);
Assert.assertTrue(queriedPj.getId().equals(pj1.getId()));
Assert.assertTrue(isItWhereItShouldBe(queriedPj));
Assert.assertTrue(getDbClient().queryByType(Project.class, true).iterator().hasNext());
FileShare queriedFs = dbClient.queryObject(FileShare.class, fs1.getId());
Assert.assertNotNull(queriedFs);
Assert.assertTrue(queriedFs.getId().equals(fs1.getId()));
Assert.assertTrue(isItWhereItShouldBe(queriedFs));
Assert.assertTrue(getDbClient().queryByType(FileShare.class, true).iterator().hasNext());
// get fileshare by project tests global index into a local cf
ContainmentConstraint fcc = ContainmentConstraint.Factory.getProjectFileshareConstraint(pj1.getId());
URIQueryResultList flist = new URIQueryResultList();
dbClient.queryByConstraint(fcc, flist);
Assert.assertTrue(flist.iterator().hasNext());
// get project by tenant tests global index into another global cf
ContainmentConstraint pcc = ContainmentConstraint.Factory.getTenantOrgProjectConstraint(rootTenant.getId());
URIQueryResultList plist = new URIQueryResultList();
dbClient.queryByConstraint(pcc, plist);
Assert.assertTrue(plist.iterator().hasNext());
dbClient.removeObject(fs1);
URIQueryResultList flist2 = new URIQueryResultList();
dbClient.queryByConstraint(fcc, flist2);
Assert.assertFalse(flist2.iterator().hasNext());
dbClient.removeObject(pj1);
URIQueryResultList plist2 = new URIQueryResultList();
dbClient.queryByConstraint(pcc, plist2);
Iterator<URI> pjItr = plist2.iterator();
while (pjItr.hasNext()) {
if (pjItr.next().equals(pj1.getId())) {
Assert.fail("project found after delete");
}
}
Assert.assertNull(dbClient.queryObject(FileShare.class, fs1.getId()));
Assert.assertNull(dbClient.queryObject(Project.class, pj1.getId()));
}
@Test
public void testEncryption() {
DbClient dbClient = getDbClient();
// create a geo-replicated object with an encrypted field
AuthnProvider authProvider = new AuthnProvider();
authProvider.setId(URIUtil.createId(AuthnProvider.class));
authProvider.setManagerPassword("password");
dbClient.createObject(authProvider);
// create a local object with an encrypted field
Vcenter vc = new Vcenter();
vc.setId(URIUtil.createId(Vcenter.class));
vc.setPassword("password");
dbClient.createObject(vc);
AuthnProvider q0 = dbClient.queryObject(AuthnProvider.class, authProvider.getId());
Assert.assertNotNull(q0);
Assert.assertEquals(q0.getManagerPassword(), "password");
Vcenter k0 = dbClient.queryObject(Vcenter.class, vc.getId());
Assert.assertNotNull(k0);
Assert.assertEquals(k0.getPassword(), "password");
// null out geo encryption provider; make sure local object is still valid
TypeMap.setEncryptionProviders(_encryptionProvider, null);
Vcenter k1 = dbClient.queryObject(Vcenter.class, vc.getId());
Assert.assertNotNull(k1);
Assert.assertEquals(k1.getPassword(), "password");
// geo-replicated object should be encrypted
AuthnProvider q1 = dbClient.queryObject(AuthnProvider.class, authProvider.getId());
Assert.assertNotNull(q1);
Assert.assertFalse(q1.getManagerPassword().equals("password"));
// restore geo encryption provider and null out local
TypeMap.setEncryptionProviders(null, _geoEncryptionProvider);
AuthnProvider q2 = dbClient.queryObject(AuthnProvider.class, authProvider.getId());
Assert.assertNotNull(q2);
Assert.assertEquals(q2.getManagerPassword(), "password");
Vcenter k2 = dbClient.queryObject(Vcenter.class, vc.getId());
Assert.assertNotNull(k2);
Assert.assertFalse(k2.getPassword().equals("password"));
// now just to make sure the encryption keys are different, lets swap them
// and make sure the queries are not successful
TypeMap.setEncryptionProviders(_geoEncryptionProvider, _encryptionProvider);
try {
AuthnProvider q3 = dbClient.queryObject(AuthnProvider.class, authProvider.getId());
Assert.fail("geo repliated object query after swapping encryption providers succeeded; failure expected");
} catch (Exception e) {
// this is expected; test passes
}
try {
Vcenter k3 = dbClient.queryObject(Vcenter.class, vc.getId());
Assert.fail("local object query after swapping encryption providers succeeded; failure expected");
} catch (Exception e) {
// this is expected; test passes
}
}
private boolean isRootTenantExist(DbClient dbClient) {
URIQueryResultList tenants = new URIQueryResultList();
try {
dbClient.queryByConstraint(
ContainmentConstraint.Factory.getTenantOrgSubTenantConstraint(URI.create(TenantOrg.NO_PARENT)),
tenants);
if (tenants.iterator().hasNext()) {
return true;
} else {
_logger.info("root tenant query returned no results");
return false;
}
} catch (DatabaseException ex) {
_logger.error("failed querying for root tenant", ex);
}
throw new IllegalStateException("root tenant query failed");
}
private TenantOrg getRootTenant() {
URIQueryResultList tenants = new URIQueryResultList();
try {
_dbClient.queryByConstraint(
ContainmentConstraint.Factory.getTenantOrgSubTenantConstraint(URI.create(TenantOrg.NO_PARENT)),
tenants);
if (tenants.iterator().hasNext()) {
URI root = tenants.iterator().next();
TenantOrg rootTenant = _dbClient.queryObject(TenantOrg.class, root);
// safety check to prevent further operations when root tenant is messed up
// It is possible have multiple index entries for the same root tenant at a certain period (CQ610571)
while (tenants.iterator().hasNext()) {
URI mulRoot = tenants.iterator().next();
if (!mulRoot.equals(root)) {
_logger.error("multiple entries found for root tenant. Stop.");
return null;
}
}
return rootTenant;
} else {
_logger.error("root tenant query returned no results");
return null;
}
} catch (DatabaseException ex) {
_logger.error("DatabaseException :", ex);
return null;
}
}
private List<DataObject> addDataObjects(TenantOrg t, List<FileShare> fsList, List<Project> pjList) {
// FileShare is local
FileShare fs1 = new FileShare();
fs1.setId(URIUtil.createId(FileShare.class));
fs1.setLabel("testfs1");
fs1.setNativeGuid(UUID.randomUUID().toString());
FileShare fs2 = new FileShare();
fs2.setId(URIUtil.createId(FileShare.class));
fs2.setLabel("testfs2");
fs2.setNativeGuid(UUID.randomUUID().toString());
if (fsList != null) {
fsList.add(fs1);
fsList.add(fs2);
}
// project is geo
Project pj1 = new Project();
pj1.setId(URIUtil.createId(Project.class));
pj1.setLabel("testpj1");
pj1.setTenantOrg(new NamedURI(t.getId(), "testpj1"));
Project pj2 = new Project();
pj2.setId(URIUtil.createId(Project.class));
pj2.setLabel("testpj2");
pj2.setTenantOrg(new NamedURI(t.getId(), "testpj2"));
if (pjList != null) {
pjList.add(pj1);
pjList.add(pj2);
}
List<DataObject> dbObjList = new ArrayList<DataObject>();
dbObjList.add(fs1);
dbObjList.add(fs2);
dbObjList.add(pj1);
dbObjList.add(pj2);
DbClient dbClient = getDbClient();
dbClient.createObject(dbObjList);
return dbObjList;
}
}