Adding support for cross-cluster storage migration for managed storage when using XenServer

This commit is contained in:
Mike Tutkowski 2016-08-20 17:58:30 -06:00
parent 1d9735c346
commit b508fb8692
21 changed files with 2084 additions and 725 deletions

View File

@ -104,4 +104,6 @@ public interface StoragePool extends Identity, InternalIdentity {
boolean isInMaintenance();
Hypervisor.HypervisorType getHypervisor();
boolean isManaged();
}

View File

@ -21,26 +21,25 @@ package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.utils.Pair;
public class MigrateWithStorageReceiveCommand extends Command {
VirtualMachineTO vm;
List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler;
List<Pair<VolumeTO, String>> volumeToStorageUuid;
public MigrateWithStorageReceiveCommand(VirtualMachineTO vm, List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler) {
public MigrateWithStorageReceiveCommand(VirtualMachineTO vm, List<Pair<VolumeTO, String>> volumeToStorageUuid) {
this.vm = vm;
this.volumeToFiler = volumeToFiler;
this.volumeToStorageUuid = volumeToStorageUuid;
}
public VirtualMachineTO getVirtualMachine() {
return vm;
}
public List<Pair<VolumeTO, StorageFilerTO>> getVolumeToFiler() {
return volumeToFiler;
public List<Pair<VolumeTO, String>> getVolumeToStorageUuid() {
return volumeToStorageUuid;
}
@Override

View File

@ -134,6 +134,9 @@ public class BackupSnapshotCommandTest {
return 25;
};
@Override
public boolean isManaged() { return false; }
@Override
public Long getPodId() {
return 0L;

View File

@ -173,6 +173,9 @@ public class CheckNetworkAnswerTest {
return 25;
};
@Override
public boolean isManaged() { return false; }
@Override
public Long getPodId() {
return 0L;

View File

@ -135,6 +135,9 @@ public class SnapshotCommandTest {
return 25;
};
@Override
public boolean isManaged() { return false; }
@Override
public Long getPodId() {
return 0L;

View File

@ -25,6 +25,13 @@ import com.cloud.host.Host;
import com.cloud.storage.StoragePool;
public interface PrimaryDataStoreDriver extends DataStoreDriver {
String BASIC_CREATE = "basicCreate";
String BASIC_DELETE = "basicDelete";
String BASIC_DELETE_FAILURE = "basicDeleteFailure";
String BASIC_GRANT_ACCESS = "basicGrantAccess";
String BASIC_REVOKE_ACCESS = "basicRevokeAccess";
String BASIC_IQN = "basicIqn";
ChapInfo getChapInfo(DataObject dataObject);
boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore);

View File

@ -2045,48 +2045,60 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
private Map<Volume, StoragePool> getPoolListForVolumesForMigration(final VirtualMachineProfile profile, final Host host, final Map<Long, Long> volumeToPool) {
final List<VolumeVO> allVolumes = _volsDao.findUsableVolumesForInstance(profile.getId());
final Map<Volume, StoragePool> volumeToPoolObjectMap = new HashMap<Volume, StoragePool> ();
final Map<Volume, StoragePool> volumeToPoolObjectMap = new HashMap<>();
for (final VolumeVO volume : allVolumes) {
final Long poolId = volumeToPool.get(Long.valueOf(volume.getId()));
final StoragePoolVO pool = _storagePoolDao.findById(poolId);
final StoragePoolVO destPool = _storagePoolDao.findById(poolId);
final StoragePoolVO currentPool = _storagePoolDao.findById(volume.getPoolId());
final DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
if (pool != null) {
if (destPool != null) {
// Check if pool is accessible from the destination host and disk offering with which the volume was
// created is compliant with the pool type.
if (_poolHostDao.findByPoolHost(pool.getId(), host.getId()) == null || pool.isLocal() != diskOffering.getUseLocalStorage()) {
if (_poolHostDao.findByPoolHost(destPool.getId(), host.getId()) == null || destPool.isLocal() != diskOffering.getUseLocalStorage()) {
// Cannot find a pool for the volume. Throw an exception.
throw new CloudRuntimeException("Cannot migrate volume " + volume + " to storage pool " + pool + " while migrating vm to host " + host +
throw new CloudRuntimeException("Cannot migrate volume " + volume + " to storage pool " + destPool + " while migrating vm to host " + host +
". Either the pool is not accessible from the host or because of the offering with which the volume is created it cannot be placed on " +
"the given pool.");
} else if (pool.getId() == currentPool.getId()) {
// If the pool to migrate too is the same as current pool, the volume doesn't need to be migrated.
} else if (destPool.getId() == currentPool.getId()) {
// If the pool to migrate to is the same as current pool, the volume doesn't need to be migrated.
} else {
volumeToPoolObjectMap.put(volume, pool);
volumeToPoolObjectMap.put(volume, destPool);
}
} else {
if (currentPool.isManaged()) {
volumeToPoolObjectMap.put(volume, currentPool);
} else {
// Find a suitable pool for the volume. Call the storage pool allocator to find the list of pools.
final DiskProfile diskProfile = new DiskProfile(volume, diskOffering, profile.getHypervisorType());
final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), host.getId(), null, null);
final ExcludeList avoid = new ExcludeList();
boolean currentPoolAvailable = false;
final List<StoragePool> poolList = new ArrayList<StoragePool>();
final List<StoragePool> poolList = new ArrayList<>();
final ExcludeList avoid = new ExcludeList();
for (final StoragePoolAllocator allocator : _storagePoolAllocators) {
final List<StoragePool> poolListFromAllocator = allocator.allocateToPool(diskProfile, profile, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL);
if (poolListFromAllocator != null && !poolListFromAllocator.isEmpty()) {
poolList.addAll(poolListFromAllocator);
}
}
boolean currentPoolAvailable = false;
if (poolList != null && !poolList.isEmpty()) {
// Volume needs to be migrated. Pick the first pool from the list. Add a mapping to migrate the
// volume to a pool only if it is required; that is the current pool on which the volume resides
// is not available on the destination host.
final Iterator<StoragePool> iter = poolList.iterator();
while (iter.hasNext()) {
if (currentPool.getId() == iter.next().getId()) {
currentPoolAvailable = true;
break;
}
}
@ -2096,7 +2108,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
}
if (!currentPoolAvailable && !volumeToPoolObjectMap.containsKey(volume)) {
// Cannot find a pool for the volume. Throw an exception.
throw new CloudRuntimeException("Cannot find a storage pool which is available for volume " + volume + " while migrating virtual machine " +
@ -2104,6 +2115,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
}
}
}
return volumeToPoolObjectMap;
}

View File

@ -231,6 +231,7 @@ public class StoragePoolVO implements StoragePool {
this.managed = managed;
}
@Override
public boolean isManaged() {
return managed;
}

View File

@ -31,7 +31,6 @@ import com.cloud.agent.api.Answer;
import com.cloud.agent.api.MigrateWithStorageReceiveAnswer;
import com.cloud.agent.api.MigrateWithStorageReceiveCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
@ -56,7 +55,7 @@ public final class XenServer610MigrateWithStorageReceiveCommandWrapper extends C
public Answer execute(final MigrateWithStorageReceiveCommand command, final XenServer610Resource xenServer610Resource) {
final Connection connection = xenServer610Resource.getConnection();
final VirtualMachineTO vmSpec = command.getVirtualMachine();
final List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler = command.getVolumeToFiler();
final List<Pair<VolumeTO, String>> volumeToStorageUuid = command.getVolumeToStorageUuid();
try {
// In a cluster management server setup, the migrate with storage receive and send
@ -69,10 +68,12 @@ public final class XenServer610MigrateWithStorageReceiveCommandWrapper extends C
// storage send command execution.
Gson gson = new Gson();
// Get a map of all the SRs to which the vdis will be migrated.
final List<Pair<VolumeTO, Object>> volumeToSr = new ArrayList<Pair<VolumeTO, Object>>();
for (final Pair<VolumeTO, StorageFilerTO> entry : volumeToFiler) {
final StorageFilerTO storageFiler = entry.second();
final SR sr = xenServer610Resource.getStorageRepository(connection, storageFiler.getUuid());
final List<Pair<VolumeTO, Object>> volumeToSr = new ArrayList<>();
for (final Pair<VolumeTO, String> entry : volumeToStorageUuid) {
final String storageUuid = entry.second();
final SR sr = xenServer610Resource.getStorageRepository(connection, storageUuid);
volumeToSr.add(new Pair<VolumeTO, Object>(entry.first(), sr));
}

View File

@ -19,6 +19,8 @@
package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase;
import java.util.Map;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
@ -39,7 +41,18 @@ public final class CitrixCreateStoragePoolCommandWrapper extends CommandWrapper<
public Answer execute(final CreateStoragePoolCommand command, final CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final StorageFilerTO pool = command.getPool();
try {
if (command.getCreateDatastore()) {
Map<String, String> details = command.getDetails();
String srNameLabel = details.get(CreateStoragePoolCommand.DATASTORE_NAME);
String storageHost = details.get(CreateStoragePoolCommand.STORAGE_HOST);
String iqn = details.get(CreateStoragePoolCommand.IQN);
citrixResourceBase.getIscsiSR(conn, srNameLabel, storageHost, iqn, null, null, false);
}
else {
if (pool.getType() == StoragePoolType.NetworkFilesystem) {
citrixResourceBase.getNfsSR(conn, Long.toString(pool.getId()), pool.getUuid(), pool.getHost(), pool.getPath(), pool.toString());
} else if (pool.getType() == StoragePoolType.IscsiLUN) {
@ -48,11 +61,15 @@ public final class CitrixCreateStoragePoolCommandWrapper extends CommandWrapper<
} else {
return new Answer(command, false, "The pool type: " + pool.getType().name() + " is not supported.");
}
}
return new Answer(command, true, "success");
} catch (final Exception e) {
final String msg = "Catch Exception " + e.getClass().getName() + ", create StoragePool failed due to " + e.toString() + " on host:"
+ citrixResourceBase.getHost().getUuid() + " pool: " + pool.getHost() + pool.getPath();
s_logger.warn(msg, e);
return new Answer(command, false, msg);
}
}

View File

@ -19,6 +19,8 @@
package com.cloud.hypervisor.xenserver.resource.wrapper.xenbase;
import java.util.Map;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
@ -32,22 +34,40 @@ import com.xensource.xenapi.SR;
@ResourceWrapper(handles = DeleteStoragePoolCommand.class)
public final class CitrixDeleteStoragePoolCommandWrapper extends CommandWrapper<DeleteStoragePoolCommand, Answer, CitrixResourceBase> {
private static final Logger s_logger = Logger.getLogger(CitrixDeleteStoragePoolCommandWrapper.class);
@Override
public Answer execute(final DeleteStoragePoolCommand command, final CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final StorageFilerTO poolTO = command.getPool();
try {
final SR sr = citrixResourceBase.getStorageRepository(conn, poolTO.getUuid());
final SR sr;
// getRemoveDatastore being true indicates we are using managed storage and need to pull the SR name out of a Map
// instead of pulling it out using getUuid of the StorageFilerTO instance.
if (command.getRemoveDatastore()) {
Map<String, String> details = command.getDetails();
String srNameLabel = details.get(DeleteStoragePoolCommand.DATASTORE_NAME);
sr = citrixResourceBase.getStorageRepository(conn, srNameLabel);
}
else {
sr = citrixResourceBase.getStorageRepository(conn, poolTO.getUuid());
}
citrixResourceBase.removeSR(conn, sr);
final Answer answer = new Answer(command, true, "success");
return answer;
} catch (final Exception e) {
final String msg = "DeleteStoragePoolCommand XenAPIException:" + e.getMessage() + " host:" + citrixResourceBase.getHost().getUuid() + " pool: " + poolTO.getHost()
+ poolTO.getPath();
s_logger.warn(msg, e);
final String msg = "DeleteStoragePoolCommand XenAPIException:" + e.getMessage() + " host:" + citrixResourceBase.getHost().getUuid() +
" pool: " + poolTO.getHost() + poolTO.getPath();
s_logger.error(msg, e);
return new Answer(command, false, msg);
}
}

View File

@ -19,6 +19,7 @@
package org.apache.cloudstack.storage.motion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -28,6 +29,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
@ -39,6 +41,8 @@ import org.springframework.stereotype.Component;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.MigrateWithStorageAnswer;
import com.cloud.agent.api.MigrateWithStorageCommand;
import com.cloud.agent.api.MigrateWithStorageCompleteAnswer;
@ -56,9 +60,12 @@ import com.cloud.host.Host;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.StoragePool;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.VolumeDetailVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
@ -74,6 +81,8 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
@Inject
PrimaryDataStoreDao storagePoolDao;
@Inject
private VolumeDetailsDao volumeDetailsDao;
@Inject
VMInstanceDao instanceDao;
@Override
@ -120,25 +129,175 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
callback.complete(result);
}
private String getBasicIqn(long volumeId) {
VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, PrimaryDataStoreDriver.BASIC_IQN);
return volumeDetail.getValue();
}
/**
* Tell the underlying storage plug-in to create a new volume, put it in the VAG of the destination cluster, and
* send a command to the destination cluster to create an SR and to attach to the SR from all hosts in the cluster.
*/
private String handleManagedVolumePreMigration(VolumeInfo volumeInfo, StoragePool storagePool, Host destHost) {
final PrimaryDataStoreDriver pdsd = (PrimaryDataStoreDriver)volumeInfo.getDataStore().getDriver();
VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeInfo.getId(), PrimaryDataStoreDriver.BASIC_CREATE, Boolean.TRUE.toString(), false);
volumeDetailsDao.persist(volumeDetailVo);
pdsd.createAsync(volumeInfo.getDataStore(), volumeInfo, null);
volumeDetailVo = new VolumeDetailVO(volumeInfo.getId(), PrimaryDataStoreDriver.BASIC_GRANT_ACCESS, Boolean.TRUE.toString(), false);
volumeDetailsDao.persist(volumeDetailVo);
pdsd.grantAccess(volumeInfo, destHost, volumeInfo.getDataStore());
final Map<String, String> details = new HashMap<>();
final String iqn = getBasicIqn(volumeInfo.getId());
details.put(CreateStoragePoolCommand.DATASTORE_NAME, iqn);
details.put(CreateStoragePoolCommand.IQN, iqn);
details.put(CreateStoragePoolCommand.STORAGE_HOST, storagePool.getHostAddress());
details.put(CreateStoragePoolCommand.STORAGE_PORT, String.valueOf(storagePool.getPort()));
final CreateStoragePoolCommand cmd = new CreateStoragePoolCommand(true, storagePool);
cmd.setDetails(details);
cmd.setCreateDatastore(true);
final Answer answer = agentMgr.easySend(destHost.getId(), cmd);
if (answer == null || !answer.getResult()) {
String errMsg = "Error interacting with host (related to CreateStoragePoolCommand)" +
(StringUtils.isNotBlank(answer.getDetails()) ? ": " + answer.getDetails() : "");
s_logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
return iqn;
}
private void handleManagedVolumePostMigration(VolumeInfo volumeInfo, Host srcHost, VolumeObjectTO volumeTO) {
final Map<String, String> details = new HashMap<>();
details.put(DeleteStoragePoolCommand.DATASTORE_NAME, volumeInfo.get_iScsiName());
final DeleteStoragePoolCommand cmd = new DeleteStoragePoolCommand();
cmd.setDetails(details);
cmd.setRemoveDatastore(true);
final Answer answer = agentMgr.easySend(srcHost.getId(), cmd);
if (answer == null || !answer.getResult()) {
String errMsg = "Error interacting with host (related to DeleteStoragePoolCommand)" +
(StringUtils.isNotBlank(answer.getDetails()) ? ": " + answer.getDetails() : "");
s_logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
final PrimaryDataStoreDriver pdsd = (PrimaryDataStoreDriver)volumeInfo.getDataStore().getDriver();
pdsd.revokeAccess(volumeInfo, srcHost, volumeInfo.getDataStore());
VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeInfo.getId(), PrimaryDataStoreDriver.BASIC_DELETE, Boolean.TRUE.toString(), false);
volumeDetailsDao.persist(volumeDetailVo);
pdsd.deleteAsync(volumeInfo.getDataStore(), volumeInfo, null);
VolumeVO volumeVO = volDao.findById(volumeInfo.getId());
volumeVO.setPath(volumeTO.getPath());
volDao.update(volumeVO.getId(), volumeVO);
}
private void handleManagedVolumesAfterFailedMigration(Map<VolumeInfo, DataStore> volumeToPool, Host destHost) {
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
VolumeInfo volumeInfo = entry.getKey();
StoragePool storagePool = storagePoolDao.findById(volumeInfo.getPoolId());
if (storagePool.isManaged()) {
final Map<String, String> details = new HashMap<>();
details.put(DeleteStoragePoolCommand.DATASTORE_NAME, getBasicIqn(volumeInfo.getId()));
final DeleteStoragePoolCommand cmd = new DeleteStoragePoolCommand();
cmd.setDetails(details);
cmd.setRemoveDatastore(true);
final Answer answer = agentMgr.easySend(destHost.getId(), cmd);
if (answer == null || !answer.getResult()) {
String errMsg = "Error interacting with host (related to handleManagedVolumesAfterFailedMigration)" +
(StringUtils.isNotBlank(answer.getDetails()) ? ": " + answer.getDetails() : "");
s_logger.error(errMsg);
// no need to throw an exception here as the calling code is responsible for doing so
// regardless of the success or lack thereof concerning this method
return;
}
final PrimaryDataStoreDriver pdsd = (PrimaryDataStoreDriver)volumeInfo.getDataStore().getDriver();
VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeInfo.getId(), PrimaryDataStoreDriver.BASIC_REVOKE_ACCESS, Boolean.TRUE.toString(), false);
volumeDetailsDao.persist(volumeDetailVo);
pdsd.revokeAccess(volumeInfo, destHost, volumeInfo.getDataStore());
volumeDetailVo = new VolumeDetailVO(volumeInfo.getId(), PrimaryDataStoreDriver.BASIC_DELETE_FAILURE, Boolean.TRUE.toString(), false);
volumeDetailsDao.persist(volumeDetailVo);
pdsd.deleteAsync(volumeInfo.getDataStore(), volumeInfo, null);
}
}
}
private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost, Host destHost, Map<VolumeInfo, DataStore> volumeToPool)
throws AgentUnavailableException {
// Initiate migration of a virtual machine with its volumes.
// Initiate migration of a virtual machine with it's volumes.
try {
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerto = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
List<Pair<VolumeTO, String>> volumeToStorageUuid = new ArrayList<>();
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
VolumeInfo volume = entry.getKey();
VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
volumeToFilerto.add(new Pair<VolumeTO, StorageFilerTO>(volumeTo, filerTo));
VolumeInfo volumeInfo = entry.getKey();
StoragePool storagePool = storagePoolDao.findById(volumeInfo.getPoolId());
VolumeTO volumeTo = new VolumeTO(volumeInfo, storagePool);
if (storagePool.isManaged()) {
String iqn = handleManagedVolumePreMigration(volumeInfo, storagePool, destHost);
volumeToStorageUuid.add(new Pair<>(volumeTo, iqn));
}
else {
volumeToStorageUuid.add(new Pair<>(volumeTo, ((StoragePool)entry.getValue()).getPath()));
}
}
// Migration across cluster needs to be done in three phases.
// 1. Send a migrate receive command to the destination host so that it is ready to receive a vm.
// 2. Send a migrate send command to the source host. This actually migrates the vm to the destination.
// 3. Complete the process. Update the volume details.
MigrateWithStorageReceiveCommand receiveCmd = new MigrateWithStorageReceiveCommand(to, volumeToFilerto);
MigrateWithStorageReceiveCommand receiveCmd = new MigrateWithStorageReceiveCommand(to, volumeToStorageUuid);
MigrateWithStorageReceiveAnswer receiveAnswer = (MigrateWithStorageReceiveAnswer)agentMgr.send(destHost.getId(), receiveCmd);
if (receiveAnswer == null) {
s_logger.error("Migration with storage of vm " + vm + " to host " + destHost + " failed.");
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
@ -150,16 +309,22 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
MigrateWithStorageSendCommand sendCmd =
new MigrateWithStorageSendCommand(to, receiveAnswer.getVolumeToSr(), receiveAnswer.getNicToNetwork(), receiveAnswer.getToken());
MigrateWithStorageSendAnswer sendAnswer = (MigrateWithStorageSendAnswer)agentMgr.send(srcHost.getId(), sendCmd);
if (sendAnswer == null) {
handleManagedVolumesAfterFailedMigration(volumeToPool, destHost);
s_logger.error("Migration with storage of vm " + vm + " to host " + destHost + " failed.");
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
} else if (!sendAnswer.getResult()) {
handleManagedVolumesAfterFailedMigration(volumeToPool, destHost);
s_logger.error("Migration with storage of vm " + vm + " failed. Details: " + sendAnswer.getDetails());
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
}
MigrateWithStorageCompleteCommand command = new MigrateWithStorageCompleteCommand(to);
MigrateWithStorageCompleteAnswer answer = (MigrateWithStorageCompleteAnswer)agentMgr.send(destHost.getId(), command);
if (answer == null) {
s_logger.error("Migration with storage of vm " + vm + " failed.");
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
@ -168,7 +333,7 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
} else {
// Update the volume details after migration.
updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos(), srcHost);
}
return answer;
@ -181,7 +346,7 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
private Answer migrateVmWithVolumesWithinCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost, Host destHost, Map<VolumeInfo, DataStore> volumeToPool)
throws AgentUnavailableException {
// Initiate migration of a virtual machine with it's volumes.
// Initiate migration of a virtual machine with its volumes.
try {
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerto = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
@ -201,7 +366,7 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost + ". " + answer.getDetails());
} else {
// Update the volume details after migration.
updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos(), srcHost);
}
return answer;
@ -211,28 +376,39 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
}
}
private void updateVolumePathsAfterMigration(Map<VolumeInfo, DataStore> volumeToPool, List<VolumeObjectTO> volumeTos) {
private void updateVolumePathsAfterMigration(Map<VolumeInfo, DataStore> volumeToPool, List<VolumeObjectTO> volumeTos, Host srcHost) {
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
VolumeInfo volumeInfo = entry.getKey();
StoragePool storagePool = (StoragePool)entry.getValue();
boolean updated = false;
VolumeInfo volume = entry.getKey();
StoragePool pool = (StoragePool)entry.getValue();
for (VolumeObjectTO volumeTo : volumeTos) {
if (volume.getId() == volumeTo.getId()) {
VolumeVO volumeVO = volDao.findById(volume.getId());
if (volumeInfo.getId() == volumeTo.getId()) {
if (storagePool.isManaged()) {
handleManagedVolumePostMigration(volumeInfo, srcHost, volumeTo);
}
else {
VolumeVO volumeVO = volDao.findById(volumeInfo.getId());
Long oldPoolId = volumeVO.getPoolId();
volumeVO.setPath(volumeTo.getPath());
volumeVO.setFolder(pool.getPath());
volumeVO.setPodId(pool.getPodId());
volumeVO.setPoolId(pool.getId());
volumeVO.setFolder(storagePool.getPath());
volumeVO.setPodId(storagePool.getPodId());
volumeVO.setPoolId(storagePool.getId());
volumeVO.setLastPoolId(oldPoolId);
volDao.update(volume.getId(), volumeVO);
volDao.update(volumeInfo.getId(), volumeVO);
}
updated = true;
break;
}
}
if (!updated) {
s_logger.error("Volume path wasn't updated for volume " + volume + " after it was migrated.");
s_logger.error("The volume path wasn't updated for volume '" + volumeInfo + "' after it was migrated.");
}
}
}

View File

@ -204,9 +204,9 @@ public class XenServer610WrapperTest {
final StorageFilerTO storage1 = Mockito.mock(StorageFilerTO.class);
final StorageFilerTO storage2 = Mockito.mock(StorageFilerTO.class);
final List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
volumeToFiler.add(new Pair<VolumeTO, StorageFilerTO>(vol1, storage1));
volumeToFiler.add(new Pair<VolumeTO, StorageFilerTO>(vol2, storage2));
final List<Pair<VolumeTO, String>> volumeToFiler = new ArrayList<>();
volumeToFiler.add(new Pair<>(vol1, storage1.getPath()));
volumeToFiler.add(new Pair<>(vol2, storage2.getPath()));
final NicTO nicTO1 = Mockito.mock(NicTO.class);
final NicTO nicTO2 = Mockito.mock(NicTO.class);

View File

@ -94,6 +94,8 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private static final long MIN_IOPS_FOR_SNAPSHOT_VOLUME = 100L;
private static final long MAX_IOPS_FOR_SNAPSHOT_VOLUME = 20000L;
private static final String BASIC_SF_ID = "basicSfId";
@Inject private AccountDao accountDao;
@Inject private AccountDetailsDao accountDetailsDao;
@Inject private ClusterDao clusterDao;
@ -153,7 +155,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
Preconditions.checkArgument(host != null, "'host' should not be 'null'");
Preconditions.checkArgument(dataStore != null, "'dataStore' should not be 'null'");
long sfVolumeId = getSolidFireVolumeId(dataObject);
long sfVolumeId = getSolidFireVolumeId(dataObject, true);
long clusterId = host.getClusterId();
long storagePoolId = dataStore.getId();
@ -215,7 +217,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return;
}
long sfVolumeId = getSolidFireVolumeId(dataObject);
long sfVolumeId = getSolidFireVolumeId(dataObject, false);
long clusterId = host.getClusterId();
long storagePoolId = dataStore.getId();
@ -252,9 +254,31 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
}
private long getSolidFireVolumeId(DataObject dataObject) {
private long getSolidFireVolumeId(DataObject dataObject, boolean grantAccess) {
if (dataObject.getType() == DataObjectType.VOLUME) {
return Long.parseLong(((VolumeInfo)dataObject).getFolder());
final VolumeInfo volumeInfo = (VolumeInfo)dataObject;
final long volumeId = volumeInfo.getId();
if (grantAccess && isBasicGrantAccess(volumeId)) {
volumeDetailsDao.removeDetail(volumeInfo.getId(), BASIC_GRANT_ACCESS);
final Long sfVolumeId = getBasicSfVolumeId(volumeId);
Preconditions.checkNotNull(sfVolumeId, "'sfVolumeId' should not be 'null' (basic grant access).");
return sfVolumeId;
}
else if (!grantAccess && isBasicRevokeAccess(volumeId)) {
volumeDetailsDao.removeDetail(volumeInfo.getId(), BASIC_REVOKE_ACCESS);
final Long sfVolumeId = getBasicSfVolumeId(volumeId);
Preconditions.checkNotNull(sfVolumeId, "'sfVolumeId' should not be 'null' (basic revoke access).");
return sfVolumeId;
}
return Long.parseLong(volumeInfo.getFolder());
}
if (dataObject.getType() == DataObjectType.SNAPSHOT) {
@ -271,7 +295,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return getVolumeIdFrom_iScsiPath(((TemplateInfo)dataObject).getInstallPath());
}
throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to getSolidFireVolumeId(DataObject)");
throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to getSolidFireVolumeId(DataObject, boolean)");
}
private long getVolumeIdFrom_iScsiPath(String iScsiPath) {
@ -313,10 +337,11 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, DataObject dataObject, long sfAccountId) {
long storagePoolId = dataObject.getDataStore().getId();
Long minIops = null;
Long maxIops = null;
Long volumeSize = dataObject.getSize();
String volumeName = null;
final Long minIops;
final Long maxIops;
final Long volumeSize;
final String volumeName;
final Map<String, String> mapAttributes;
@ -647,6 +672,58 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
snapshotDetailsDao.remove(snapshotDetails.getId());
}
private Long getBasicSfVolumeId(long volumeId) {
VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, BASIC_SF_ID);
if (volumeDetail != null && volumeDetail.getValue() != null) {
return new Long(volumeDetail.getValue());
}
return null;
}
private String getBasicIqn(long volumeId) {
VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, BASIC_IQN);
if (volumeDetail != null && volumeDetail.getValue() != null) {
return volumeDetail.getValue();
}
return null;
}
// If isBasicCreate returns true, this means the calling code simply wants us to create a SolidFire volume with specified
// characteristics. We do not update the cloud.volumes table with this info.
private boolean isBasicCreate(long volumeId) {
return getBooleanValueFromVolumeDetails(volumeId, BASIC_CREATE);
}
private boolean isBasicDelete(long volumeId) {
return getBooleanValueFromVolumeDetails(volumeId, BASIC_DELETE);
}
private boolean isBasicDeleteFailure(long volumeId) {
return getBooleanValueFromVolumeDetails(volumeId, BASIC_DELETE_FAILURE);
}
private boolean isBasicGrantAccess(long volumeId) {
return getBooleanValueFromVolumeDetails(volumeId, BASIC_GRANT_ACCESS);
}
private boolean isBasicRevokeAccess(long volumeId) {
return getBooleanValueFromVolumeDetails(volumeId, BASIC_REVOKE_ACCESS);
}
private boolean getBooleanValueFromVolumeDetails(long volumeId, String name) {
VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, name);
if (volumeDetail != null && volumeDetail.getValue() != null) {
return Boolean.parseBoolean(volumeDetail.getValue());
}
return false;
}
private long getCsIdForCloning(long volumeId, String cloneOf) {
VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, cloneOf);
@ -788,12 +865,14 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
LOGGER.error(errMsg);
}
if (callback != null) {
CommandResult result = new CommandResult();
result.setResult(errMsg);
callback.complete(result);
}
}
@Override
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
@ -950,19 +1029,43 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
snapshotDetailsDao.persist(snapshotDetail);
}
private void addBasicCreateInfoToVolumeDetails(long volumeId, SolidFireUtil.SolidFireVolume sfVolume) {
VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeId, BASIC_SF_ID, String.valueOf(sfVolume.getId()), false);
volumeDetailsDao.persist(volumeDetailVo);
volumeDetailVo = new VolumeDetailVO(volumeId, BASIC_IQN, sfVolume.getIqn(), false);
volumeDetailsDao.persist(volumeDetailVo);
}
private String createVolume(VolumeInfo volumeInfo, long storagePoolId) {
boolean isBasicCreate = isBasicCreate(volumeInfo.getId());
if (!isBasicCreate) {
verifySufficientBytesForStoragePool(volumeInfo, storagePoolId);
verifySufficientIopsForStoragePool(volumeInfo.getMinIops() != null ? volumeInfo.getMinIops() : getDefaultMinIops(storagePoolId), storagePoolId);
}
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
long sfAccountId = getCreateSolidFireAccountId(sfConnection, volumeInfo.getAccountId(), storagePoolId);
SolidFireUtil.SolidFireVolume sfVolume;
if (isBasicCreate) {
sfVolume = createSolidFireVolume(sfConnection, volumeInfo, sfAccountId);
volumeDetailsDao.removeDetail(volumeInfo.getId(), BASIC_CREATE);
addBasicCreateInfoToVolumeDetails(volumeInfo.getId(), sfVolume);
return sfVolume.getIqn();
}
long csSnapshotId = getCsIdForCloning(volumeInfo.getId(), "cloneOfSnapshot");
long csTemplateId = getCsIdForCloning(volumeInfo.getId(), "cloneOfTemplate");
SolidFireUtil.SolidFireVolume sfVolume;
if (csSnapshotId > 0) {
// We are supposed to create a clone of the underlying volume or snapshot that supports the CloudStack snapshot.
sfVolume = createClone(sfConnection, csSnapshotId, volumeInfo, sfAccountId, storagePoolId, DataObjectType.SNAPSHOT);
@ -1083,12 +1186,54 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return iqn;
}
private void performBasicDelete(SolidFireUtil.SolidFireConnection sfConnection, long volumeId) {
Long sfVolumeId = getBasicSfVolumeId(volumeId);
Preconditions.checkNotNull(sfVolumeId, "'sfVolumeId' should not be 'null'.");
String iqn = getBasicIqn(volumeId);
Preconditions.checkNotNull(iqn, "'iqn' should not be 'null'.");
VolumeVO volumeVO = volumeDao.findById(volumeId);
SolidFireUtil.deleteSolidFireVolume(sfConnection, Long.parseLong(volumeVO.getFolder()));
volumeVO.setFolder(String.valueOf(sfVolumeId));
volumeVO.set_iScsiName(iqn);
volumeDao.update(volumeId, volumeVO);
volumeDetailsDao.removeDetail(volumeId, BASIC_SF_ID);
volumeDetailsDao.removeDetail(volumeId, BASIC_IQN);
volumeDetailsDao.removeDetail(volumeId, BASIC_DELETE);
}
private void performBasicDeleteFailure(SolidFireUtil.SolidFireConnection sfConnection, long volumeId) {
Long sfVolumeId = getBasicSfVolumeId(volumeId);
Preconditions.checkNotNull(sfVolumeId, "'sfVolumeId' should not be 'null'.");
SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId);
volumeDetailsDao.removeDetail(volumeId, BASIC_SF_ID);
volumeDetailsDao.removeDetail(volumeId, BASIC_IQN);
volumeDetailsDao.removeDetail(volumeId, BASIC_DELETE_FAILURE);
}
private void deleteVolume(VolumeInfo volumeInfo, long storagePoolId) {
try {
long volumeId = volumeInfo.getId();
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
if (isBasicDelete(volumeId)) {
performBasicDelete(sfConnection, volumeId);
}
else if (isBasicDeleteFailure(volumeId)) {
performBasicDeleteFailure(sfConnection, volumeId);
}
else {
deleteSolidFireVolume(sfConnection, volumeInfo);
volumeDetailsDao.removeDetails(volumeId);
@ -1101,6 +1246,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
storagePoolDao.update(storagePoolId, storagePool);
}
}
catch (Exception ex) {
LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume. CloudStack volume ID: " + volumeInfo.getId(), ex);

View File

@ -1205,12 +1205,15 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
srcHost.getHypervisorType(), srcHost.getHypervisorVersion());
allHosts = allHostsPair.first();
allHosts.remove(srcHost);
for (final VolumeVO volume : volumes) {
final Long volClusterId = _poolDao.findById(volume.getPoolId()).getClusterId();
// only check for volume which are not in zone wide primary store, as only those may require storage motion
if (volClusterId != null) {
final StoragePool storagePool = _poolDao.findById(volume.getPoolId());
final Long volClusterId = storagePool.getClusterId();
for (final Iterator<HostVO> iterator = allHosts.iterator(); iterator.hasNext();) {
final Host host = iterator.next();
if (volClusterId != null) {
if (!host.getClusterId().equals(volClusterId) || usesLocal) {
if (hasSuitablePoolsForVolume(volume, host, vmProfile)) {
requiresStorageMotion.put(host, true);
@ -1219,8 +1222,16 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
}
}
}
else {
if (storagePool.isManaged()) {
if (srcHost.getClusterId() != host.getClusterId()) {
requiresStorageMotion.put(host, true);
}
}
}
}
}
plan = new DataCenterDeployment(srcHost.getDataCenterId(), null, null, null, null, null);
} else {
final Long cluster = srcHost.getClusterId();

View File

@ -21,6 +21,8 @@ import SignedAPICall
import time
import XenAPI
from util import sf_util
# All tests inherit from cloudstackTestCase
from marvin.cloudstackTestCase import cloudstackTestCase
@ -37,6 +39,15 @@ from marvin.lib.utils import cleanup_resources
from solidfire import solidfire_element_api as sf_api
# Prerequisites:
# Only one zone
# Only one pod
# Only one cluster (two hosts with another added/removed during the tests)
#
# Running the tests:
# Set a breakpoint on each test after the first one. When the breakpoint is hit, reset the third
# host to a snapshot state and re-start it. Once it's up and running, run the test code.
class TestData:
account = "account"
@ -238,7 +249,7 @@ class TestAddRemoveHosts(cloudstackTestCase):
try:
cleanup_resources(cls.apiClient, cls._cleanup)
cls._purge_solidfire_volumes()
sf_util.purge_solidfire_volumes(cls.sf_client)
except Exception as e:
logging.debug("Exception in tearDownClass(cls): %s" % e)
@ -286,7 +297,7 @@ class TestAddRemoveHosts(cloudstackTestCase):
root_volume = self._get_root_volume(self.virtual_machine)
sf_iscsi_name = self._get_iqn(root_volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, root_volume, self)
self._perform_add_remove_host(primary_storage.id, sf_iscsi_name)
@ -342,7 +353,7 @@ class TestAddRemoveHosts(cloudstackTestCase):
root_volume = self._get_root_volume(self.virtual_machine)
sf_iscsi_name = self._get_iqn(root_volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, root_volume, self)
primarystorage2 = self.testdata[TestData.primaryStorage2]
@ -596,19 +607,6 @@ class TestAddRemoveHosts(cloudstackTestCase):
self.assert_(False, "Unable to locate the ROOT volume of the VM with the following ID: " + str(vm.id))
def _get_iqn(self, volume):
# Get volume IQN
sf_iscsi_name_request = {'volumeid': volume.id}
# put this commented line back once PR 1403 is in
# sf_iscsi_name_result = self.cs_api.getVolumeiScsiName(sf_iscsi_name_request)
sf_iscsi_name_result = self.cs_api.getSolidFireVolumeIscsiName(sf_iscsi_name_request)
# sf_iscsi_name = sf_iscsi_name_result['apivolumeiscsiname']['volumeiScsiName']
sf_iscsi_name = sf_iscsi_name_result['apisolidfirevolumeiscsiname']['solidFireVolumeIscsiName']
self._check_iscsi_name(sf_iscsi_name)
return sf_iscsi_name
def _get_iqn_2(self, primary_storage):
sql_query = "Select path From storage_pool Where uuid = '" + str(primary_storage.id) + "'"
@ -617,13 +615,6 @@ class TestAddRemoveHosts(cloudstackTestCase):
return sql_result[0][0]
def _check_iscsi_name(self, sf_iscsi_name):
self.assertEqual(
sf_iscsi_name[0],
"/",
"The iSCSI name needs to start with a forward slash."
)
def _get_host_iscsi_iqns(self):
hosts = self.xen_session.xenapi.host.get_all()
@ -687,24 +678,3 @@ class TestAddRemoveHosts(cloudstackTestCase):
for host_iscsi_iqn in host_iscsi_iqns:
# an error should occur if host_iscsi_iqn is not in sf_vag_initiators
sf_vag_initiators.index(host_iscsi_iqn)
def _check_list(self, in_list, expected_size_of_list, err_msg):
self.assertEqual(
isinstance(in_list, list),
True,
"'in_list' is not a list."
)
self.assertEqual(
len(in_list),
expected_size_of_list,
err_msg
)
@classmethod
def _purge_solidfire_volumes(cls):
deleted_volumes = cls.sf_client.list_deleted_volumes()
for deleted_volume in deleted_volumes:
cls.sf_client.purge_deleted_volume(deleted_volume['volumeID'])

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,697 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import logging
import random
import SignedAPICall
import XenAPI
from util import sf_util
# All tests inherit from cloudstackTestCase
from marvin.cloudstackTestCase import cloudstackTestCase
# base - contains all resources as entities and defines create, delete, list operations on them
from marvin.lib.base import Account, DiskOffering, ServiceOffering, StoragePool, User, VirtualMachine, Volume
# common - commonly used methods for all tests are listed here
from marvin.lib.common import get_domain, get_template, get_zone, list_clusters, list_hosts, list_volumes
# utils - utility classes for common cleanup, external library wrappers, etc.
from marvin.lib.utils import cleanup_resources
from solidfire import solidfire_element_api as sf_api
# Prerequisites:
# Only one zone
# Only one pod
# Two clusters
class TestData():
account = "account"
capacityBytes = "capacitybytes"
capacityIops = "capacityiops"
clusterId1 = "clusterId1"
clusterId2 = "clusterId2"
computeOffering1 = "computeoffering1"
computeOffering2 = "computeoffering2"
computeOffering3 = "computeoffering3"
diskName = "diskname"
diskOffering1 = "diskoffering1"
diskOffering2 = "diskoffering2"
domainId = "domainid"
hypervisor = "hypervisor"
login = "login"
mvip = "mvip"
name = "name"
password = "password"
podId = "podid"
port = "port"
primaryStorage = "primarystorage"
primaryStorage2 = "primarystorage2"
provider = "provider"
scope = "scope"
solidFire = "solidfire"
storageTag = "SolidFire_SAN_1"
storageTag2 = "SolidFire_Volume_1"
tags = "tags"
templateCacheName = "centos56-x86-64-xen"
templateName = "templatename"
testAccount = "testaccount"
url = "url"
user = "user"
username = "username"
virtualMachine = "virtualmachine"
virtualMachine2 = "virtualmachine2"
volume_1 = "volume_1"
xenServer = "xenserver"
zoneId = "zoneid"
def __init__(self):
self.testdata = {
TestData.solidFire: {
TestData.mvip: "192.168.139.112",
TestData.login: "admin",
TestData.password: "admin",
TestData.port: 443,
TestData.url: "https://192.168.139.112:443"
},
TestData.xenServer: {
TestData.username: "root",
TestData.password: "solidfire"
},
TestData.account: {
"email": "test@test.com",
"firstname": "John",
"lastname": "Doe",
"username": "test",
"password": "test"
},
TestData.testAccount: {
"email": "test2@test2.com",
"firstname": "Jane",
"lastname": "Doe",
"username": "test2",
"password": "test"
},
TestData.user: {
"email": "user@test.com",
"firstname": "Jane",
"lastname": "Doe",
"username": "testuser",
"password": "password"
},
TestData.primaryStorage: {
TestData.name: "SolidFire-%d" % random.randint(0, 100),
TestData.scope: "ZONE",
TestData.url: "MVIP=192.168.139.112;SVIP=10.10.8.112;" +
"clusterAdminUsername=admin;clusterAdminPassword=admin;" +
"clusterDefaultMinIops=10000;clusterDefaultMaxIops=15000;" +
"clusterDefaultBurstIopsPercentOfMaxIops=1.5;",
TestData.provider: "SolidFire",
TestData.tags: TestData.storageTag,
TestData.capacityIops: 4500000,
TestData.capacityBytes: 2251799813685248,
TestData.hypervisor: "Any",
TestData.zoneId: 1
},
TestData.primaryStorage2: {
TestData.name: "SolidFireShared-%d" % random.randint(0, 100),
TestData.scope: "CLUSTER",
TestData.url: "MVIP=192.168.139.112;SVIP=10.10.8.112;" +
"clusterAdminUsername=admin;clusterAdminPassword=admin;" +
"minIops=5000;maxIops=50000;burstIops=75000",
TestData.provider: "SolidFireShared",
TestData.tags: TestData.storageTag2,
TestData.capacityIops: 5000,
TestData.capacityBytes: 1099511627776,
TestData.hypervisor: "XenServer",
TestData.podId: 1,
TestData.zoneId: 1
},
TestData.virtualMachine: {
"name": "TestVM",
"displayname": "Test VM"
},
TestData.computeOffering1: {
"name": "SF_CO_1",
"displaytext": "SF_CO_1 (Min IOPS = 1,000; Max IOPS = 2,000)",
"cpunumber": 1,
"cpuspeed": 100,
"memory": 128,
"storagetype": "shared",
"customizediops": False,
"miniops": 1000,
"maxiops": 2000,
"hypervisorsnapshotreserve": 125,
TestData.tags: TestData.storageTag,
},
TestData.computeOffering2: {
"name": "SF_CO_2",
"displaytext": "SF_CO_2 (Min IOPS = 1,000; Max IOPS = 2,000)",
"cpunumber": 1,
"cpuspeed": 100,
"memory": 128,
"storagetype": "shared",
"customizediops": False,
"miniops": 1000,
"maxiops": 2000,
"hypervisorsnapshotreserve": 100,
TestData.tags: TestData.storageTag,
},
TestData.computeOffering3: {
"name": "SF_CO_3",
"displaytext": "SF_CO_3 Desc",
"cpunumber": 1,
"cpuspeed": 100,
"memory": 128,
"storagetype": "shared",
TestData.tags: TestData.storageTag2,
},
TestData.diskOffering1: {
"name": "SF_DO_1",
"displaytext": "SF_DO_1 (Min IOPS = 3,000; Max IOPS = 6,000)",
"disksize": 100,
"customizediops": False,
"miniops": 3000,
"maxiops": 6000,
"hypervisorsnapshotreserve": 125,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
TestData.diskOffering2: {
"name": "SF_DO_2",
"displaytext": "SF_DO_2 (Min IOPS = 3,000; Max IOPS = 6,000)",
"disksize": 100,
"customizediops": False,
"miniops": 3000,
"maxiops": 6000,
"hypervisorsnapshotreserve": 100,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
TestData.volume_1: {
TestData.diskName: "test-volume",
},
TestData.templateName: "CentOS 5.6(64-bit) no GUI (XenServer)",
TestData.zoneId: 1,
TestData.clusterId1: 1,
TestData.clusterId2: 2,
TestData.domainId: 1,
TestData.url: "192.168.129.50"
}
class TestVMMigrationWithStorage(cloudstackTestCase):
_sf_account_id_should_be_non_zero_int_err_msg = "The SolidFire account ID should be a non-zero integer."
@classmethod
def setUpClass(cls):
# Set up API client
testclient = super(TestVMMigrationWithStorage, cls).getClsTestClient()
cls.apiClient = testclient.getApiClient()
cls.dbConnection = testclient.getDbConnection()
cls.testdata = TestData().testdata
xenserver = cls.testdata[TestData.xenServer]
# Set up xenAPI connection
host_ip = "https://" + \
list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId1], name="XenServer-6.5-1")[0].ipaddress
# Set up XenAPI connection
cls.xen_session_1 = XenAPI.Session(host_ip)
cls.xen_session_1.xenapi.login_with_password(xenserver[TestData.username], xenserver[TestData.password])
# Set up xenAPI connection
host_ip = "https://" + \
list_hosts(cls.apiClient, clusterid=cls.testdata[TestData.clusterId2], name="XenServer-6.5-3")[0].ipaddress
# Set up XenAPI connection
cls.xen_session_2 = XenAPI.Session(host_ip)
cls.xen_session_2.xenapi.login_with_password(xenserver[TestData.username], xenserver[TestData.password])
# Set up SolidFire connection
cls.sf_client = sf_api.SolidFireAPI(endpoint_dict=cls.testdata[TestData.solidFire])
# Get Resources from Cloud Infrastructure
cls.zone = get_zone(cls.apiClient, zone_id=cls.testdata[TestData.zoneId])
cls.cluster_1 = list_clusters(cls.apiClient, id=cls.testdata[TestData.clusterId1])[0]
cls.cluster_2 = list_clusters(cls.apiClient, id=cls.testdata[TestData.clusterId2])[0]
cls.template = get_template(cls.apiClient, cls.zone.id, template_name=cls.testdata[TestData.templateName])
cls.domain = get_domain(cls.apiClient, cls.testdata[TestData.domainId])
# Create test account
cls.account = Account.create(
cls.apiClient,
cls.testdata["account"],
admin=1
)
# Set up connection to make customized API calls
cls.user = User.create(
cls.apiClient,
cls.testdata["user"],
account=cls.account.name,
domainid=cls.domain.id
)
url = cls.testdata[TestData.url]
api_url = "http://" + url + ":8080/client/api"
userkeys = User.registerUserKeys(cls.apiClient, cls.user.id)
cls.cs_api = SignedAPICall.CloudStack(api_url, userkeys.apikey, userkeys.secretkey)
primarystorage = cls.testdata[TestData.primaryStorage]
cls.primary_storage = StoragePool.create(
cls.apiClient,
primarystorage
)
cls.compute_offering_1 = ServiceOffering.create(
cls.apiClient,
cls.testdata[TestData.computeOffering1]
)
cls.compute_offering_2 = ServiceOffering.create(
cls.apiClient,
cls.testdata[TestData.computeOffering2]
)
cls.compute_offering_3 = ServiceOffering.create(
cls.apiClient,
cls.testdata[TestData.computeOffering3]
)
cls.disk_offering_1 = DiskOffering.create(
cls.apiClient,
cls.testdata[TestData.diskOffering1]
)
cls.disk_offering_2 = DiskOffering.create(
cls.apiClient,
cls.testdata[TestData.diskOffering2]
)
# Resources that are to be destroyed
cls._cleanup = [
cls.compute_offering_1,
cls.compute_offering_2,
cls.compute_offering_3,
cls.disk_offering_1,
cls.disk_offering_2,
cls.user,
cls.account
]
@classmethod
def tearDownClass(cls):
try:
cleanup_resources(cls.apiClient, cls._cleanup)
cls.primary_storage.delete(cls.apiClient)
except Exception as e:
logging.debug("Exception in tearDownClass(cls): %s" % e)
def setUp(self):
self.cleanup = []
def tearDown(self):
try:
cleanup_resources(self.apiClient, self.cleanup)
sf_util.purge_solidfire_volumes(self.sf_client)
except Exception as e:
logging.debug("Exception in tearDownClass(self): %s" % e)
def test_01_storage_migrate_root_and_data_disks(self):
src_host, dest_host = self._get_source_and_dest_hosts()
virtual_machine = VirtualMachine.create(
self.apiClient,
self.testdata[TestData.virtualMachine],
accountid=self.account.name,
zoneid=self.zone.id,
serviceofferingid=self.compute_offering_1.id,
templateid=self.template.id,
domainid=self.domain.id,
hostid=src_host.id,
startvm=True
)
self.cleanup.append(virtual_machine)
cs_root_volume = list_volumes(self.apiClient, listall=True, virtualmachineid=virtual_machine.id)[0]
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self,
TestVMMigrationWithStorage._sf_account_id_should_be_non_zero_int_err_msg)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
sf_root_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_root_volume.name, self)
cs_data_volume = Volume.create(
self.apiClient,
self.testdata[TestData.volume_1],
account=self.account.name,
domainid=self.domain.id,
zoneid=self.zone.id,
diskofferingid=self.disk_offering_1.id
)
self.cleanup.append(cs_data_volume)
cs_data_volume = virtual_machine.attach_volume(
self.apiClient,
cs_data_volume
)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
sf_data_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_data_volume.name, self)
sf_root_volume, sf_data_volume = self._migrate_and_verify(virtual_machine, dest_host, cs_root_volume, cs_data_volume, sf_account_id,
sf_root_volume, sf_data_volume, self.xen_session_1, self.xen_session_2)
src_host, dest_host = dest_host, src_host
self._migrate_and_verify(virtual_machine, dest_host, cs_root_volume, cs_data_volume, sf_account_id, sf_root_volume, sf_data_volume,
self.xen_session_2, self.xen_session_1)
def test_02_storage_migrate_root_and_data_disks(self):
primarystorage2 = self.testdata[TestData.primaryStorage2]
primary_storage_2 = StoragePool.create(
self.apiClient,
primarystorage2,
clusterid=self.cluster_1.id
)
primary_storage_3 = StoragePool.create(
self.apiClient,
primarystorage2,
clusterid=self.cluster_2.id
)
src_host, dest_host = self._get_source_and_dest_hosts()
virtual_machine = VirtualMachine.create(
self.apiClient,
self.testdata[TestData.virtualMachine],
accountid=self.account.name,
zoneid=self.zone.id,
serviceofferingid=self.compute_offering_3.id,
templateid=self.template.id,
domainid=self.domain.id,
hostid=src_host.id,
startvm=True
)
cs_data_volume = Volume.create(
self.apiClient,
self.testdata[TestData.volume_1],
account=self.account.name,
domainid=self.domain.id,
zoneid=self.zone.id,
diskofferingid=self.disk_offering_1.id
)
self.cleanup = [
virtual_machine,
cs_data_volume,
primary_storage_2,
primary_storage_3
]
cs_data_volume = virtual_machine.attach_volume(
self.apiClient,
cs_data_volume
)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self,
TestVMMigrationWithStorage._sf_account_id_should_be_non_zero_int_err_msg)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
sf_data_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_data_volume.name, self)
sf_data_volume = self._migrate_and_verify_one_disk_only(virtual_machine, dest_host, cs_data_volume, sf_account_id,
sf_data_volume, self.xen_session_1, self.xen_session_2)
src_host, dest_host = dest_host, src_host
self._migrate_and_verify_one_disk_only(virtual_machine, dest_host, cs_data_volume, sf_account_id, sf_data_volume,
self.xen_session_2, self.xen_session_1)
# The hypervisor snapshot reserve isn't large enough for either the compute or disk offering.
def test_03_storage_migrate_root_and_data_disks_fail(self):
self._execute_migration_failure(self.compute_offering_2.id, self.disk_offering_2.id)
# The hypervisor snapshot reserve isn't large enough for the compute offering.
def test_04_storage_migrate_root_disk_fails(self):
self._execute_migration_failure(self.compute_offering_2.id, self.disk_offering_1.id)
# The hypervisor snapshot reserve isn't large enough for the disk offering.
def test_05_storage_migrate_data_disk_fails(self):
self._execute_migration_failure(self.compute_offering_1.id, self.disk_offering_2.id)
def _execute_migration_failure(self, compute_offering_id, disk_offering_id):
src_host, dest_host = self._get_source_and_dest_hosts()
virtual_machine = VirtualMachine.create(
self.apiClient,
self.testdata[TestData.virtualMachine],
accountid=self.account.name,
zoneid=self.zone.id,
serviceofferingid=compute_offering_id,
templateid=self.template.id,
domainid=self.domain.id,
hostid=src_host.id,
startvm=True
)
self.cleanup.append(virtual_machine)
cs_root_volume = list_volumes(self.apiClient, listall=True, virtualmachineid=virtual_machine.id)[0]
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self,
TestVMMigrationWithStorage._sf_account_id_should_be_non_zero_int_err_msg)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
sf_root_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_root_volume.name, self)
cs_data_volume = Volume.create(
self.apiClient,
self.testdata[TestData.volume_1],
account=self.account.name,
domainid=self.domain.id,
zoneid=self.zone.id,
diskofferingid=disk_offering_id
)
self.cleanup.append(cs_data_volume)
cs_data_volume = virtual_machine.attach_volume(
self.apiClient,
cs_data_volume
)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
sf_data_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_data_volume.name, self)
self._fail_migrate_and_verify(virtual_machine, dest_host, cs_root_volume, cs_data_volume, sf_account_id,
sf_root_volume, sf_data_volume, self.xen_session_1, self.xen_session_2)
def _get_source_and_dest_hosts(self):
hosts = list_hosts(self.apiClient)
for host in hosts:
if host.name == "XenServer-6.5-1":
src_host = host
elif host.name == "XenServer-6.5-3":
dest_host = host
self.assertIsNotNone(src_host, "Could not locate the source host")
self.assertIsNotNone(dest_host, "Could not locate the destination host")
return src_host, dest_host
def _migrate_and_verify(self, virtual_machine, dest_host, cs_root_volume, cs_data_volume, sf_account_id, src_sf_root_volume, src_sf_data_volume,
src_xen_session, dest_xen_session):
self._verifyFields(cs_root_volume, src_sf_root_volume)
self._verifyFields(cs_data_volume, src_sf_data_volume)
virtual_machine.migrate_vm_with_volume(self.apiClient, dest_host.id)
cs_root_volume = self._get_updated_cs_volume(cs_root_volume.id)
cs_data_volume = self._get_updated_cs_volume(cs_data_volume.id)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
dest_sf_root_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_root_volume.name, self)
dest_sf_data_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_data_volume.name, self)
self._verifyFields(cs_root_volume, dest_sf_root_volume)
self._verifyFields(cs_data_volume, dest_sf_data_volume)
self._verify_no_basic_volume_details()
self._verify_different_volume_access_groups(src_sf_root_volume, dest_sf_root_volume)
self._verify_different_volume_access_groups(src_sf_data_volume, dest_sf_data_volume)
self._verify_same_account(src_sf_root_volume, dest_sf_root_volume)
self._verify_same_account(src_sf_data_volume, dest_sf_data_volume)
self._verifySfVolumeIds(src_sf_root_volume, dest_sf_root_volume)
self._verifySfVolumeIds(src_sf_data_volume, dest_sf_data_volume)
self._verify_xenserver_state(src_xen_session, src_sf_root_volume, dest_xen_session, dest_sf_root_volume)
self._verify_xenserver_state(src_xen_session, src_sf_data_volume, dest_xen_session, dest_sf_data_volume)
return dest_sf_root_volume, dest_sf_data_volume
def _migrate_and_verify_one_disk_only(self, virtual_machine, dest_host, cs_volume, sf_account_id, src_sf_volume, src_xen_session, dest_xen_session):
self._verifyFields(cs_volume, src_sf_volume)
virtual_machine.migrate_vm_with_volume(self.apiClient, dest_host.id)
cs_volume = self._get_updated_cs_volume(cs_volume.id)
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
dest_sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_volume.name, self)
self._verifyFields(cs_volume, dest_sf_volume)
self._verify_no_basic_volume_details()
self._verify_different_volume_access_groups(src_sf_volume, dest_sf_volume)
self._verify_same_account(src_sf_volume, dest_sf_volume)
self._verifySfVolumeIds(src_sf_volume, dest_sf_volume)
self._verify_xenserver_state(src_xen_session, src_sf_volume, dest_xen_session, dest_sf_volume)
return dest_sf_volume
def _fail_migrate_and_verify(self, virtual_machine, dest_host, cs_root_volume, cs_data_volume, sf_account_id, src_sf_root_volume, src_sf_data_volume,
src_xen_session, dest_xen_session):
self._verifyFields(cs_root_volume, src_sf_root_volume)
self._verifyFields(cs_data_volume, src_sf_data_volume)
class MigrationException(Exception):
def __init__(self, *args, **kwargs):
Exception.__init__(self, *args, **kwargs)
try:
virtual_machine.migrate_vm_with_volume(self.apiClient, dest_host.id)
raise MigrationException("The migration did not fail (as expected).")
except MigrationException:
raise
except Exception:
pass
self._verify_no_basic_volume_details()
cs_root_volume_refreshed = self._get_updated_cs_volume(cs_root_volume.id)
cs_data_volume_refreshed = self._get_updated_cs_volume(cs_data_volume.id)
self._verifyFields(cs_root_volume_refreshed, src_sf_root_volume)
self._verifyFields(cs_data_volume_refreshed, src_sf_data_volume)
sf_volumes = sf_util.get_not_active_sf_volumes(self.sf_client, sf_account_id)
dest_sf_root_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_root_volume.name, self)
dest_sf_data_volume = sf_util.check_and_get_sf_volume(sf_volumes, cs_data_volume.name, self)
self._verify_xenserver_state(dest_xen_session, dest_sf_root_volume, src_xen_session, src_sf_root_volume)
self._verify_xenserver_state(dest_xen_session, dest_sf_data_volume, src_xen_session, src_sf_data_volume)
def _verify_different_volume_access_groups(self, src_sf_volume, dest_sf_volume):
src_vags = src_sf_volume['volumeAccessGroups']
sf_util.check_list(src_vags, 1, self, "'src_vags' should be a list with only one element in it.")
dest_vags = dest_sf_volume['volumeAccessGroups']
sf_util.check_list(dest_vags, 1, self, "'dest_vags' should be a list with only one element in it.")
self.assertNotEqual(src_vags[0], dest_vags[0], "The source and destination volumes should not be in the same volume access group.")
def _get_updated_cs_volume(self, cs_volume_id):
return list_volumes(self.apiClient, listall=True, id=cs_volume_id)[0]
def _verify_same_account(self, src_sf_volume, dest_sf_volume):
self.assertEqual(src_sf_volume['accountID'], dest_sf_volume['accountID'], "The source and destination volumes should be in the same SolidFire account.")
def _verifySfVolumeIds(self, src_sf_volume, dest_sf_volume):
self.assert_(src_sf_volume['volumeID'] < dest_sf_volume['volumeID'],
"The destination SolidFire root volume's ID should be greater than the id of the source one.")
# verify the name, folder, and iscsi_name
def _verifyFields(self, cs_volume, sf_volume):
self.assert_(cs_volume.name == sf_volume['name'], "The CloudStack volume name does not match the SolidFire volume name.")
cs_volume_folder = self._get_cs_volume_folder(cs_volume.id)
self.assert_(int(cs_volume_folder) == sf_volume['volumeID'], "The CloudStack folder name does not match the SolidFire volume ID.")
cs_volume_iscsi_name = self._get_cs_volume_iscsi_name(cs_volume.id)
self.assert_(cs_volume_iscsi_name == sf_util.format_iqn(sf_volume['iqn']), "The CloudStack volume iscsi_name does not match the SolidFire volume IQN.")
def _get_cs_volume_property(self, cs_volume_id, volume_property):
sql_query = "Select " + volume_property + " From volumes Where uuid = '" + cs_volume_id + "'"
# make sure you can connect to MySQL: https://teamtreehouse.com/community/cant-connect-remotely-to-mysql-server-with-mysql-workbench
sql_result = self.dbConnection.execute(sql_query)
return sql_result[0][0]
def _get_cs_volume_folder(self, cs_volume_id):
return self._get_cs_volume_property(cs_volume_id, "folder")
def _get_cs_volume_iscsi_name(self, cs_volume_id):
return self._get_cs_volume_property(cs_volume_id, "iscsi_name")
def _verify_no_basic_volume_details(self):
sql_query = "Select id From volume_details Where name like 'basic_'"
# make sure you can connect to MySQL: https://teamtreehouse.com/community/cant-connect-remotely-to-mysql-server-with-mysql-workbench
sql_result = self.dbConnection.execute(sql_query)
sf_util.check_list(sql_result, 0, self, "The cloud.volume_details table should not have any name fields that start with 'basic_'.")
def _verify_xenserver_state(self, xen_session_1, sf_volume_1, xen_session_2, sf_volume_2):
sr_name = sf_util.format_iqn(sf_volume_1["iqn"])
sf_util.check_xen_sr(sr_name, xen_session_1, self, False)
sr_name = sf_util.format_iqn(sf_volume_2["iqn"])
sf_util.check_xen_sr(sr_name, xen_session_2, self)

