From 1d2f3300adab216fe962bb35d0c5a342ba4ec785 Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Mon, 25 Aug 2014 16:15:04 -0600 Subject: [PATCH] Adding support for SolidFire snapshots --- .../api/storage/PrimaryDataStoreDriver.java | 9 +- .../test/FakePrimaryDataStoreDriver.java | 5 + .../storage/snapshot/SnapshotServiceImpl.java | 6 +- .../CloudStackPrimaryDataStoreDriverImpl.java | 5 + .../driver/NexentaPrimaryDataStoreDriver.java | 5 + .../SamplePrimaryDataStoreDriverImpl.java | 5 + .../SolidFirePrimaryDataStoreDriver.java | 263 ++++++++++++++++-- .../storage/datastore/util/SolidFireUtil.java | 135 ++++++++- .../cloud/capacity/CapacityManagerImpl.java | 28 +- .../com/cloud/storage/StorageManagerImpl.java | 4 +- 10 files changed, 405 insertions(+), 60 deletions(-) diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index c4dfc5ceb7e..43d7d5a07f5 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -32,7 +32,14 @@ public interface PrimaryDataStoreDriver extends DataStoreDriver { public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore); - public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool); + // intended for managed storage (cloud.storage_pool.managed = true) + // if not managed, return volume.getSize() + public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool storagePool); + + // intended for managed storage (cloud.storage_pool.managed = true) + // if managed storage, return the total number of bytes currently in use for the storage pool in question + // if not managed storage, return 0 + public long getUsedBytes(StoragePool storagePool); public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java index d277991cbe6..7d9cd72c6b2 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java @@ -54,6 +54,11 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Override public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) {} + @Override + public long getUsedBytes(StoragePool storagePool) { + return 0; + } + @Override public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { return volume.getSize(); diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index 684b5bd29c9..309f6d6c21f 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -316,10 +316,10 @@ public class SnapshotServiceImpl implements SnapshotService { } try { - CopyCmdAnswer answer = (CopyCmdAnswer)result.getAnswer(); - destSnapshot.processEvent(Event.OperationSuccessed, result.getAnswer()); + CopyCmdAnswer copyCmdAnswer = (CopyCmdAnswer)result.getAnswer(); + destSnapshot.processEvent(Event.OperationSuccessed, copyCmdAnswer); srcSnapshot.processEvent(Snapshot.Event.OperationSucceeded); - snapResult = new SnapshotResult(_snapshotFactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), answer); + snapResult = new SnapshotResult(_snapshotFactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), copyCmdAnswer); future.complete(snapResult); } catch (Exception e) { s_logger.debug("Failed to update snapshot state", e); diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 41152899545..d58c0b15d11 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -157,6 +157,11 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) { } + @Override + public long getUsedBytes(StoragePool storagePool) { + return 0; + } + @Override public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { return volume.getSize(); diff --git a/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java b/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java index 70f4a4f140c..aff50e5a42b 100644 --- a/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java @@ -67,6 +67,11 @@ public class NexentaPrimaryDataStoreDriver implements PrimaryDataStoreDriver { //To change body of implemented methods use File | Settings | File Templates. } + @Override + public long getUsedBytes(StoragePool storagePool) { + return 0; + } + @Override public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { return 0; //To change body of implemented methods use File | Settings | File Templates. diff --git a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java index 1f9a128727e..4ade4671c1d 100644 --- a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java @@ -87,6 +87,11 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver @Override public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) {} + @Override + public long getUsedBytes(StoragePool storagePool) { + return 0; + } + @Override public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { return volume.getSize(); diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index e5a15859c2e..e966cf6e061 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -32,11 +32,15 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DataObjectType; @@ -46,7 +50,6 @@ import com.cloud.capacity.CapacityManager; import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterDetailsDao; -import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -55,8 +58,14 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.ResizeVolumePayload; import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; +import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.user.AccountDetailVO; import com.cloud.user.AccountDetailsDao; import com.cloud.user.AccountVO; @@ -64,16 +73,20 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.exception.CloudRuntimeException; public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { + private static final Logger s_logger = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class); + @Inject private AccountDao _accountDao; @Inject private AccountDetailsDao _accountDetailsDao; @Inject private CapacityManager _capacityMgr; @Inject private ClusterDao _clusterDao; @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private DataCenterDao _zoneDao; @Inject private HostDao _hostDao; + @Inject private SnapshotDao _snapshotDao; + @Inject private SnapshotDetailsDao _snapshotDetailsDao; @Inject private PrimaryDataStoreDao _storagePoolDao; @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; @Inject private VolumeDao _volumeDao; + @Inject private VolumeDetailsDao _volumeDetailsDao; @Override public Map getCapabilities() { @@ -228,14 +241,54 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId); } + @Override + public long getUsedBytes(StoragePool storagePool) { + long usedSpace = 0; + + List lstVolumes = _volumeDao.findByPoolId(storagePool.getId(), null); + + if (lstVolumes != null) { + for (VolumeVO volume : lstVolumes) { + VolumeDetailVO volumeDetail = _volumeDetailsDao.findDetail(volume.getId(), SolidFireUtil.VOLUME_SIZE); + + if (volumeDetail != null && volumeDetail.getValue() != null) { + long volumeSize = Long.parseLong(volumeDetail.getValue()); + + usedSpace += volumeSize; + } + } + } + + List lstSnapshots = _snapshotDao.listAll(); + + if (lstSnapshots != null) { + 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 (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePool.getId()) { + snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.SNAPSHOT_SIZE); + + if (snapshotDetails != null && snapshotDetails.getValue() != null) { + long snapshotSize = Long.parseLong(snapshotDetails.getValue()); + + usedSpace += snapshotSize; + } + } + } + } + + return usedSpace; + } + @Override public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { long volumeSize = volume.getSize(); Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve(); if (hypervisorSnapshotReserve != null) { - if (hypervisorSnapshotReserve < 25) { - hypervisorSnapshotReserve = 25; + if (hypervisorSnapshotReserve < 50) { + hypervisorSnapshotReserve = 50; } volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f); @@ -280,17 +333,17 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } - private SolidFireUtil.SolidFireVolume deleteSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, VolumeInfo volumeInfo) + private void deleteSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, VolumeInfo volumeInfo) { Long storagePoolId = volumeInfo.getPoolId(); if (storagePoolId == null) { - return null; // this volume was never assigned to a storage pool, so no SAN volume should exist for it + return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it } long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); - return SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId); + SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId); } @Override @@ -327,7 +380,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { iqn = sfVolume.getIqn(); - VolumeVO volume = this._volumeDao.findById(volumeInfo.getId()); + VolumeVO volume = _volumeDao.findById(volumeInfo.getId()); volume.set_iScsiName(iqn); volume.setFolder(String.valueOf(sfVolume.getId())); @@ -336,12 +389,14 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { _volumeDao.update(volume.getId(), volume); + updateVolumeDetails(volume.getId(), sfVolume.getTotalSize()); + StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId()); long capacityBytes = storagePool.getCapacityBytes(); - long usedBytes = storagePool.getUsedBytes(); - - usedBytes += sfVolume.getTotalSize(); + // getUsedBytes(StoragePool) will include the bytes of the newly created volume because + // updateVolumeDetails(long, long) has already been called for this volume + long usedBytes = getUsedBytes(storagePool); storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); @@ -359,29 +414,52 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { callback.complete(result); } + private void updateVolumeDetails(long volumeId, long sfVolumeSize) { + VolumeDetailVO volumeDetailVo = _volumeDetailsDao.findDetail(volumeId, SolidFireUtil.VOLUME_SIZE); + + if (volumeDetailVo == null || volumeDetailVo.getValue() == null) { + volumeDetailVo = new VolumeDetailVO(volumeId, SolidFireUtil.VOLUME_SIZE, String.valueOf(sfVolumeSize), false); + + _volumeDetailsDao.persist(volumeDetailVo); + } + } + @Override public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback callback) { String errMsg = null; if (dataObject.getType() == DataObjectType.VOLUME) { - VolumeInfo volumeInfo = (VolumeInfo)dataObject; + try { + VolumeInfo volumeInfo = (VolumeInfo)dataObject; + long volumeId = volumeInfo.getId(); - long storagePoolId = dataStore.getId(); - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + long storagePoolId = dataStore.getId(); - SolidFireUtil.SolidFireVolume sfVolume = deleteSolidFireVolume(sfConnection, volumeInfo); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); - _volumeDao.deleteVolumesByInstance(volumeInfo.getId()); + deleteSolidFireVolume(sfConnection, volumeInfo); - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + _volumeDetailsDao.removeDetails(volumeId); - long usedBytes = storagePool.getUsedBytes(); + _volumeDao.deleteVolumesByInstance(volumeId); - usedBytes -= sfVolume != null ? sfVolume.getTotalSize() : 0; + StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); - storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); + // getUsedBytes(StoragePool) will not include the volume to delete because it has already been deleted by this point + long usedBytes = getUsedBytes(storagePool); - _storagePoolDao.update(storagePoolId, storagePool); + storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); + + _storagePoolDao.update(storagePoolId, storagePool); + } + catch (Exception ex) { + s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume", ex); + + errMsg = ex.getMessage(); + } + } else if (dataObject.getType() == DataObjectType.SNAPSHOT) { + // should return null when no error message + errMsg = deleteSnapshot((SnapshotInfo)dataObject, dataStore.getId()); } else { errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync"; } @@ -394,15 +472,145 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback callback) { + public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) { + // in this situation, we don't want to copy the snapshot anywhere + + CopyCmdAnswer copyCmdAnswer = new CopyCmdAnswer(destData.getTO()); + CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer); + + result.setResult(null); + + callback.complete(result); + + return; + } + throw new UnsupportedOperationException(); } @Override public boolean canCopy(DataObject srcData, DataObject destData) { + if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) { + return true; + } + return false; } + @Override + public void takeSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback callback) { + CreateCmdResult result = null; + + try { + VolumeInfo volumeInfo = snapshotInfo.getBaseVolume(); + VolumeVO volume = _volumeDao.findById(volumeInfo.getId()); + + long sfVolumeId = Long.parseLong(volume.getFolder()); + long storagePoolId = volume.getPoolId(); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + + SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId); + + 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 + 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 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()); + + // Now that we have successfully taken a snapshot, 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); + + SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO(); + + snapshotObjectTo.setPath(String.valueOf(sfSnapshotId)); + + CreateObjectAnswer createObjectAnswer = new CreateObjectAnswer(snapshotObjectTo); + + result = new CreateCmdResult(null, createObjectAnswer); + + result.setResult(null); + } + catch (Exception ex) { + s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex); + + result = new CreateCmdResult(null, new CreateObjectAnswer(ex.toString())); + + result.setResult(ex.toString()); + } + + callback.complete(result); + } + + private void updateSnapshotDetails(long csSnapshotId, long sfSnapshotId, long storagePoolId, long sfSnapshotSize) { + SnapshotDetailsVO accountDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.SNAPSHOT_ID, + String.valueOf(sfSnapshotId), + false); + + _snapshotDetailsDao.persist(accountDetail); + + accountDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.SNAPSHOT_STORAGE_POOL_ID, + String.valueOf(storagePoolId), + false); + + _snapshotDetailsDao.persist(accountDetail); + + accountDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.SNAPSHOT_SIZE, + String.valueOf(sfSnapshotSize), + false); + + _snapshotDetailsDao.persist(accountDetail); + } + + // return null for no error message + private String deleteSnapshot(SnapshotInfo snapshotInfo, long storagePoolId) { + String errMsg = null; + + try { + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + + SolidFireUtil.deleteSolidFireSnapshot(sfConnection, getSolidFireSnapshotId(snapshotInfo.getId())); + + _snapshotDetailsDao.removeDetails(snapshotInfo.getId()); + } + catch (Exception ex) { + s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire snapshot: " + snapshotInfo.getId(), ex); + + errMsg = ex.getMessage(); + } + + return errMsg; + } + + private long getSolidFireSnapshotId(long csSnapshotId) { + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID); + + return Long.parseLong(snapshotDetails.getValue()); + } + @Override public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { throw new UnsupportedOperationException(); @@ -434,6 +642,12 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { volume.setMaxIops(payload.newMaxIops); _volumeDao.update(volume.getId(), volume); + + // SolidFireUtil.VOLUME_SIZE was introduced in 4.5. + // To be backward compatible with releases prior to 4.5, call updateVolumeDetails here. + // That way if SolidFireUtil.VOLUME_SIZE wasn't put in the volume_details table when the + // volume was initially created, it can be placed in volume_details if the volume is resized. + updateVolumeDetails(volume.getId(), sfVolume.getTotalSize()); } else { errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to resize"; } @@ -462,9 +676,4 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } } - - @Override - public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { - throw new UnsupportedOperationException(); - } } diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 93ec4a20274..e3be2620d27 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -69,6 +69,8 @@ public class SolidFireUtil { public static final String PROVIDER_NAME = "SolidFire"; public static final String SHARED_PROVIDER_NAME = "SolidFireShared"; + public static final String LOG_PREFIX = "SolidFire: "; + public static final String MANAGEMENT_VIP = "mVip"; public static final String STORAGE_VIP = "sVip"; @@ -90,6 +92,13 @@ 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"; public static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername"; public static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret"; @@ -554,10 +563,8 @@ public class SolidFireUtil { return deletedVolumes; } - public static SolidFireVolume deleteSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) + public static void deleteSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) { - SolidFireVolume sfVolume = getSolidFireVolume(sfConnection, lVolumeId); - final Gson gson = new GsonBuilder().create(); VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId); @@ -565,8 +572,6 @@ public class SolidFireUtil { String strVolumeToDeleteJson = gson.toJson(volumeToDelete); executeJsonRpc(sfConnection, strVolumeToDeleteJson); - - return sfVolume; } public static void purgeSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) @@ -657,6 +662,49 @@ public class SolidFireUtil { } } + public static long createSolidFireSnapshot(SolidFireConnection sfConnection, long lVolumeId, String snapshotName) { + final Gson gson = new GsonBuilder().create(); + + SnapshotToCreate snapshotToCreate = new SnapshotToCreate(lVolumeId, snapshotName); + + String strSnapshotToCreateJson = gson.toJson(snapshotToCreate); + + String strSnapshotCreateResultJson = executeJsonRpc(sfConnection, strSnapshotToCreateJson); + + SnapshotCreateResult snapshotCreateResult = gson.fromJson(strSnapshotCreateResultJson, SnapshotCreateResult.class); + + verifyResult(snapshotCreateResult.result, strSnapshotCreateResultJson, gson); + + return snapshotCreateResult.result.snapshotID; + } + + public static void deleteSolidFireSnapshot(SolidFireConnection sfConnection, long lSnapshotId) + { + final Gson gson = new GsonBuilder().create(); + + SnapshotToDelete snapshotToDelete = new SnapshotToDelete(lSnapshotId); + + String strSnapshotToDeleteJson = gson.toJson(snapshotToDelete); + + executeJsonRpc(sfConnection, strSnapshotToDeleteJson); + } + + public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, String cloneName) { + final Gson gson = new GsonBuilder().create(); + + CloneToCreate cloneToCreate = new CloneToCreate(lVolumeId, cloneName); + + String strCloneToCreateJson = gson.toJson(cloneToCreate); + + String strCloneCreateResultJson = executeJsonRpc(sfConnection, strCloneToCreateJson); + + CloneCreateResult cloneCreateResult = gson.fromJson(strCloneCreateResultJson, CloneCreateResult.class); + + verifyResult(cloneCreateResult.result, strCloneCreateResultJson, gson); + + return cloneCreateResult.result.cloneID; + } + public static long createSolidFireAccount(SolidFireConnection sfConnection, String strAccountName) { final Gson gson = new GsonBuilder().create(); @@ -1206,6 +1254,65 @@ public class SolidFireUtil { } } + @SuppressWarnings("unused") + private static final class SnapshotToCreate { + private final String method = "CreateSnapshot"; + private final SnapshotToCreateParams params; + + private SnapshotToCreate(final long lVolumeId, final String snapshotName) { + params = new SnapshotToCreateParams(lVolumeId, snapshotName); + } + + private static final class SnapshotToCreateParams { + private long volumeID; + private String name; + + private SnapshotToCreateParams(final long lVolumeId, final String snapshotName) { + volumeID = lVolumeId; + name = snapshotName; + } + } + } + + @SuppressWarnings("unused") + private static final class SnapshotToDelete + { + private final String method = "DeleteSnapshot"; + private final SnapshotToDeleteParams params; + + private SnapshotToDelete(final long lSnapshotId) { + params = new SnapshotToDeleteParams(lSnapshotId); + } + + private static final class SnapshotToDeleteParams { + private long snapshotID; + + private SnapshotToDeleteParams(final long lSnapshotId) { + snapshotID = lSnapshotId; + } + } + } + + @SuppressWarnings("unused") + private static final class CloneToCreate { + private final String method = "CloneVolume"; + private final CloneToCreateParams params; + + private CloneToCreate(final long lVolumeId, final String cloneName) { + params = new CloneToCreateParams(lVolumeId, cloneName); + } + + private static final class CloneToCreateParams { + private long volumeID; + private String name; + + private CloneToCreateParams(final long lVolumeId, final String cloneName) { + volumeID = lVolumeId; + name = cloneName; + } + } + } + @SuppressWarnings("unused") private static final class AccountToAdd { @@ -1424,6 +1531,22 @@ public class SolidFireUtil { } } + private static final class SnapshotCreateResult { + private Result result; + + private static final class Result { + private long snapshotID; + } + } + + private static final class CloneCreateResult { + private Result result; + + private static final class Result { + private long cloneID; + } + } + private static final class AccountAddResult { private Result result; @@ -1528,7 +1651,7 @@ public class SolidFireUtil { httpClient = getHttpClient(sfConnection.getManagementPort()); - URI uri = new URI("https://" + sfConnection.getManagementVip() + ":" + sfConnection.getManagementPort() + "/json-rpc/5.0"); + URI uri = new URI("https://" + sfConnection.getManagementVip() + ":" + sfConnection.getManagementPort() + "/json-rpc/6.0"); AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword()); diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 8f2a2b2ed21..e5b7d191e2c 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -516,30 +516,16 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, @Override public long getUsedBytes(StoragePoolVO pool) { - long usedBytes = 0; + DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); - List volumes = _volumeDao.findByPoolId(pool.getId(), null); + if (storeDriver instanceof PrimaryDataStoreDriver) { + PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; - if (volumes != null && volumes.size() > 0) { - DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); - DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); - PrimaryDataStoreDriver primaryStoreDriver = null; - - if (storeDriver instanceof PrimaryDataStoreDriver) { - primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; - } - - for (VolumeVO volume : volumes) { - if (primaryStoreDriver != null) { - usedBytes += primaryStoreDriver.getVolumeSizeIncludingHypervisorSnapshotReserve(volume, pool); - } - else { - usedBytes += volume.getSize(); - } - } + return primaryStoreDriver.getUsedBytes(pool); } - return usedBytes; + throw new CloudRuntimeException("Storage driver in CapacityManagerImpl.getUsedBytes(StoragePoolVO) is not a PrimaryDataStoreDriver."); } @Override @@ -548,7 +534,7 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, List volumes = _volumeDao.findByPoolId(pool.getId(), null); - if (volumes != null && volumes.size() > 0) { + if (volumes != null) { for (VolumeVO volume : volumes) { usedIops += volume.getMinIops() != null ? volume.getMinIops() : 0; } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index cdd04062452..c1e6be2567d 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -1578,7 +1578,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } } if (volume.getState() != Volume.State.Ready) { - totalAskingSize = totalAskingSize + getVolumeSizeIncludingHvSsReserve(volume, pool); + totalAskingSize = totalAskingSize + getVolumeSizeIncludingHypervisorSnapshotReserve(volume, pool); } } @@ -1623,7 +1623,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return true; } - private long getVolumeSizeIncludingHvSsReserve(Volume volume, StoragePool pool) { + private long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); DataStoreDriver storeDriver = storeProvider.getDataStoreDriver();