You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mt...@apache.org on 2014/09/04 04:11:26 UTC
git commit: updated refs/heads/master to 1d2f330
Repository: cloudstack
Updated Branches:
refs/heads/master 659eafffe -> 1d2f3300a
Adding support for SolidFire snapshots
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/1d2f3300
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/1d2f3300
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/1d2f3300
Branch: refs/heads/master
Commit: 1d2f3300adab216fe962bb35d0c5a342ba4ec785
Parents: 659eaff
Author: Mike Tutkowski <mi...@solidfire.com>
Authored: Mon Aug 25 16:15:04 2014 -0600
Committer: Mike Tutkowski <mi...@solidfire.com>
Committed: Wed Sep 3 20:09:00 2014 -0600
----------------------------------------------------------------------
.../api/storage/PrimaryDataStoreDriver.java | 9 +-
.../test/FakePrimaryDataStoreDriver.java | 5 +
.../storage/snapshot/SnapshotServiceImpl.java | 6 +-
.../CloudStackPrimaryDataStoreDriverImpl.java | 5 +
.../driver/NexentaPrimaryDataStoreDriver.java | 5 +
.../SamplePrimaryDataStoreDriverImpl.java | 5 +
.../driver/SolidFirePrimaryDataStoreDriver.java | 263 +++++++++++++++++--
.../storage/datastore/util/SolidFireUtil.java | 135 +++++++++-
.../com/cloud/capacity/CapacityManagerImpl.java | 28 +-
.../com/cloud/storage/StorageManagerImpl.java | 4 +-
10 files changed, 405 insertions(+), 60 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java
----------------------------------------------------------------------
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 c4dfc5c..43d7d5a 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<CreateCmdResult> callback);
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java
----------------------------------------------------------------------
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 d277991..7d9cd72 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
@@ -55,6 +55,11 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
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();
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
----------------------------------------------------------------------
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 684b5bd..309f6d6 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);
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
----------------------------------------------------------------------
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 4115289..d58c0b1 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
@@ -158,6 +158,11 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
}
@Override
+ public long getUsedBytes(StoragePool storagePool) {
+ return 0;
+ }
+
+ @Override
public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
return volume.getSize();
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java
----------------------------------------------------------------------
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 70f4a4f..aff50e5 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
@@ -68,6 +68,11 @@ public class NexentaPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
@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.
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java
----------------------------------------------------------------------
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 1f9a128..4ade467 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
@@ -88,6 +88,11 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
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();
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java
----------------------------------------------------------------------
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 e5a1585..e966cf6 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<String, String> getCapabilities() {
@@ -229,13 +242,53 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
@Override
+ public long getUsedBytes(StoragePool storagePool) {
+ long usedSpace = 0;
+
+ List<VolumeVO> 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<SnapshotVO> 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<CommandResult> 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,16 +472,146 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
@Override
- public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
+ public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> 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<CreateCmdResult> 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<CommandResult> 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<CreateCmdResult> callback) {
- throw new UnsupportedOperationException();
- }
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java
----------------------------------------------------------------------
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 93ec4a2..e3be262 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();
@@ -1207,6 +1255,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
{
private final String method = "AddAccount";
@@ -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());
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/server/src/com/cloud/capacity/CapacityManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java
index 8f2a2b2..e5b7d19 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<VolumeVO> volumes = _volumeDao.findByPoolId(pool.getId(), null);
-
- 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;
- }
+ if (storeDriver instanceof PrimaryDataStoreDriver) {
+ 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<VolumeVO> 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;
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d2f3300/server/src/com/cloud/storage/StorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java
index cdd0406..c1e6be2 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();