View File

@ -20,6 +20,8 @@ import random
import SignedAPICall
import XenAPI
from util import sf_util
# All tests inherit from cloudstackTestCase
from marvin.cloudstackTestCase import cloudstackTestCase
@ -36,8 +38,10 @@ from marvin.lib.utils import cleanup_resources
from solidfire import solidfire_element_api as sf_api
# on April 15, 2016: Ran 2 tests in 800.299s with three hosts
# on May 2, 2016: Ran 2 tests in 789.729s with two hosts
# Prerequisites:
# Only one zone
# Only one pod
# Only one cluster
class TestData:
@ -328,7 +332,7 @@ class TestVMSnapshots(cloudstackTestCase):
cls.primary_storage.delete(cls.apiClient)
cls._purge_solidfire_volumes()
sf_util.purge_solidfire_volumes(cls.sf_client)
except Exception as e:
logging.debug("Exception in tearDownClass(cls): %s" % e)
@ -346,7 +350,7 @@ class TestVMSnapshots(cloudstackTestCase):
root_volumes = list_volumes(self.apiClient, type="ROOT", listAll="true")
self._check_list(root_volumes, 1, TestVMSnapshots._should_only_be_one_root_volume_err_msg)
sf_util.check_list(root_volumes, 1, self, TestVMSnapshots._should_only_be_one_root_volume_err_msg)
root_volume = root_volumes[0]
@ -355,7 +359,7 @@ class TestVMSnapshots(cloudstackTestCase):
sf_iscsi_name_result = self.cs_api.getVolumeiScsiName(volume_id)
sf_iscsi_name = sf_iscsi_name_result['apivolumeiscsiname']['volumeiScsiName']
self._check_iscsi_name(sf_iscsi_name)
sf_util.check_iscsi_name(sf_iscsi_name, self)
root_volume_path_1 = self._get_path(volume_id)
@ -388,7 +392,7 @@ class TestVMSnapshots(cloudstackTestCase):
xen_vdis = self.xen_session.xenapi.SR.get_VDIs(xen_sr)
self._check_list(xen_vdis, 3, TestVMSnapshots._should_be_three_vdis_err_msg)
sf_util.check_list(xen_vdis, 3, self, TestVMSnapshots._should_be_three_vdis_err_msg)
vdis_after_create = self._get_vdis(xen_vdis)
@ -411,7 +415,7 @@ class TestVMSnapshots(cloudstackTestCase):
list_vm_snapshots = VmSnapshot.list(self.apiClient, listAll="true")
self._check_list(list_vm_snapshots, 1, TestVMSnapshots._should_only_be_one_vm_snapshot_err_msg)
sf_util.check_list(list_vm_snapshots, 1, self, TestVMSnapshots._should_only_be_one_vm_snapshot_err_msg)
root_volume_path_3 = self._get_path(volume_id)
@ -423,7 +427,7 @@ class TestVMSnapshots(cloudstackTestCase):
xen_vdis = self.xen_session.xenapi.SR.get_VDIs(xen_sr)
self._check_list(xen_vdis, 3, TestVMSnapshots._should_be_three_vdis_err_msg)
sf_util.check_list(xen_vdis, 3, self, TestVMSnapshots._should_be_three_vdis_err_msg)
vdis_after_revert = self._get_vdis(xen_vdis)
@ -470,7 +474,7 @@ class TestVMSnapshots(cloudstackTestCase):
xen_vdis = self.xen_session.xenapi.SR.get_VDIs(xen_sr)
self._check_list(xen_vdis, 1, TestVMSnapshots._should_only_be_one_vdi_err_msg)
sf_util.check_list(xen_vdis, 1, self, TestVMSnapshots._should_only_be_one_vdi_err_msg)
vdis_after_delete = self._get_vdis(xen_vdis, True)
@ -505,7 +509,7 @@ class TestVMSnapshots(cloudstackTestCase):
root_volumes = list_volumes(self.apiClient, type="ROOT", listAll="true")
self._check_list(root_volumes, 1, TestVMSnapshots._should_only_be_one_root_volume_err_msg)
sf_util.check_list(root_volumes, 1, self, TestVMSnapshots._should_only_be_one_root_volume_err_msg)
root_volume = root_volumes[0]
@ -514,13 +518,13 @@ class TestVMSnapshots(cloudstackTestCase):
sf_iscsi_name_result = self.cs_api.getVolumeiScsiName(root_volume_id)
sf_iscsi_root_volume_name = sf_iscsi_name_result['apivolumeiscsiname']['volumeiScsiName']
self._check_iscsi_name(sf_iscsi_root_volume_name)
sf_util.check_iscsi_name(sf_iscsi_root_volume_name, self)
root_volume_path_1 = self._get_path(root_volume_id)
data_volumes = list_volumes(self.apiClient, type="DATADISK", listAll="true")
self._check_list(data_volumes, 1, "There should only be one data volume.")
sf_util.check_list(data_volumes, 1, self, "There should only be one data volume.")
data_volume = data_volumes[0]
@ -529,7 +533,7 @@ class TestVMSnapshots(cloudstackTestCase):
sf_iscsi_name_result = self.cs_api.getVolumeiScsiName(data_volume_id)
sf_iscsi_data_volume_name = sf_iscsi_name_result['apivolumeiscsiname']['volumeiScsiName']
self._check_iscsi_name(sf_iscsi_data_volume_name)
sf_util.check_iscsi_name(sf_iscsi_data_volume_name, self)
data_volume_path_1 = self._get_path(data_volume_id)
@ -570,7 +574,7 @@ class TestVMSnapshots(cloudstackTestCase):
root_volume_xen_vdis = self.xen_session.xenapi.SR.get_VDIs(root_volume_xen_sr)
self._check_list(root_volume_xen_vdis, 3, TestVMSnapshots._should_be_three_vdis_err_msg)
sf_util.check_list(root_volume_xen_vdis, 3, self, TestVMSnapshots._should_be_three_vdis_err_msg)
root_volume_vdis_after_create = self._get_vdis(root_volume_xen_vdis)
@ -586,7 +590,7 @@ class TestVMSnapshots(cloudstackTestCase):
data_volume_xen_vdis = self.xen_session.xenapi.SR.get_VDIs(data_volume_xen_sr)
self._check_list(data_volume_xen_vdis, 3, TestVMSnapshots._should_be_three_vdis_err_msg)
sf_util.check_list(data_volume_xen_vdis, 3, self, TestVMSnapshots._should_be_three_vdis_err_msg)
data_volume_vdis_after_create = self._get_vdis(data_volume_xen_vdis)
@ -609,7 +613,7 @@ class TestVMSnapshots(cloudstackTestCase):
list_vm_snapshots = VmSnapshot.list(self.apiClient, listAll="true")
self._check_list(list_vm_snapshots, 1, TestVMSnapshots._should_only_be_one_vm_snapshot_err_msg)
sf_util.check_list(list_vm_snapshots, 1, self, TestVMSnapshots._should_only_be_one_vm_snapshot_err_msg)
root_volume_path_3 = self._get_path(root_volume_id)
@ -621,7 +625,7 @@ class TestVMSnapshots(cloudstackTestCase):
root_volume_xen_vdis = self.xen_session.xenapi.SR.get_VDIs(root_volume_xen_sr)
self._check_list(root_volume_xen_vdis, 3, TestVMSnapshots._should_be_three_vdis_err_msg)
sf_util.check_list(root_volume_xen_vdis, 3, self, TestVMSnapshots._should_be_three_vdis_err_msg)
root_volume_vdis_after_revert = self._get_vdis(root_volume_xen_vdis)
@ -653,7 +657,7 @@ class TestVMSnapshots(cloudstackTestCase):
data_volume_xen_vdis = self.xen_session.xenapi.SR.get_VDIs(data_volume_xen_sr)
self._check_list(data_volume_xen_vdis, 3, TestVMSnapshots._should_be_three_vdis_err_msg)
sf_util.check_list(data_volume_xen_vdis, 3, self, TestVMSnapshots._should_be_three_vdis_err_msg)
data_volume_vdis_after_revert = self._get_vdis(data_volume_xen_vdis)
@ -700,7 +704,7 @@ class TestVMSnapshots(cloudstackTestCase):
root_volume_xen_vdis = self.xen_session.xenapi.SR.get_VDIs(root_volume_xen_sr)
self._check_list(root_volume_xen_vdis, 1, TestVMSnapshots._should_only_be_one_vdi_err_msg)
sf_util.check_list(root_volume_xen_vdis, 1, self, TestVMSnapshots._should_only_be_one_vdi_err_msg)
root_volume_vdis_after_delete = self._get_vdis(root_volume_xen_vdis, True)
@ -720,7 +724,7 @@ class TestVMSnapshots(cloudstackTestCase):
data_volume_xen_vdis = self.xen_session.xenapi.SR.get_VDIs(data_volume_xen_sr)
self._check_list(data_volume_xen_vdis, 1, TestVMSnapshots._should_only_be_one_vdi_err_msg)
sf_util.check_list(data_volume_xen_vdis, 1, self, TestVMSnapshots._should_only_be_one_vdi_err_msg)
data_volume_vdis_after_delete = self._get_vdis(data_volume_xen_vdis, True)
@ -745,7 +749,7 @@ class TestVMSnapshots(cloudstackTestCase):
return path_result['apipathforvolume']['path']
def _verify_vm_snapshot(self, list_vm_snapshots, vm_snapshot):
self._check_list(list_vm_snapshots, 1, TestVMSnapshots._should_only_be_one_vm_snapshot_err_msg)
sf_util.check_list(list_vm_snapshots, 1, self, TestVMSnapshots._should_only_be_one_vm_snapshot_err_msg)
vm_snapshot_from_list = list_vm_snapshots[0]
@ -767,26 +771,6 @@ class TestVMSnapshots(cloudstackTestCase):
"The snapshot is not in the 'Ready' state."
)
def _check_iscsi_name(self, sf_iscsi_name):
self.assertEqual(
sf_iscsi_name[0],
"/",
"The iSCSI name needs to start with a forward slash."
)
def _check_list(self, in_list, expected_size_of_list, err_msg):
self.assertEqual(
isinstance(in_list, list),
True,
"'in_list' is not a list."
)
self.assertEqual(
len(in_list),
expected_size_of_list,
err_msg
)
def _get_vdis(self, xen_vdis, only_active_expected=False):
expected_number_of_vdis = 1 if only_active_expected else 3
@ -852,11 +836,3 @@ class TestVMSnapshots(cloudstackTestCase):
vdis.base_vdi = base_vdi
return vdis
@classmethod
def _purge_solidfire_volumes(cls):
deleted_volumes = cls.sf_client.list_deleted_volumes()
for deleted_volume in deleted_volumes:
cls.sf_client.purge_deleted_volume(deleted_volume['volumeID'])

