CLOUDSTACK-4810: Enable hypervisor snapshots for CloudStack-managed storage (for XenServer and VMware)

This commit is contained in:
Mike Tutkowski 2013-11-12 12:08:03 -07:00
parent cd11d3e9df
commit bb8794203e
12 changed files with 124 additions and 49 deletions

View File

@ -186,7 +186,5 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
Storage.ImageFormat getFormat();
Long getVmSnapshotChainSize();
void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve);
Integer getHypervisorSnapshotReserve();
}

View File

@ -21,8 +21,12 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CommandResult;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
public interface PrimaryDataStoreDriver extends DataStoreDriver {
public ChapInfo getChapInfo(VolumeInfo volumeInfo);
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool);
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback);
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback);
}

View File

@ -374,6 +374,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
@DB
public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMachineTemplate template, DataCenter dc, Pod pod, Long clusterId, ServiceOffering offering,
DiskOffering diskOffering, List<StoragePool> avoids, long size, HypervisorType hyperType) {
volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
StoragePool pool = null;
if (diskOffering != null && diskOffering.isCustomized()) {
@ -393,8 +395,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
pool = findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(), vm, avoidPools);
if (pool == null) {
s_logger.warn("Unable to find storage pool when create volume " + volume.getName());
throw new CloudRuntimeException("Unable to find storage pool when create volume" + volume.getName());
s_logger.warn("Unable to find suitable primary storage when creating volume " + volume.getName());
throw new CloudRuntimeException("Unable to find suitable primary storage when creating volume " + volume.getName());
}
if (s_logger.isDebugEnabled()) {
@ -406,7 +408,6 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
AsyncCallFuture<VolumeApiResult> future = null;
boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false;
if (isNotCreatedFromTemplate) {
volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
future = volService.createVolumeAsync(volume, store);
} else {
TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image);

View File

@ -587,6 +587,7 @@ public class VolumeVO implements Volume {
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
}
@Override
public Integer getHypervisorSnapshotReserve() {
return hypervisorSnapshotReserve;
}

View File

@ -35,6 +35,8 @@ import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
boolean snapshotResult = true;
@ -43,6 +45,11 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
return volume.getSize();
}
@Override
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
CreateCmdResult result = new CreateCmdResult(null, null);

View File

@ -141,7 +141,6 @@ public class VolumeObject implements VolumeInfo {
return volumeVO.getMaxIops();
}
@Override
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
}

View File

@ -23,6 +23,7 @@ import java.util.UUID;
import javax.inject.Inject;
import com.cloud.storage.*;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.log4j.Logger;
@ -132,6 +133,11 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
return null;
}
@Override
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
return volume.getSize();
}
@Override
public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
String errMsg = null;

View File

