Logic on SolidFire side for CloudStack snapshots

This commit is contained in:
Mike Tutkowski 2014-10-17 15:48:01 -06:00
parent 06f6b00cd1
commit 6beeeff7d4
2 changed files with 134 additions and 104 deletions

View File

@ -271,9 +271,9 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
for (SnapshotVO snapshot : lstSnapshots) {
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.SNAPSHOT_STORAGE_POOL_ID);
// if this snapshot belong to the storagePool that was passed in
// if this snapshot belongs to the storagePool that was passed in
if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePool.getId()) {
snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.SNAPSHOT_SIZE);
snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.VOLUME_SIZE);
if (snapshotDetails != null && snapshotDetails.getValue() != null) {
long snapshotSize = Long.parseLong(snapshotDetails.getValue());
@ -493,10 +493,10 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
try {
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
VolumeVO volume = _volumeDao.findById(volumeInfo.getId());
VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
long sfVolumeId = Long.parseLong(volume.getFolder());
long storagePoolId = volume.getPoolId();
long sfVolumeId = Long.parseLong(volumeVO.getFolder());
long storagePoolId = volumeVO.getPoolId();
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
@ -505,50 +505,36 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
long capacityBytes = storagePool.getCapacityBytes();
// getUsedBytes(StoragePool) will not include the bytes of the proposed snapshot because
// updateSnapshotDetails(long, long, long, long) has not yet been called for this snapshot
// getUsedBytes(StoragePool) will not include the bytes of the proposed new volume because
// updateSnapshotDetails(long, long, long, long, String) has not yet been called for this new volume
long usedBytes = getUsedBytes(storagePool);
long sfVolumeSize = sfVolume.getTotalSize();
usedBytes += sfVolumeSize;
// For taking a snapshot, we need to check to make sure a sufficient amount of space remains in the primary storage.
// For creating a volume, we need to check to make sure a sufficient amount of space remains in the primary storage.
// For the purpose of "charging" these bytes against storage_pool.capacityBytes, we take the full size of the SolidFire volume.
// Generally snapshots take up much less space than the size of the volume, but the easiest way to track this space usage
// is to take the full size of the volume (you can always increase the amount of bytes you give to the primary storage).
if (usedBytes > capacityBytes) {
throw new CloudRuntimeException("Insufficient amount of space remains in this primary storage to take a snapshot");
}
storagePool.setUsedBytes(usedBytes);
long sfSnapshotId = SolidFireUtil.createSolidFireSnapshot(sfConnection, sfVolumeId, snapshotInfo.getUuid());
/** @todo Mike T. fill in the CloudStackVolumeSize */
long sfNewVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, snapshotInfo.getUuid(), sfVolume.getAccountId(), sfVolumeSize,
sfVolume.isEnable512e(), "", sfVolume.getMinIops(), sfVolume.getMaxIops(), sfVolume.getBurstIops());
long sfCloneId;
String sfCloneIqn;
try {
sfCloneId = SolidFireUtil.createSolidFireClone(sfConnection, sfVolumeId, sfSnapshotId, snapshotInfo.getUuid());
SolidFireUtil.SolidFireVolume sfClonedVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfCloneId);
sfCloneIqn = sfClonedVolume.getIqn();
}
catch (Exception ex) {
SolidFireUtil.deleteSolidFireSnapshot(sfConnection, sfSnapshotId);
throw ex;
}
// Now that we have successfully taken a snapshot (for the purpose of reverting) and a clone (for the purpose of creating a template
// and a volume), update the space usage in the storage_pool table (even though storage_pool.used_bytes is likely no longer in use).
// Now that we have successfully created a volume, update the space usage in the storage_pool table
// (even though storage_pool.used_bytes is likely no longer in use).
_storagePoolDao.update(storagePoolId, storagePool);
updateSnapshotDetails(snapshotInfo.getId(), sfSnapshotId, storagePoolId, sfVolumeSize, sfCloneId, sfCloneIqn);
SolidFireUtil.SolidFireVolume sfNewVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfNewVolumeId);
updateSnapshotDetails(snapshotInfo.getId(), sfNewVolumeId, storagePoolId, sfVolumeSize, sfNewVolume.getIqn());
SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO();
snapshotObjectTo.setPath(String.valueOf(sfSnapshotId));
snapshotObjectTo.setPath(String.valueOf(sfNewVolumeId));
CreateObjectAnswer createObjectAnswer = new CreateObjectAnswer(snapshotObjectTo);
@ -567,54 +553,52 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
callback.complete(result);
}
private void updateSnapshotDetails(long csSnapshotId, long sfSnapshotId, long storagePoolId, long sfSnapshotSize, long sfCloneId, String sfCloneIqn) {
private void updateSnapshotDetails(long csSnapshotId, long sfNewVolumeId, long storagePoolId, long sfNewVolumeSize, String sfNewVolumeIqn) {
SnapshotDetailsVO accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.SNAPSHOT_ID,
String.valueOf(sfSnapshotId),
SolidFireUtil.VOLUME_ID,
String.valueOf(sfNewVolumeId),
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(accountDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.SNAPSHOT_STORAGE_POOL_ID,
String.valueOf(storagePoolId),
false);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.SNAPSHOT_STORAGE_POOL_ID,
String.valueOf(storagePoolId),
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(accountDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.SNAPSHOT_SIZE,
String.valueOf(sfSnapshotSize),
false);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.VOLUME_SIZE,
String.valueOf(sfNewVolumeSize),
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(accountDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.CLONE_ID,
String.valueOf(sfCloneId),
false);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
DataMotionStrategy.IQN,
sfNewVolumeIqn,
false);
_snapshotDetailsDao.persist(accountDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
DataMotionStrategy.IQN,
sfCloneIqn,
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(accountDetail);
}
// return null for no error message
private String deleteSnapshot(SnapshotInfo snapshotInfo, long storagePoolId) {
String errMsg = null;
long snapshotId = snapshotInfo.getId();
try {
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
SolidFireUtil.deleteSolidFireSnapshot(sfConnection, getSolidFireSnapshotId(snapshotInfo.getId()));
SolidFireUtil.deleteSolidFireVolume(sfConnection, getSolidFireCloneId(snapshotInfo.getId()));
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, SolidFireUtil.VOLUME_ID);
_snapshotDetailsDao.removeDetails(snapshotInfo.getId());
long volumeId = Long.parseLong(snapshotDetails.getValue());
SolidFireUtil.deleteSolidFireVolume(sfConnection, volumeId);
_snapshotDetailsDao.removeDetails(snapshotId);
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
@ -626,7 +610,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
_storagePoolDao.update(storagePoolId, storagePool);
}
catch (Exception ex) {
s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire snapshot: " + snapshotInfo.getId(), ex);
s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume. CloudStack snapshot ID: " + snapshotId, ex);
errMsg = ex.getMessage();
}
@ -634,44 +618,9 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return errMsg;
}
private long getSolidFireSnapshotId(long csSnapshotId) {
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID);
return Long.parseLong(snapshotDetails.getValue());
}
private long getSolidFireCloneId(long csSnapshotId) {
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.CLONE_ID);
return Long.parseLong(snapshotDetails.getValue());
}
@Override
public void revertSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback<CommandResult> callback) {
String errMsg = null;
try {
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
long storagePoolId = volumeInfo.getPoolId();
long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
long sfSnapshotId = getSolidFireSnapshotId(snapshotInfo.getId());
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
SolidFireUtil.rollBackVolumeToSnapshot(sfConnection, sfVolumeId, sfSnapshotId);
}
catch (Exception ex) {
s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex);
errMsg = ex.getMessage();
}
CommandResult result = new CommandResult();
result.setResult(errMsg);
callback.complete(result);
throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead.");
}
@Override