View File

@ -20,6 +20,8 @@ import random
import SignedAPICall
import XenAPI
from util import sf_util
# All tests inherit from cloudstackTestCase
from marvin.cloudstackTestCase import cloudstackTestCase
@ -39,11 +41,13 @@ from marvin.lib.utils import cleanup_resources
from solidfire import solidfire_element_api as sf_api
# on April 14, 2016: Ran 11 tests in 2494.043s with three hosts (resign = True)
# on April 14, 2016: Ran 11 tests in 2033.516s with three hosts (resign = False)
# on May 2, 2016: Ran 11 tests in 2352.461s with two hosts (resign = True)
# on May 2, 2016: Ran 11 tests in 1982.066s with two hosts (resign = False)
# Prerequisites:
# Only one zone
# Only one pod
# Only one cluster
#
# Running the tests:
# Change the "supports_resign" variable to True or False as desired.
class TestData():
@ -145,7 +149,7 @@ class TestData():
"miniops": "10000",
"maxiops": "15000",
"hypervisorsnapshotreserve": 200,
"tags": "SolidFire_SAN_1"
TestData.tags: TestData.storageTag
},
TestData.diskOffering: {
"name": "SF_DO_1",
@ -158,71 +162,6 @@ class TestData():
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
"testdiskofferings": {
"customiopsdo": {
"name": "SF_Custom_Iops_DO",
"displaytext": "Customized Iops DO",
"disksize": 128,
"customizediops": True,
"miniops": 500,
"maxiops": 1000,
"hypervisorsnapshotreserve": 200,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
"customsizedo": {
"name": "SF_Custom_Size_DO",
"displaytext": "Customized Size DO",
"disksize": 175,
"customizediops": False,
"miniops": 500,
"maxiops": 1000,
"hypervisorsnapshotreserve": 200,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
"customsizeandiopsdo": {
"name": "SF_Custom_Iops_Size_DO",
"displaytext": "Customized Size and Iops DO",
"disksize": 200,
"customizediops": True,
"miniops": 400,
"maxiops": 800,
"hypervisorsnapshotreserve": 200,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
"newiopsdo": {
"name": "SF_New_Iops_DO",
"displaytext": "New Iops (min=350, max = 700)",
"disksize": 128,
"miniops": 350,
"maxiops": 700,
"hypervisorsnapshotreserve": 200,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
"newsizedo": {
"name": "SF_New_Size_DO",
"displaytext": "New Size: 175",
"disksize": 175,
"miniops": 400,
"maxiops": 800,
"hypervisorsnapshotreserve": 200,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
},
"newsizeandiopsdo": {
"name": "SF_New_Size_Iops_DO",
"displaytext": "New Size and Iops",
"disksize": 200,
"miniops": 200,
"maxiops": 400,
"hypervisorsnapshotreserve": 200,
TestData.tags: TestData.storageTag,
"storagetype": "shared"
}
},
TestData.volume_1: {
TestData.diskName: "test-volume",
},
@ -241,14 +180,11 @@ class TestVolumes(cloudstackTestCase):
_should_only_be_one_vm_in_list_err_msg = "There should only be one VM in this list."
_should_only_be_one_volume_in_list_err_msg = "There should only be one volume in this list."
_sf_account_id_should_be_non_zero_int_err_msg = "The SolidFire account ID should be a non-zero integer."
_vag_id_should_be_non_zero_int_err_msg = "The SolidFire VAG ID should be a non-zero integer."
_volume_size_should_be_non_zero_int_err_msg = "The SolidFire volume size should be a non-zero integer."
_volume_vm_id_and_vm_id_do_not_match_err_msg = "The volume's VM ID and the VM's ID do not match."
_vm_not_in_running_state_err_msg = "The VM is not in the 'Running' state."
_vm_not_in_stopped_state_err_msg = "The VM is not in the 'Stopped' state."
_sr_not_shared_err_msg = "The SR is not shared."
_volume_response_should_not_be_zero_err_msg = "The length of the response for the SolidFire-volume query should not be zero."
_list_should_be_empty = "The list should be empty."
_volume_should_not_be_in_a_vag = "The volume should not be in a volume access group."
@classmethod
@ -262,7 +198,7 @@ class TestVolumes(cloudstackTestCase):
cls.supports_resign = True
cls._set_supports_resign()
sf_util.set_supports_resign(cls.supports_resign, cls.dbConnection)
# Set up xenAPI connection
host_ip = "https://" + \
@ -368,7 +304,7 @@ class TestVolumes(cloudstackTestCase):
cls.primary_storage.delete(cls.apiClient)
cls._purge_solidfire_volumes()
sf_util.purge_solidfire_volumes(cls.sf_client)
except Exception as e:
logging.debug("Exception in tearDownClass(cls): %s" % e)
@ -387,9 +323,9 @@ class TestVolumes(cloudstackTestCase):
if self.supports_resign == False:
return
sf_volumes = self._get_sf_volumes()
sf_volumes = self._get_active_sf_volumes()
sf_volume = self._check_and_get_sf_volume(sf_volumes, TestData.templateCacheName)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, TestData.templateCacheName, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -451,21 +387,23 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_volume_size = self._get_volume_size_with_hsr(new_volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, new_volume, self)
sf_vag_id = self._get_vag_id()
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_iscsi_name = self._get_iqn(new_volume)
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, new_volume, self)
sf_volume = self._check_and_get_sf_volume(sf_volumes, newvolume.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, newvolume, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, newvolume.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, newvolume, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -481,9 +419,9 @@ class TestVolumes(cloudstackTestCase):
self.virtual_machine.start(self.apiClient)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_vag_id = self._get_vag_id()
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
self._check_and_get_cs_volume(self.volume.id, self.testdata[TestData.volume_1][TestData.diskName])
@ -516,17 +454,19 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -559,9 +499,9 @@ class TestVolumes(cloudstackTestCase):
str(vm.state)
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -600,11 +540,11 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -614,9 +554,9 @@ class TestVolumes(cloudstackTestCase):
self.virtual_machine.start(self.apiClient)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_vag_id = self._get_vag_id()
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
self._check_and_get_cs_volume(self.volume.id, self.testdata[TestData.volume_1][TestData.diskName])
@ -649,17 +589,19 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -674,17 +616,19 @@ class TestVolumes(cloudstackTestCase):
vm = self._get_vm(self.virtual_machine.id)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -694,9 +638,9 @@ class TestVolumes(cloudstackTestCase):
self.virtual_machine.start(self.apiClient)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_vag_id = self._get_vag_id()
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
self._check_and_get_cs_volume(self.volume.id, self.testdata[TestData.volume_1][TestData.diskName])
@ -729,17 +673,19 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -772,9 +718,9 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -796,9 +742,9 @@ class TestVolumes(cloudstackTestCase):
vm = self._get_vm(self.virtual_machine.id)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -814,9 +760,9 @@ class TestVolumes(cloudstackTestCase):
self.virtual_machine.start(self.apiClient)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_vag_id = self._get_vag_id()
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
self._check_and_get_cs_volume(self.volume.id, self.testdata[TestData.volume_1][TestData.diskName])
@ -849,17 +795,19 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -894,9 +842,9 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_stopped_state_err_msg
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -918,9 +866,9 @@ class TestVolumes(cloudstackTestCase):
vm = self._get_vm(self.virtual_machine.id)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -936,9 +884,9 @@ class TestVolumes(cloudstackTestCase):
self.virtual_machine.stop(self.apiClient)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_vag_id = self._get_vag_id()
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
self._check_and_get_cs_volume(self.volume.id, self.testdata[TestData.volume_1][TestData.diskName])
@ -971,17 +919,19 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_stopped_state_err_msg
)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -1003,17 +953,19 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_iscsi_name = self._get_iqn(self.volume)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -1061,21 +1013,23 @@ class TestVolumes(cloudstackTestCase):
TestVolumes._vm_not_in_running_state_err_msg
)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_vag_id = self._get_vag_id()
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_iscsi_name = self._get_iqn(self.volume)
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -1114,11 +1068,11 @@ class TestVolumes(cloudstackTestCase):
"Check if VM was actually expunged"
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -1174,21 +1128,23 @@ class TestVolumes(cloudstackTestCase):
str(vm.state)
)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_volume_size = self._get_volume_size_with_hsr(new_volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, new_volume, self)
sf_vag_id = self._get_vag_id()
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_iscsi_name = self._get_iqn(new_volume)
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, new_volume, self)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_util.check_vag(sf_volume, sf_vag_id, self)
self._check_xen_sr(sf_iscsi_name)
@ -1219,11 +1175,11 @@ class TestVolumes(cloudstackTestCase):
str(vm.state)
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
@ -1246,9 +1202,9 @@ class TestVolumes(cloudstackTestCase):
"Check volume was deleted"
)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
self._check_and_get_sf_volume(sf_volumes, vol.name, False)
sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self, False)
@attr(hypervisor='XenServer')
def test_09_attach_volumes_multiple_accounts(self):
@ -1342,39 +1298,43 @@ class TestVolumes(cloudstackTestCase):
str(test_vm.state)
)
sf_vag_id = self._get_vag_id()
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_volumes = self._get_sf_volumes(sf_account_id)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
sf_volume_size = self._get_volume_size_with_hsr(vol)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, vol, self)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_iscsi_name = self._get_iqn(self.volume)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
self._check_xen_sr(sf_iscsi_name)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_vag(sf_volume, sf_vag_id, self)
sf_test_account_id = self._get_sf_account_id(self.primary_storage.id, test_account.id)
sf_test_account_id = sf_util.get_sf_account_id(self.cs_api, test_account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_test_volumes = self._get_sf_volumes(sf_test_account_id)
sf_test_volumes = self._get_active_sf_volumes(sf_test_account_id)
sf_test_volume = self._check_and_get_sf_volume(sf_test_volumes, test_vol.name)
sf_test_volume = sf_util.check_and_get_sf_volume(sf_test_volumes, test_vol.name, self)
sf_test_volume_size = self._get_volume_size_with_hsr(test_vol)
sf_test_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, test_vol, self)
self._check_size_and_iops(sf_test_volume, test_vol, sf_test_volume_size)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_test_volume_size)
sf_test_iscsi_name = self._get_iqn(test_volume)
sf_util.check_size_and_iops(sf_test_volume, test_vol, sf_test_volume_size, self)
sf_test_iscsi_name = sf_util.get_iqn(self.cs_api, test_volume, self)
self._check_xen_sr(sf_test_iscsi_name)
self._check_vag(sf_test_volume, sf_vag_id)
sf_util.check_vag(sf_test_volume, sf_vag_id, self)
@attr(hypervisor='XenServer')
def test_10_attach_more_than_one_disk_to_VM(self):
@ -1417,66 +1377,50 @@ class TestVolumes(cloudstackTestCase):
vol_2 = self._check_and_get_cs_volume(volume_2.id, self.testdata[TestData.volume_2][TestData.diskName])
sf_account_id = self._get_sf_account_id(self.primary_storage.id, self.account.id)
sf_account_id = sf_util.get_sf_account_id(self.cs_api, self.account.id, self.primary_storage.id, self, TestVolumes._sf_account_id_should_be_non_zero_int_err_msg)
sf_volume_size = self._get_volume_size_with_hsr(self.volume)
sf_volume_size = sf_util.get_volume_size_with_hsr(self.cs_api, self.volume, self)
sf_volume_2_size = self._get_volume_size_with_hsr(volume_2)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_size)
sf_vag_id = self._get_vag_id()
sf_volume_2_size = sf_util.get_volume_size_with_hsr(self.cs_api, volume_2, self)
sf_volumes = self._get_sf_volumes(sf_account_id)
self._verify_hsr(self.disk_offering.disksize, self.disk_offering.hypervisorsnapshotreserve, sf_volume_2_size)
sf_volume = self._check_and_get_sf_volume(sf_volumes, vol.name)
sf_vag_id = sf_util.get_vag_id(self.cs_api, self.cluster.id, self.primary_storage.id, self)
self._check_size_and_iops(sf_volume, vol, sf_volume_size)
sf_volumes = self._get_active_sf_volumes(sf_account_id)
sf_iscsi_name = self._get_iqn(self.volume)
sf_volume = sf_util.check_and_get_sf_volume(sf_volumes, vol.name, self)
sf_util.check_size_and_iops(sf_volume, vol, sf_volume_size, self)
sf_iscsi_name = sf_util.get_iqn(self.cs_api, self.volume, self)
self._check_xen_sr(sf_iscsi_name)
self._check_vag(sf_volume, sf_vag_id)
sf_util.check_vag(sf_volume, sf_vag_id, self)
sf_volume_2 = self._check_and_get_sf_volume(sf_volumes, vol_2.name)
sf_volume_2 = sf_util.check_and_get_sf_volume(sf_volumes, vol_2.name, self)
self._check_size_and_iops(sf_volume_2, vol_2, sf_volume_2_size)
sf_util.check_size_and_iops(sf_volume_2, vol_2, sf_volume_2_size, self)
sf_iscsi_name_2 = self._get_iqn(volume_2)
sf_iscsi_name_2 = sf_util.get_iqn(self.cs_api, volume_2, self)
self._check_xen_sr(sf_iscsi_name_2)
self._check_vag(sf_volume_2, sf_vag_id)
sf_util.check_vag(sf_volume_2, sf_vag_id, self)
self.virtual_machine.detach_volume(self.apiClient, volume_2)
'''
@attr(hypervisor = 'XenServer')
def _test_11_attach_disk_to_running_vm_change_iops(self):
def test_11_attach_disk_to_running_vm_change_iops(self):
Attach a disk to a running VM, then change iops
self.custom_iops_disk_offering = DiskOffering.create(
)'''
def _check_list(self, in_list, expected_size_of_list, err_msg):
self.assertEqual(
isinstance(in_list, list),
True,
"'in_list' is not a list."
)
self.assertEqual(
len(in_list),
expected_size_of_list,
err_msg
)
def _check_iscsi_name(self, sf_iscsi_name):
self.assertEqual(
sf_iscsi_name[0],
"/",
"The iSCSI name needs to start with a forward slash."
)
def _check_volume(self, volume, volume_name):
self.assertTrue(
volume.name.startswith(volume_name),
@ -1501,45 +1445,13 @@ class TestVolumes(cloudstackTestCase):
"The storage type is incorrect."
)
def _check_size_and_iops(self, sf_volume, volume, size):
self.assertEqual(
sf_volume['qos']['minIOPS'],
volume.miniops,
"Check QOS - Min IOPS: " + str(sf_volume['qos']['minIOPS'])
)
self.assertEqual(
sf_volume['qos']['maxIOPS'],
volume.maxiops,
"Check QOS - Max IOPS: " + str(sf_volume['qos']['maxIOPS'])
)
self.assertEqual(
sf_volume['totalSize'],
size,
"Check SF volume size: " + str(sf_volume['totalSize'])
)
def _check_vag(self, sf_volume, sf_vag_id):
self.assertEqual(
len(sf_volume['volumeAccessGroups']),
1,
"The volume should only be in one VAG."
)
self.assertEqual(
sf_volume['volumeAccessGroups'][0],
sf_vag_id,
"The volume is not in the VAG with the following ID: " + str(sf_vag_id) + "."
)
def _check_and_get_cs_volume(self, volume_id, volume_name):
list_volumes_response = list_volumes(
self.apiClient,
id=volume_id
)
self._check_list(list_volumes_response, 1, TestVolumes._should_only_be_one_volume_in_list_err_msg)
sf_util.check_list(list_volumes_response, 1, self, TestVolumes._should_only_be_one_volume_in_list_err_msg)
cs_volume = list_volumes_response[0]
@ -1547,108 +1459,37 @@ class TestVolumes(cloudstackTestCase):
return cs_volume
def _get_sf_account_id(self, primary_storage_id, account_id):
sf_account_id_request = {'storageid': primary_storage_id, 'accountid': account_id}
sf_account_id_result = self.cs_api.getSolidFireAccountId(sf_account_id_request)
sf_account_id = sf_account_id_result['apisolidfireaccountid']['solidFireAccountId']
def _verify_hsr(self, cs_volume_size_in_gb, hsr, sf_volume_size_in_bytes):
cs_volume_size_including_hsr_in_bytes = self._get_cs_volume_size_including_hsr_in_bytes(cs_volume_size_in_gb, hsr)
self.assertEqual(
isinstance(sf_account_id, int),
True,
TestVolumes._sf_account_id_should_be_non_zero_int_err_msg
)
self.assertTrue(
cs_volume_size_including_hsr_in_bytes == sf_volume_size_in_bytes,
"HSR does not add up correctly."
);
return sf_account_id
def _get_cs_volume_size_including_hsr_in_bytes(self, cs_volume_size_in_gb, hsr):
lowest_hsr = 10
def _get_volume_size_with_hsr(self, cs_volume):
# Get underlying SF volume size with hypervisor snapshot reserve
sf_volume_size_request = {'volumeid': cs_volume.id}
sf_volume_size_result = self.cs_api.getSolidFireVolumeSize(sf_volume_size_request)
sf_volume_size = sf_volume_size_result['apisolidfirevolumesize']['solidFireVolumeSize']
if hsr < lowest_hsr:
hsr = lowest_hsr;
self.assertEqual(
isinstance(sf_volume_size, int),
True,
"The SolidFire volume size should be a non-zero integer."
)
return self._get_bytes_from_gb(cs_volume_size_in_gb + (cs_volume_size_in_gb * (hsr / 100)))
return sf_volume_size
def _get_vag_id(self):
# Get SF Volume Access Group ID
sf_vag_id_request = {'clusterid': self.cluster.id, 'storageid': self.primary_storage.id}
sf_vag_id_result = self.cs_api.getSolidFireVolumeAccessGroupId(sf_vag_id_request)
sf_vag_id = sf_vag_id_result['apisolidfirevolumeaccessgroupid']['solidFireVolumeAccessGroupId']
self.assertEqual(
isinstance(sf_vag_id, int),
True,
TestVolumes._vag_id_should_be_non_zero_int_err_msg
)
return sf_vag_id
def _get_iqn(self, volume):
# Get volume IQN
sf_iscsi_name_request = {'volumeid': volume.id}
sf_iscsi_name_result = self.cs_api.getVolumeiScsiName(sf_iscsi_name_request)
sf_iscsi_name = sf_iscsi_name_result['apivolumeiscsiname']['volumeiScsiName']
self._check_iscsi_name(sf_iscsi_name)
return sf_iscsi_name
def _get_bytes_from_gb(self, number_in_gb):
return number_in_gb * 1024 * 1024 * 1024
def _get_vm(self, vm_id):
list_vms_response = list_virtual_machines(self.apiClient, id=vm_id)
self._check_list(list_vms_response, 1, TestVolumes._should_only_be_one_vm_in_list_err_msg)
sf_util.check_list(list_vms_response, 1, self, TestVolumes._should_only_be_one_vm_in_list_err_msg)
return list_vms_response[0]
def _check_and_get_sf_volume(self, sf_volumes, sf_volume_name, should_exist=True):
sf_volume = None
for volume in sf_volumes:
if volume['name'] == sf_volume_name:
sf_volume = volume
break
if should_exist:
self.assertNotEqual(
sf_volume,
None,
"Check if SF volume was created in correct account: " + str(sf_volumes)
)
else:
self.assertEqual(
sf_volume,
None,
"Check if SF volume was deleted: " + str(sf_volumes)
)
return sf_volume
def _check_xen_sr(self, xen_sr_name, should_exist=True):
if should_exist:
xen_sr = self.xen_session.xenapi.SR.get_by_name_label(xen_sr_name)[0]
sf_util.check_xen_sr(xen_sr_name, self.xen_session, self, should_exist)
self.sr_shared = self.xen_session.xenapi.SR.get_shared(xen_sr)
self.assertEqual(
self.sr_shared,
True,
TestVolumes._sr_not_shared_err_msg
)
else:
xen_sr = self.xen_session.xenapi.SR.get_by_name_label(xen_sr_name)
self._check_list(xen_sr, 0, TestVolumes._list_should_be_empty)
def _get_sf_volumes(self, sf_account_id=None):
if sf_account_id is not None:
sf_volumes = self.sf_client.list_volumes_for_account(sf_account_id)
else:
sf_volumes = self.sf_client.list_active_volumes()
def _get_active_sf_volumes(self, sf_account_id=None):
sf_volumes = sf_util.get_active_sf_volumes(self.sf_client, sf_account_id)
self.assertNotEqual(
len(sf_volumes),
@ -1657,20 +1498,3 @@ class TestVolumes(cloudstackTestCase):
)
return sf_volumes
@classmethod
def _set_supports_resign(cls):
supports_resign = str(cls.supports_resign)
sql_query = "Update host_details Set value = '" + supports_resign + "' Where name = 'supportsResign'"
# make sure you can connect to MySQL: https://teamtreehouse.com/community/cant-connect-remotely-to-mysql-server-with-mysql-workbench
cls.dbConnection.execute(sql_query)
@classmethod
def _purge_solidfire_volumes(cls):
deleted_volumes = cls.sf_client.list_deleted_volumes()
for deleted_volume in deleted_volumes:
cls.sf_client.purge_deleted_volume(deleted_volume['volumeID'])

