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

This commit is contained in:
Mike Tutkowski 2014-01-09 14:42:35 -07:00
parent 09da5153df
commit 03118c2969
12 changed files with 129 additions and 44 deletions

View File

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

View File

@ -21,9 +21,14 @@ 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

@ -404,6 +404,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;
DiskProfile dskCh = null;
@ -423,8 +425,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()) {
@ -436,7 +438,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,7 +587,6 @@ public class VolumeVO implements Volume {
this.state = state;
}
@Override
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
this.hypervisorSnapshotReserve = hypervisorSnapshotReserve;
}

View File

@ -36,6 +36,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;
@ -45,6 +47,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

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

View File

@ -68,6 +68,7 @@ import com.cloud.storage.ResizeVolumePayload;
import com.cloud.storage.Storage;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.VMTemplateDao;
@ -148,6 +149,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

@ -42,6 +42,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;
@ -78,6 +80,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;

View File

@ -46,6 +46,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;
@ -284,8 +286,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) {
@ -295,11 +309,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) {
@ -356,11 +366,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();
@ -370,7 +381,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) {
@ -429,7 +440,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);
@ -493,31 +504,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

@ -95,7 +95,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);
@ -103,6 +106,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) {
@ -132,8 +137,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, String strSfAdmin, String strSfPassword, long lAccountId) {
@ -152,7 +158,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;
@ -166,13 +172,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) {
public SolidFireVolume(long id, String name, String iqn,
long accountId, String status, long totalSize)
{
_id = id;
_name = name;
_iqn = "/" + iqn + "/0";
_accountId = accountId;
_status = status;
_totalSize = totalSize;
}
public long getId() {
@ -195,6 +205,10 @@ public class SolidFireUtil {
return ACTIVE.equalsIgnoreCase(_status);
}
public long getTotalSize() {
return _totalSize;
}
@Override
public int hashCode() {
return _iqn.hashCode();
@ -217,7 +231,9 @@ public class SolidFireUtil {
SolidFireVolume sfv = (SolidFireVolume)obj;
if (_id == sfv._id && _name.equals(sfv._name) && _iqn.equals(sfv._iqn) && _accountId == sfv._accountId && isActive() == sfv.isActive()) {
if (_id == sfv._id && _name.equals(sfv._name) &&
_iqn.equals(sfv._iqn) && _accountId == sfv._accountId &&
isActive() == sfv.isActive() && getTotalSize() == sfv.getTotalSize()) {
return true;
}
@ -366,7 +382,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;
@ -655,6 +671,7 @@ public class SolidFireUtil {
private String iqn;
private long accountID;
private String status;
private long totalSize;
}
}
}
@ -811,7 +828,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) {
@ -819,7 +836,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) {
@ -827,7 +844,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) {
@ -835,6 +852,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

@ -500,10 +500,19 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
@Override
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

@ -54,6 +54,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;
@ -62,6 +63,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;
@ -1542,7 +1544,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
}
if (volume.getState() != Volume.State.Ready) {
totalAskingSize = totalAskingSize + volume.getSize();
totalAskingSize = totalAskingSize + getVolumeSizeIncludingHvSsReserve(volume, pool);
}
}
@ -1581,6 +1583,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);