@ -40,6 +40,8 @@ import org.apache.cloudstack.storage.datastore.DataObjectManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.utils.exception.CloudRuntimeException;
@ -71,6 +73,11 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
return null;
}
@Override
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
return volume.getSize();
}
private class CreateVolumeContext<T> extends AsyncRpcContext<T> {
private final DataObject volume;
public CreateVolumeContext(AsyncCompletionCallback<T> callback, DataObject volume) {

View File

@ -44,6 +44,8 @@ import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
@ -288,8 +290,20 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
}
long volumeSize = volumeInfo.getSize();
Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve();
long volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId));
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
NumberFormat.getInstance().format(volumeInfo.getSize()),
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
}
@Override
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
long volumeSize = volume.getSize();
Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve();
if (hypervisorSnapshotReserve != null) {
if (hypervisorSnapshotReserve < 25) {
@ -299,12 +313,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);
}
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
NumberFormat.getNumberInstance().format(volumeInfo.getSize().toString()),
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
return volumeSize;
}
private String getSolidFireVolumeName(String strCloudStackVolumeName) {
@ -367,12 +376,12 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
}
private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
private SolidFireUtil.SolidFireVolume deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
{
Long storagePoolId = volumeInfo.getPoolId();
if (storagePoolId == null) {
return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
return null; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
}
String mVip = sfConnection.getManagementVip();
@ -382,7 +391,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
return SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
}
private String getSfAccountName(String csAccountUuid, long csAccountId) {
@ -443,7 +452,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
long capacityBytes = storagePool.getCapacityBytes();
long usedBytes = storagePool.getUsedBytes();
usedBytes += volumeInfo.getSize();
usedBytes += sfVolume.getTotalSize();
storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes);
@ -510,31 +519,31 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
if (dataObject.getType() == DataObjectType.VOLUME) {
VolumeInfo volumeInfo = (VolumeInfo)dataObject;
AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
long sfAccountId = Long.parseLong(accountDetails.getValue());
// AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
// AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
// long sfAccountId = Long.parseLong(accountDetails.getValue());
long storagePoolId = dataStore.getId();
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
deleteSolidFireVolume(volumeInfo, sfConnection);
SolidFireUtil.SolidFireVolume sfVolume = deleteSolidFireVolume(volumeInfo, sfConnection);
_volumeDao.deleteVolumesByInstance(volumeInfo.getId());
// if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
// // delete the account from the SolidFire SAN
// deleteSolidFireAccount(sfAccountId, sfConnection);
// if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
// // delete the account from the SolidFire SAN
// deleteSolidFireAccount(sfAccountId, sfConnection);
//
// // delete the info in the account_details table
// // that's related to the SolidFire account
// _accountDetailsDao.deleteDetails(account.getAccountId());
// }
// // delete the info in the account_details table
// // that's related to the SolidFire account
// _accountDetailsDao.deleteDetails(account.getAccountId());
// }
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
long usedBytes = storagePool.getUsedBytes();
usedBytes -= volumeInfo.getSize();
usedBytes -= sfVolume != null ? sfVolume.getTotalSize() : 0;
storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);

View File

@ -97,8 +97,10 @@ public class SolidFireUtil
return volumeCreateResult.result.volumeID;
}
public static void deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
public static SolidFireVolume deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
{
SolidFireVolume sfVolume = getSolidFireVolume(strSfMvip, iSfPort, strSfAdmin, strSfPassword, lVolumeId);
final Gson gson = new GsonBuilder().create();
VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId);
@ -106,6 +108,8 @@ public class SolidFireUtil
String strVolumeToDeleteJson = gson.toJson(volumeToDelete);
executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
return sfVolume;
}
public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId)
@ -137,8 +141,9 @@ public class SolidFireUtil
String strVolumeIqn = getVolumeIqn(volumeGetResult, lVolumeId);
long lAccountId = getVolumeAccountId(volumeGetResult, lVolumeId);
String strVolumeStatus = getVolumeStatus(volumeGetResult, lVolumeId);
long lTotalSize = getVolumeTotalSize(volumeGetResult, lVolumeId);
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus);
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, lTotalSize);
}
public static List<SolidFireVolume> getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort,
@ -160,7 +165,7 @@ public class SolidFireUtil
List<SolidFireVolume> sfVolumes = new ArrayList<SolidFireVolume>();
for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.totalSize));
}
return sfVolumes;
@ -175,15 +180,17 @@ public class SolidFireUtil
private final String _iqn;
private final long _accountId;
private final String _status;
private final long _totalSize;
public SolidFireVolume(long id, String name, String iqn,
long accountId, String status)
long accountId, String status, long totalSize)
{
_id = id;
_name = name;
_iqn = "/" + iqn + "/0";
_accountId = accountId;
_status = status;
_totalSize = totalSize;
}
public long getId()
@ -211,6 +218,10 @@ public class SolidFireUtil
return ACTIVE.equalsIgnoreCase(_status);
}
public long getTotalSize() {
return _totalSize;
}
@Override
public int hashCode() {
return _iqn.hashCode();
@ -235,7 +246,7 @@ public class SolidFireUtil
if (_id == sfv._id && _name.equals(sfv._name) &&
_iqn.equals(sfv._iqn) && _accountId == sfv._accountId &&
isActive() == sfv.isActive()) {
isActive() == sfv.isActive() && getTotalSize() == sfv.getTotalSize()) {
return true;
}
@ -402,7 +413,7 @@ public class SolidFireUtil
List<SolidFireVolume> deletedVolumes = new ArrayList<SolidFireVolume> ();
for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.totalSize));
}
return deletedVolumes;
@ -748,6 +759,7 @@ public class SolidFireUtil
private String iqn;
private long accountID;
private String status;
private long totalSize;
}
}
}
@ -927,8 +939,7 @@ public class SolidFireUtil
return volumeGetResult.result.volumes[0].name;
}
throw new CloudRuntimeException("Could not determine the name of the volume, " +
"but the volume was created with an ID of " + lVolumeId + ".");
throw new CloudRuntimeException("Could not determine the name of the volume for volume ID of " + lVolumeId + ".");
}
private static String getVolumeIqn(VolumeGetResult volumeGetResult, long lVolumeId)
@ -939,8 +950,7 @@ public class SolidFireUtil
return volumeGetResult.result.volumes[0].iqn;
}
throw new CloudRuntimeException("Could not determine the IQN of the volume, " +
"but the volume was created with an ID of " + lVolumeId + ".");
throw new CloudRuntimeException("Could not determine the IQN of the volume for volume ID of " + lVolumeId + ".");
}
private static long getVolumeAccountId(VolumeGetResult volumeGetResult, long lVolumeId)
@ -951,8 +961,7 @@ public class SolidFireUtil
return volumeGetResult.result.volumes[0].accountID;
}
throw new CloudRuntimeException("Could not determine the volume's account ID, " +
"but the volume was created with an ID of " + lVolumeId + ".");
throw new CloudRuntimeException("Could not determine the account ID of the volume for volume ID of " + lVolumeId + ".");
}
private static String getVolumeStatus(VolumeGetResult volumeGetResult, long lVolumeId)
@ -963,7 +972,17 @@ public class SolidFireUtil
return volumeGetResult.result.volumes[0].status;
}
throw new CloudRuntimeException("Could not determine the status of the volume, " +
"but the volume was created with an ID of " + lVolumeId + ".");
throw new CloudRuntimeException("Could not determine the status of the volume for volume ID of " + lVolumeId + ".");
}
private static long getVolumeTotalSize(VolumeGetResult volumeGetResult, long lVolumeId)
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId)
{
return volumeGetResult.result.volumes[0].totalSize;
}
throw new CloudRuntimeException("Could not determine the total size of the volume for volume ID of " + lVolumeId + ".");
}
}