View File

@ -92,11 +92,8 @@ public class SolidFireUtil {
public static final String ACCOUNT_ID = "accountId";
public static final String VOLUME_ID = "volumeId";
public static final String SNAPSHOT_ID = "snapshotId";
public static final String CLONE_ID = "cloneId";
public static final String VOLUME_SIZE = "sfVolumeSize";
public static final String SNAPSHOT_SIZE = "sfSnapshotSize";
public static final String SNAPSHOT_STORAGE_POOL_ID = "sfSnapshotStoragePoolId";
@ -513,9 +510,14 @@ public class SolidFireUtil {
String strVolumeIqn = getVolumeIqn(volumeGetResult, lVolumeId);
long lAccountId = getVolumeAccountId(volumeGetResult, lVolumeId);
String strVolumeStatus = getVolumeStatus(volumeGetResult, lVolumeId);
boolean enable512e = getVolumeEnable512e(volumeGetResult, lVolumeId);
long lMinIops = getVolumeMinIops(volumeGetResult, lVolumeId);
long lMaxIops = getVolumeMaxIops(volumeGetResult, lVolumeId);
long lBurstIops = getVolumeBurstIops(volumeGetResult, lVolumeId);
long lTotalSize = getVolumeTotalSize(volumeGetResult, lVolumeId);
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, lTotalSize);
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, enable512e,
lMinIops, lMaxIops, lBurstIops, lTotalSize);
}
public static List<SolidFireVolume> getSolidFireVolumesForAccountId(SolidFireConnection sfConnection, long lAccountId) {
@ -534,7 +536,8 @@ 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, volume.totalSize));
sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.enable512e,
volume.qos.minIOPS, volume.qos.maxIOPS, volume.qos.burstIOPS, volume.totalSize));
}
return sfVolumes;
@ -557,7 +560,8 @@ 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, volume.totalSize));
deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.enable512e,
volume.qos.minIOPS, volume.qos.maxIOPS, volume.qos.burstIOPS, volume.totalSize));
}
return deletedVolumes;
@ -593,16 +597,25 @@ public class SolidFireUtil {
private final String _iqn;
private final long _accountId;
private final String _status;
private final boolean _enable512e;
private final long _minIops;
private final long _maxIops;
private final long _burstIops;
private final long _totalSize;
public SolidFireVolume(long id, String name, String iqn,
long accountId, String status, long totalSize)
long accountId, String status, boolean enable512e,
long minIops, long maxIops, long burstIops, long totalSize)
{
_id = id;
_name = name;
_iqn = "/" + iqn + "/0";
_accountId = accountId;
_status = status;
_enable512e = enable512e;
_minIops = minIops;
_maxIops = maxIops;
_burstIops = burstIops;
_totalSize = totalSize;
}
@ -626,6 +639,22 @@ public class SolidFireUtil {
return ACTIVE.equalsIgnoreCase(_status);
}
public boolean isEnable512e() {
return _enable512e;
}
public long getMinIops() {
return _minIops;
}
public long getMaxIops() {
return _maxIops;
}
public long getBurstIops() {
return _burstIops;
}
public long getTotalSize() {
return _totalSize;
}
@ -1562,7 +1591,15 @@ public class SolidFireUtil {
private String iqn;
private long accountID;
private String status;
private boolean enable512e;
private Qos qos;
private long totalSize;
private static final class Qos {
private long minIOPS;
private long maxIOPS;
private long burstIOPS;
}
}
}
}
@ -1790,6 +1827,50 @@ public class SolidFireUtil {
throw new CloudRuntimeException("Could not determine the status of the volume for volume ID of " + lVolumeId + ".");
}
private static boolean getVolumeEnable512e(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].enable512e;
}
throw new CloudRuntimeException("Could not determine the enable 512 emulation of the volume for volume ID of " + lVolumeId + ".");
}
private static long getVolumeMinIops(VolumeGetResult volumeGetResult, long lVolumeId)
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId && volumeGetResult.result.volumes[0].qos != null)
{
return volumeGetResult.result.volumes[0].qos.minIOPS;
}
throw new CloudRuntimeException("Could not determine the min IOPS of the volume for volume ID of " + lVolumeId + ".");
}
private static long getVolumeMaxIops(VolumeGetResult volumeGetResult, long lVolumeId)
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId && volumeGetResult.result.volumes[0].qos != null)
{
return volumeGetResult.result.volumes[0].qos.maxIOPS;
}
throw new CloudRuntimeException("Could not determine the max IOPS of the volume for volume ID of " + lVolumeId + ".");
}
private static long getVolumeBurstIops(VolumeGetResult volumeGetResult, long lVolumeId)
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId && volumeGetResult.result.volumes[0].qos != null)
{
return volumeGetResult.result.volumes[0].qos.burstIOPS;
}
throw new CloudRuntimeException("Could not determine the burst IOPS 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 &&