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();