View File

@ -475,11 +475,20 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
}
@Override
public long getAllocatedPoolCapacity(StoragePoolVO pool, VMTemplateVO templateForVmCreation){
public long getAllocatedPoolCapacity(StoragePoolVO pool, VMTemplateVO templateForVmCreation) {
long totalAllocatedSize = 0;
// Get size for all the non-destroyed volumes
Pair<Long, Long> sizes = _volumeDao.getNonDestroyedCountAndTotalByPool(pool.getId());
long totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume;
// if the storage pool is managed, the used bytes can be larger than the sum of the sizes of all of the non-destroyed volumes
// in this case, just get the used bytes from the storage pool object
if (pool.isManaged()) {
totalAllocatedSize = pool.getUsedBytes();
}
else {
// Get size for all the non-destroyed volumes
Pair<Long, Long> sizes = _volumeDao.getNonDestroyedCountAndTotalByPool(pool.getId());
totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume;
}
// Get size for VM Snapshots
totalAllocatedSize = totalAllocatedSize + _volumeDao.getVMSnapshotSizeByPool(pool.getId());

View File

@ -55,6 +55,7 @@ import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
@ -63,6 +64,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.engine.subsystem.api.storage.ImageStoreProvider;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
@ -1582,7 +1584,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
}
if (volume.getState() != Volume.State.Ready) {
totalAskingSize = totalAskingSize + volume.getSize();
totalAskingSize = totalAskingSize + getVolumeSizeIncludingHvSsReserve(volume, pool);
}
}
@ -1623,6 +1625,19 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
return true;
}
private long getVolumeSizeIncludingHvSsReserve(Volume volume, StoragePool pool) {
DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName());
DataStoreDriver storeDriver = storeProvider.getDataStoreDriver();
if (storeDriver instanceof PrimaryDataStoreDriver) {
PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver;
return primaryStoreDriver.getVolumeSizeIncludingHypervisorSnapshotReserve(volume, pool);
}
return volume.getSize();
}
@Override
public void createCapacityEntry(long poolId) {
StoragePoolVO storage = _storagePoolDao.findById(poolId);