View File

@ -0,0 +1,217 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
def check_list(in_list, expected_size_of_list, obj_assert, err_msg):
obj_assert.assertEqual(
isinstance(in_list, list),
True,
"'in_list' is not a list."
)
obj_assert.assertEqual(
len(in_list),
expected_size_of_list,
err_msg
)
def get_sf_account_id(cs_api, cs_account_id, primary_storage_id, obj_assert, err_msg):
sf_account_id_request = {'accountid': cs_account_id, 'storageid': primary_storage_id}
sf_account_id_result = cs_api.getSolidFireAccountId(sf_account_id_request)
sf_account_id = sf_account_id_result['apisolidfireaccountid']['solidFireAccountId']
obj_assert.assertEqual(
isinstance(sf_account_id, int),
True,
err_msg
)
return sf_account_id
def get_iqn(cs_api, volume, obj_assert):
# Get volume IQN
sf_iscsi_name_request = {'volumeid': volume.id}
sf_iscsi_name_result = cs_api.getVolumeiScsiName(sf_iscsi_name_request)
sf_iscsi_name = sf_iscsi_name_result['apivolumeiscsiname']['volumeiScsiName']
check_iscsi_name(sf_iscsi_name, obj_assert)
return sf_iscsi_name
def check_iscsi_name(sf_iscsi_name, obj_assert):
obj_assert.assertEqual(
sf_iscsi_name[0],
"/",
"The iSCSI name needs to start with a forward slash."
)
def set_supports_resign(supports_resign, db_connection):
_set_supports_resign_for_table(supports_resign, db_connection, "host_details")
_set_supports_resign_for_table(supports_resign, db_connection, "cluster_details")
def _set_supports_resign_for_table(supports_resign, db_connection, table):
sql_query = "Update " + str(table) + " Set value = '" + str(supports_resign) + "' Where name = 'supportsResign'"
# make sure you can connect to MySQL: https://teamtreehouse.com/community/cant-connect-remotely-to-mysql-server-with-mysql-workbench
db_connection.execute(sql_query)
def purge_solidfire_volumes(sf_client):
deleted_volumes = sf_client.list_deleted_volumes()
for deleted_volume in deleted_volumes:
sf_client.purge_deleted_volume(deleted_volume['volumeID'])
def get_not_active_sf_volumes(sf_client, sf_account_id=None):
if sf_account_id is not None:
sf_volumes = sf_client.list_volumes_for_account(sf_account_id)
if sf_volumes is not None and len(sf_volumes) > 0:
sf_volumes = _get_not_active_sf_volumes_only(sf_volumes)
else:
sf_volumes = sf_client.list_deleted_volumes()
return sf_volumes
def _get_not_active_sf_volumes_only(sf_volumes):
not_active_sf_volumes_only = []
for sf_volume in sf_volumes:
if sf_volume["status"] != "active":
not_active_sf_volumes_only.append(sf_volume)
return not_active_sf_volumes_only
def get_active_sf_volumes(sf_client, sf_account_id=None):
if sf_account_id is not None:
sf_volumes = sf_client.list_volumes_for_account(sf_account_id)
if sf_volumes is not None and len(sf_volumes) > 0:
sf_volumes = _get_active_sf_volumes_only(sf_volumes)
else:
sf_volumes = sf_client.list_active_volumes()
return sf_volumes
def _get_active_sf_volumes_only(sf_volumes):
active_sf_volumes_only = []
for sf_volume in sf_volumes:
if sf_volume["status"] == "active":
active_sf_volumes_only.append(sf_volume)
return active_sf_volumes_only
def check_and_get_sf_volume(sf_volumes, sf_volume_name, obj_assert, should_exist=True):
sf_volume = None
for volume in sf_volumes:
if volume['name'] == sf_volume_name:
sf_volume = volume
break
if should_exist:
obj_assert.assertNotEqual(
sf_volume,
None,
"Check if SF volume was created in correct account: " + str(sf_volumes)
)
else:
obj_assert.assertEqual(
sf_volume,
None,
"Check if SF volume was deleted: " + str(sf_volumes)
)
return sf_volume
def check_xen_sr(xen_sr_name, xen_session, obj_assert, should_exist=True):
xen_sr = xen_session.xenapi.SR.get_by_name_label(xen_sr_name)
if should_exist:
check_list(xen_sr, 1, obj_assert, "SR " + xen_sr_name + " doesn't exist, but should.")
sr_shared = xen_session.xenapi.SR.get_shared(xen_sr[0])
obj_assert.assertEqual(
sr_shared,
True,
"SR " + xen_sr_name + " is not shared, but should be."
)
else:
check_list(xen_sr, 0, obj_assert, "SR " + xen_sr_name + " exists, but shouldn't.")
def check_vag(sf_volume, sf_vag_id, obj_assert):
obj_assert.assertEqual(
len(sf_volume['volumeAccessGroups']),
1,
"The volume should only be in one VAG."
)
obj_assert.assertEqual(
sf_volume['volumeAccessGroups'][0],
sf_vag_id,
"The volume is not in the VAG with the following ID: " + str(sf_vag_id) + "."
)
def get_vag_id(cs_api, cluster_id, primary_storage_id, obj_assert):
# Get SF Volume Access Group ID
sf_vag_id_request = {'clusterid': cluster_id, 'storageid': primary_storage_id}
sf_vag_id_result = cs_api.getSolidFireVolumeAccessGroupId(sf_vag_id_request)
sf_vag_id = sf_vag_id_result['apisolidfirevolumeaccessgroupid']['solidFireVolumeAccessGroupId']
obj_assert.assertEqual(
isinstance(sf_vag_id, int),
True,
"The SolidFire VAG ID should be a non-zero integer."
)
return sf_vag_id
def format_iqn(iqn):
return "/" + iqn + "/0"
def check_size_and_iops(sf_volume, cs_volume, size, obj_assert):
obj_assert.assertEqual(
sf_volume['qos']['minIOPS'],
cs_volume.miniops,
"Check QoS - Min IOPS: " + str(sf_volume['qos']['minIOPS'])
)
obj_assert.assertEqual(
sf_volume['qos']['maxIOPS'],
cs_volume.maxiops,
"Check QoS - Max IOPS: " + str(sf_volume['qos']['maxIOPS'])
)
obj_assert.assertEqual(
sf_volume['totalSize'],
size,
"Check SolidFire volume size: " + str(sf_volume['totalSize'])
)
def get_volume_size_with_hsr(cs_api, cs_volume, obj_assert):
# Get underlying SF volume size with hypervisor snapshot reserve
sf_volume_size_request = {'volumeid': cs_volume.id}
sf_volume_size_result = cs_api.getSolidFireVolumeSize(sf_volume_size_request)
sf_volume_size = sf_volume_size_result['apisolidfirevolumesize']['solidFireVolumeSize']
obj_assert.assertEqual(
isinstance(sf_volume_size, int),
True,
"The SolidFire volume size should be a non-zero integer."
)
return sf_volume_size