cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sw...@apache.org
Subject [2/8] git commit: updated refs/heads/master to 82b702d
Date Fri, 20 May 2016 12:33:31 GMT
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/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 5f647db..af969e1 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
@@ -17,32 +17,13 @@
 package org.apache.cloudstack.storage.datastore.driver;
 
 import java.text.NumberFormat;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import javax.inject.Inject;
 
-import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
-import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
-import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
-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.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;
 import com.cloud.agent.api.to.DataStoreTO;
@@ -55,16 +36,19 @@ import com.cloud.dc.dao.ClusterDao;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
-import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ResizeVolumePayload;
+import com.cloud.storage.Snapshot.State;
+import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.StoragePool;
-import com.cloud.storage.Volume;
+import com.cloud.storage.VMTemplateStoragePoolVO;
 import com.cloud.storage.VolumeDetailVO;
 import com.cloud.storage.VolumeVO;
-import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.dao.SnapshotDao;
 import com.cloud.storage.dao.SnapshotDetailsDao;
 import com.cloud.storage.dao.SnapshotDetailsVO;
+import com.cloud.storage.dao.VMTemplatePoolDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.dao.VolumeDetailsDao;
 import com.cloud.user.AccountDetailVO;
@@ -74,27 +58,64 @@ import com.cloud.user.dao.AccountDao;
 import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.exception.CloudRuntimeException;
 
+import com.google.common.base.Preconditions;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+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.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;
+
 public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
-    private static final Logger s_logger = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class);
-    private static final int s_lowestHypervisorSnapshotReserve = 10;
-
-    @Inject private AccountDao _accountDao;
-    @Inject private AccountDetailsDao _accountDetailsDao;
-    @Inject private ClusterDao _clusterDao;
-    @Inject private ClusterDetailsDao _clusterDetailsDao;
-    @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;
+    private static final Logger LOGGER = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class);
+    private static final int LOCK_TIME_IN_SECONDS = 300;
+    private static final int LOWEST_HYPERVISOR_SNAPSHOT_RESERVE = 10;
+    private static final long MIN_IOPS_FOR_TEMPLATE_VOLUME = 100L;
+    private static final long MAX_IOPS_FOR_TEMPLATE_VOLUME = 20000L;
+    private static final long MIN_IOPS_FOR_TEMP_VOLUME = 100L;
+    private static final long MAX_IOPS_FOR_TEMP_VOLUME = 20000L;
+    private static final long MIN_IOPS_FOR_SNAPSHOT_VOLUME = 100L;
+    private static final long MAX_IOPS_FOR_SNAPSHOT_VOLUME = 20000L;
+
+    @Inject private AccountDao accountDao;
+    @Inject private AccountDetailsDao accountDetailsDao;
+    @Inject private ClusterDao clusterDao;
+    @Inject private ClusterDetailsDao clusterDetailsDao;
+    @Inject private DataStoreManager dataStoreMgr;
+    @Inject private HostDao hostDao;
+    @Inject private SnapshotDao snapshotDao;
+    @Inject private SnapshotDetailsDao snapshotDetailsDao;
+    @Inject private PrimaryDataStoreDao storagePoolDao;
+    @Inject private StoragePoolDetailsDao storagePoolDetailsDao;
+    @Inject private VMTemplatePoolDao tmpltPoolDao;
+    @Inject private VolumeDao volumeDao;
+    @Inject private VolumeDetailsDao volumeDetailsDao;
+    @Inject private VolumeDataFactory volumeFactory;
 
     @Override
     public Map<String, String> getCapabilities() {
-        Map<String, String> mapCapabilities = new HashMap<String, String>();
+        Map<String, String> mapCapabilities = new HashMap<>();
 
         mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString());
+        mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString());
+        mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString(), Boolean.TRUE.toString());
 
         return mapCapabilities;
     }
@@ -116,7 +137,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     }
 
     @Override
-    public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
+    public ChapInfo getChapInfo(DataObject dataObject) {
         return null;
     }
 
@@ -128,38 +149,42 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     @Override
     public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore)
     {
-        if (dataObject == null || host == null || dataStore == null) {
-            return false;
-        }
+        Preconditions.checkArgument(dataObject != null, "'dataObject' should not be 'null'");
+        Preconditions.checkArgument(host != null, "'host' should not be 'null'");
+        Preconditions.checkArgument(dataStore != null, "'dataStore' should not be 'null'");
 
         long sfVolumeId = getSolidFireVolumeId(dataObject);
         long clusterId = host.getClusterId();
         long storagePoolId = dataStore.getId();
 
-        ClusterVO cluster = _clusterDao.findById(clusterId);
+        ClusterVO cluster = clusterDao.findById(clusterId);
 
         GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid());
 
-        if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) {
+        if (!lock.lock(LOCK_TIME_IN_SECONDS)) {
             String errMsg = "Couldn't lock the DB (in grantAccess) on the following string: " + cluster.getUuid();
 
-            s_logger.debug(errMsg);
+            LOGGER.warn(errMsg);
 
             throw new CloudRuntimeException(errMsg);
         }
 
         try {
-            ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId));
+            ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId));
 
             String vagId = clusterDetail != null ? clusterDetail.getValue() : null;
 
-            List<HostVO> hosts = _hostDao.findByClusterId(clusterId);
+            List<HostVO> hosts = hostDao.findByClusterId(clusterId);
 
             if (!SolidFireUtil.hostsSupport_iScsi(hosts)) {
-                return false;
+                String errMsg = "Not all hosts in the compute cluster support iSCSI.";
+
+                LOGGER.warn(errMsg);
+
+                throw new CloudRuntimeException(errMsg);
             }
 
-            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
 
             if (vagId != null) {
                 SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId));
@@ -169,7 +194,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
                 SolidFireUtil.modifySolidFireVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds);
             }
             else {
-                SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, cluster.getUuid(), hosts, _clusterDetailsDao);
+                SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, cluster.getUuid(), hosts, clusterDetailsDao);
             }
 
             return true;
@@ -194,25 +219,25 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         long clusterId = host.getClusterId();
         long storagePoolId = dataStore.getId();
 
-        ClusterVO cluster = _clusterDao.findById(clusterId);
+        ClusterVO cluster = clusterDao.findById(clusterId);
 
         GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid());
 
-        if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) {
+        if (!lock.lock(LOCK_TIME_IN_SECONDS)) {
             String errMsg = "Couldn't lock the DB (in revokeAccess) on the following string: " + cluster.getUuid();
 
-            s_logger.debug(errMsg);
+            LOGGER.debug(errMsg);
 
             throw new CloudRuntimeException(errMsg);
         }
 
         try {
-            ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId));
+            ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId));
 
             String vagId = clusterDetail != null ? clusterDetail.getValue() : null;
 
             if (vagId != null) {
-                SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+                SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
 
                 SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId));
 
@@ -233,7 +258,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         }
 
         if (dataObject.getType() == DataObjectType.SNAPSHOT) {
-            SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(dataObject.getId(), SolidFireUtil.VOLUME_ID);
+            SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(dataObject.getId(), SolidFireUtil.VOLUME_ID);
 
             if (snapshotDetails == null || snapshotDetails.getValue() == null) {
                 throw new CloudRuntimeException("Unable to locate the volume ID associated with the following snapshot ID: " + dataObject.getId());
@@ -242,11 +267,26 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             return Long.parseLong(snapshotDetails.getValue());
         }
 
+        if (dataObject.getType() == DataObjectType.TEMPLATE) {
+            return getVolumeIdFrom_iScsiPath(((TemplateInfo)dataObject).getInstallPath());
+        }
+
         throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to getSolidFireVolumeId(DataObject)");
     }
 
+    private long getVolumeIdFrom_iScsiPath(String iScsiPath) {
+        String[] splits = iScsiPath.split("/");
+        String iqn = splits[1];
+
+        String sequenceToSearchFor = ".";
+        int lastIndexOf = iqn.lastIndexOf(sequenceToSearchFor);
+        String volumeIdAsString = iqn.substring(lastIndexOf + sequenceToSearchFor.length());
+
+        return Long.parseLong(volumeIdAsString);
+    }
+
     private long getDefaultMinIops(long storagePoolId) {
-        StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS);
+        StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS);
 
         String clusterDefaultMinIops = storagePoolDetail.getValue();
 
@@ -254,7 +294,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     }
 
     private long getDefaultMaxIops(long storagePoolId) {
-        StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS);
+        StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS);
 
         String clusterDefaultMaxIops = storagePoolDetail.getValue();
 
@@ -262,7 +302,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     }
 
     private long getDefaultBurstIops(long storagePoolId, long maxIops) {
-        StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS);
+        StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS);
 
         String clusterDefaultBurstIopsPercentOfMaxIops = storagePoolDetail.getValue();
 
@@ -271,30 +311,56 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         return (long)(maxIops * fClusterDefaultBurstIopsPercentOfMaxIops);
     }
 
-    private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, VolumeInfo volumeInfo, long sfAccountId) {
-        long storagePoolId = volumeInfo.getDataStore().getId();
+    private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, DataObject dataObject, long sfAccountId) {
+        long storagePoolId = dataObject.getDataStore().getId();
+        Long minIops = null;
+        Long maxIops = null;
+        Long volumeSize = dataObject.getSize();
+        String volumeName = null;
 
-        final Iops iops;
+        final Map<String, String> mapAttributes;
 
-        Long minIops = volumeInfo.getMinIops();
-        Long maxIops = volumeInfo.getMaxIops();
+        if (dataObject.getType() == DataObjectType.VOLUME) {
+            VolumeInfo volumeInfo = (VolumeInfo)dataObject;
 
-        if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) {
-            long defaultMaxIops = getDefaultMaxIops(storagePoolId);
+            minIops = volumeInfo.getMinIops();
+            maxIops = volumeInfo.getMaxIops();
+            volumeSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, storagePoolDao.findById(storagePoolId));
+            volumeName = volumeInfo.getName();
 
-            iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops));
-        } else {
-            iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
+            mapAttributes = getVolumeAttributes(volumeInfo);
+        } else if (dataObject.getType() == DataObjectType.TEMPLATE) {
+            TemplateInfo templateInfo = (TemplateInfo)dataObject;
+
+            minIops = MIN_IOPS_FOR_TEMPLATE_VOLUME;
+            maxIops = MAX_IOPS_FOR_TEMPLATE_VOLUME;
+            volumeSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(templateInfo, storagePoolDao.findById(storagePoolId));
+            volumeName = templateInfo.getUniqueName();
+
+            mapAttributes = getTemplateAttributes(templateInfo);
+        }
+        else {
+            throw new CloudRuntimeException("Invalid type passed to createSolidFireVolume: " + dataObject.getType());
         }
 
-        long volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId));
+        final Iops iops = getIops(minIops, maxIops, storagePoolId);
 
-        long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true,
-                NumberFormat.getInstance().format(volumeInfo.getSize()), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
+        long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeName), sfAccountId,
+                volumeSize, true, mapAttributes, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
 
         return SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId);
     }
 
+    private Iops getIops(Long minIops, Long maxIops, long storagePoolId) {
+        if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) {
+            long defaultMaxIops = getDefaultMaxIops(storagePoolId);
+
+            return new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops));
+        }
+
+        return new Iops(minIops, maxIops, getDefaultBurstIops(storagePoolId, maxIops));
+    }
+
     @Override
     public long getUsedBytes(StoragePool storagePool) {
         return getUsedBytes(storagePool, Long.MIN_VALUE);
@@ -303,7 +369,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     private long getUsedBytes(StoragePool storagePool, long volumeIdToIgnore) {
         long usedSpace = 0;
 
-        List<VolumeVO> lstVolumes = _volumeDao.findByPoolId(storagePool.getId(), null);
+        List<VolumeVO> lstVolumes = volumeDao.findByPoolId(storagePool.getId(), null);
 
         if (lstVolumes != null) {
             for (VolumeVO volume : lstVolumes) {
@@ -311,7 +377,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
                     continue;
                 }
 
-                VolumeDetailVO volumeDetail = _volumeDetailsDao.findDetail(volume.getId(), SolidFireUtil.VOLUME_SIZE);
+                VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volume.getId(), SolidFireUtil.VOLUME_SIZE);
 
                 if (volumeDetail != null && volumeDetail.getValue() != null) {
                     long volumeSize = Long.parseLong(volumeDetail.getValue());
@@ -319,18 +385,22 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
                     usedSpace += volumeSize;
                 }
                 else {
-                    SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), _storagePoolDetailsDao);
+                    SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao);
 
                     try {
                         long lVolumeId = Long.parseLong(volume.getFolder());
 
                         SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, lVolumeId);
 
+                        long volumeSize = sfVolume.getTotalSize();
+
                         // 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 here.
-                        updateVolumeDetails(volume.getId(), sfVolume.getTotalSize());
+                        updateVolumeDetails(volume.getId(), volumeSize);
+
+                        usedSpace += volumeSize;
                     }
                     catch (NumberFormatException ex) {
                         // can be ignored (the "folder" column didn't have a valid "long" in it (hasn't been placed there yet))
@@ -339,15 +409,15 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             }
         }
 
-        List<SnapshotVO> lstSnapshots = _snapshotDao.listAll();
+        List<SnapshotVO> lstSnapshots = snapshotDao.listAll();
 
         if (lstSnapshots != null) {
             for (SnapshotVO snapshot : lstSnapshots) {
-                SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID);
+                SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID);
 
                 // if this snapshot belongs to the storagePool that was passed in
                 if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePool.getId()) {
-                    snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.VOLUME_SIZE);
+                    snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.VOLUME_SIZE);
 
                     if (snapshotDetails != null && snapshotDetails.getValue() != null) {
                         long snapshotSize = Long.parseLong(snapshotDetails.getValue());
@@ -358,6 +428,14 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             }
         }
 
+        List<VMTemplateStoragePoolVO> lstTemplatePoolRefs = tmpltPoolDao.listByPoolId(storagePool.getId());
+
+        if (lstTemplatePoolRefs != null) {
+            for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) {
+                usedSpace += templatePoolRef.getTemplateSize();
+            }
+        }
+
         return usedSpace;
     }
 
@@ -365,7 +443,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     public long getUsedIops(StoragePool storagePool) {
         long usedIops = 0;
 
-        List<VolumeVO> volumes = _volumeDao.findByPoolId(storagePool.getId(), null);
+        List<VolumeVO> volumes = volumeDao.findByPoolId(storagePool.getId(), null);
 
         if (volumes != null) {
             for (VolumeVO volume : volumes) {
@@ -377,12 +455,25 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
     }
 
     @Override
-    public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) {
-        long volumeSize = volume.getSize();
-        Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve();
+    public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) {
+        long volumeSize = 0;
+
+        if (dataObject.getType() == DataObjectType.VOLUME) {
+            VolumeInfo volume = (VolumeInfo)dataObject;
+
+            volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volume.getSize(), volume.getHypervisorSnapshotReserve());
+        } else if (dataObject.getType() == DataObjectType.TEMPLATE) {
+            TemplateInfo templateInfo = (TemplateInfo)dataObject;
+
+            volumeSize = (long)(templateInfo.getSize() + templateInfo.getSize() * (LOWEST_HYPERVISOR_SNAPSHOT_RESERVE / 100f));
+        }
 
+        return volumeSize;
+    }
+
+    private long getVolumeSizeIncludingHypervisorSnapshotReserve(long volumeSize, Integer hypervisorSnapshotReserve) {
         if (hypervisorSnapshotReserve != null) {
-            hypervisorSnapshotReserve = Math.max(hypervisorSnapshotReserve, s_lowestHypervisorSnapshotReserve);
+            hypervisorSnapshotReserve = Math.max(hypervisorSnapshotReserve, LOWEST_HYPERVISOR_SNAPSHOT_RESERVE);
 
             volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f);
         }
@@ -390,6 +481,29 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         return volumeSize;
     }
 
+    /**
+     * This method is only relevant when storagePool is being used with a compute cluster that supports UUID resigning.
+     */
+    @Override
+    public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) {
+        List<VMTemplateStoragePoolVO> lstTemplatePoolRefs = tmpltPoolDao.listByPoolId(storagePool.getId());
+
+        if (lstTemplatePoolRefs != null) {
+            for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) {
+                if (templatePoolRef.getTemplateId() == templateInfo.getId()) {
+                    // This indicates that we already have this template stored on this primary storage, so
+                    // we do not require additional space.
+                    return 0;
+                }
+            }
+        }
+
+        // This indicates that we do not have a copy of this template on this primary storage, so
+        // we need to take it into consideration from a space standpoint (ex. when a new VM is spun
+        // up and wants to use this particular template for its root disk).
+        return getDataObjectSizeIncludingHypervisorSnapshotReserve(templateInfo, storagePool);
+    }
+
     private static class Iops {
         private final long _minIops;
         private final long _maxIops;
@@ -436,7 +550,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
 
         long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
 
-        SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId);
+        deleteSolidFireVolume(sfConnection, volumeInfo.getId(), sfVolumeId);
     }
 
     @Override
@@ -444,114 +558,234 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         String iqn = null;
         String errMsg = null;
 
-        if (dataObject.getType() == DataObjectType.VOLUME) {
-            VolumeInfo volumeInfo = (VolumeInfo)dataObject;
+        try {
+            if (dataObject.getType() == DataObjectType.VOLUME) {
+                iqn = createVolume((VolumeInfo)dataObject, dataStore.getId());
+            } else if (dataObject.getType() == DataObjectType.SNAPSHOT) {
+                createTempVolume((SnapshotInfo)dataObject, dataStore.getId());
+            } else if (dataObject.getType() == DataObjectType.TEMPLATE) {
+                iqn = createTemplateVolume((TemplateInfo)dataObject, dataStore.getId());
+            } else {
+                errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
+
+                LOGGER.error(errMsg);
+            }
+        }
+        catch (Exception ex) {
+            errMsg = ex.getMessage();
 
-            long storagePoolId = dataStore.getId();
+            LOGGER.error(errMsg);
 
-            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+            if (callback == null) {
+                throw ex;
+            }
+        }
 
-            AccountDetailVO accountDetail = SolidFireUtil.getAccountDetail(volumeInfo.getAccountId(), storagePoolId, _accountDetailsDao);
+        if (callback != null) {
+            // path = iqn
+            // size is pulled from DataObject instance, if errMsg is null
+            CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg));
 
-            if (accountDetail == null || accountDetail.getValue() == null) {
-                AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
-                String sfAccountName = SolidFireUtil.getSolidFireAccountName(account.getUuid(), account.getAccountId());
-                SolidFireUtil.SolidFireAccount sfAccount = SolidFireUtil.getSolidFireAccount(sfConnection, sfAccountName);
+            result.setResult(errMsg);
 
-                if (sfAccount == null) {
-                    sfAccount = createSolidFireAccount(sfConnection, sfAccountName);
-                }
+            callback.complete(result);
+        }
+    }
+
+    private long getCreateSolidFireAccountId(SolidFireUtil.SolidFireConnection sfConnection, long csAccountId, long storagePoolId) {
+        AccountDetailVO accountDetail = SolidFireUtil.getAccountDetail(csAccountId, storagePoolId, accountDetailsDao);
 
-                SolidFireUtil.updateCsDbWithSolidFireAccountInfo(account.getId(), sfAccount, storagePoolId, _accountDetailsDao);
+        if (accountDetail == null || accountDetail.getValue() == null) {
+            AccountVO account = accountDao.findById(csAccountId);
+            String sfAccountName = SolidFireUtil.getSolidFireAccountName(account.getUuid(), account.getAccountId());
+            SolidFireUtil.SolidFireAccount sfAccount = SolidFireUtil.getSolidFireAccount(sfConnection, sfAccountName);
 
-                accountDetail = SolidFireUtil.getAccountDetail(volumeInfo.getAccountId(), storagePoolId, _accountDetailsDao);
+            if (sfAccount == null) {
+                sfAccount = createSolidFireAccount(sfConnection, sfAccountName);
             }
 
-            long sfAccountId = Long.parseLong(accountDetail.getValue());
+            SolidFireUtil.updateCsDbWithSolidFireAccountInfo(account.getId(), sfAccount, storagePoolId, accountDetailsDao);
 
-            SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(sfConnection, volumeInfo, sfAccountId);
+            accountDetail = SolidFireUtil.getAccountDetail(csAccountId, storagePoolId, accountDetailsDao);
+        }
 
-            iqn = sfVolume.getIqn();
+        return Long.parseLong(accountDetail.getValue());
+    }
 
-            VolumeVO volume = _volumeDao.findById(volumeInfo.getId());
+    private void handleSnapshotDetails(long csSnapshotId, String name, String value) {
+        snapshotDetailsDao.removeDetail(csSnapshotId, name);
 
-            volume.set_iScsiName(iqn);
-            volume.setFolder(String.valueOf(sfVolume.getId()));
-            volume.setPoolType(StoragePoolType.IscsiLUN);
-            volume.setPoolId(storagePoolId);
+        SnapshotDetailsVO snapshotDetails = new SnapshotDetailsVO(csSnapshotId, name, value, false);
 
-            _volumeDao.update(volume.getId(), volume);
+        snapshotDetailsDao.persist(snapshotDetails);
+    }
 
-            updateVolumeDetails(volume.getId(), sfVolume.getTotalSize());
+    private void addTempVolumeId(long csSnapshotId, String tempVolumeId) {
+        SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID);
 
-            StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId());
+        if (snapshotDetails == null || snapshotDetails.getValue() == null) {
+            throw new CloudRuntimeException("'addTempVolumeId' should not be invoked unless " + SolidFireUtil.VOLUME_ID + " exists.");
+        }
 
-            long capacityBytes = storagePool.getCapacityBytes();
-            // 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);
+        String originalVolumeId = snapshotDetails.getValue();
 
-            storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes);
+        handleSnapshotDetails(csSnapshotId, SolidFireUtil.TEMP_VOLUME_ID, originalVolumeId);
+        handleSnapshotDetails(csSnapshotId, SolidFireUtil.VOLUME_ID, tempVolumeId);
+    }
 
-            _storagePoolDao.update(storagePoolId, storagePool);
-        } else {
-            errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
+    private void removeTempVolumeId(long csSnapshotId) {
+        SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.TEMP_VOLUME_ID);
+
+        if (snapshotDetails == null || snapshotDetails.getValue() == null) {
+            throw new CloudRuntimeException("'removeTempVolumeId' should not be invoked unless " + SolidFireUtil.TEMP_VOLUME_ID + " exists.");
         }
 
-        // path = iqn
-        // size is pulled from DataObject instance, if errMsg is null
-        CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg));
+        String originalVolumeId = snapshotDetails.getValue();
 
-        result.setResult(errMsg);
+        handleSnapshotDetails(csSnapshotId, SolidFireUtil.VOLUME_ID, originalVolumeId);
 
-        callback.complete(result);
+        snapshotDetailsDao.remove(snapshotDetails.getId());
     }
 
-    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);
+    private long getCsIdForCloning(long volumeId, String cloneOf) {
+        VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, cloneOf);
 
-            _volumeDetailsDao.persist(volumeDetailVo);
+        if (volumeDetail != null && volumeDetail.getValue() != null) {
+            return new Long(volumeDetail.getValue());
         }
+
+        return Long.MIN_VALUE;
     }
 
-    @Override
-    public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CommandResult> callback) {
-        String errMsg = null;
+    private boolean shouldTakeSnapshot(long snapshotId) {
+        SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, "takeSnapshot");
 
-        if (dataObject.getType() == DataObjectType.VOLUME) {
-            try {
-                VolumeInfo volumeInfo = (VolumeInfo)dataObject;
-                long volumeId = volumeInfo.getId();
+        if (snapshotDetails != null && snapshotDetails.getValue() != null) {
+            return new Boolean(snapshotDetails.getValue());
+        }
 
-                long storagePoolId = dataStore.getId();
+        return false;
+    }
 
-                SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+    private SolidFireUtil.SolidFireVolume createClone(SolidFireUtil.SolidFireConnection sfConnection, long dataObjectId, VolumeInfo volumeInfo, long sfAccountId,
+            long storagePoolId, DataObjectType dataObjectType) {
+        String sfNewVolumeName = volumeInfo.getName();
 
-                deleteSolidFireVolume(sfConnection, volumeInfo);
+        long sfVolumeId = Long.MIN_VALUE;
+        long sfSnapshotId = Long.MIN_VALUE;
 
-                _volumeDetailsDao.removeDetails(volumeId);
+        if (dataObjectType == DataObjectType.SNAPSHOT) {
+            SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(dataObjectId, SolidFireUtil.SNAPSHOT_ID);
 
-                StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
+            if (snapshotDetails != null && snapshotDetails.getValue() != null) {
+                sfSnapshotId = Long.parseLong(snapshotDetails.getValue());
+            }
 
-                long usedBytes = getUsedBytes(storagePool, volumeId);
+            snapshotDetails = snapshotDetailsDao.findDetail(dataObjectId, SolidFireUtil.VOLUME_ID);
 
-                storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);
+            sfVolumeId = Long.parseLong(snapshotDetails.getValue());
+        } else if (dataObjectType == DataObjectType.TEMPLATE) {
+            // get the cached template on this storage
+            VMTemplateStoragePoolVO templatePoolRef = tmpltPoolDao.findByPoolTemplate(storagePoolId, dataObjectId);
 
-                _storagePoolDao.update(storagePoolId, storagePool);
+            if (templatePoolRef != null) {
+                sfVolumeId = Long.parseLong(templatePoolRef.getLocalDownloadPath());
             }
-            catch (Exception ex) {
-                s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume", ex);
+        }
+
+        if (sfVolumeId <= 0) {
+            throw new CloudRuntimeException("Unable to find SolidFire volume for the following data-object ID: " + dataObjectId +
+                    " and data-object type: " + dataObjectType);
+        }
+
+        final long newSfVolumeId = SolidFireUtil.createSolidFireClone(sfConnection, sfVolumeId, sfSnapshotId, sfAccountId, sfNewVolumeName,
+                getVolumeAttributes(volumeInfo));
+
+        final Iops iops = getIops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), storagePoolId);
+
+        SolidFireUtil.modifySolidFireVolume(sfConnection, newSfVolumeId, null, null, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
+
+        return SolidFireUtil.getSolidFireVolume(sfConnection, newSfVolumeId);
+    }
+
+    private Map<String, String> getVolumeAttributes(VolumeInfo volumeInfo) {
+        Map<String, String> mapAttributes = new HashMap<>();
+
+        mapAttributes.put(SolidFireUtil.CloudStackVolumeId, String.valueOf(volumeInfo.getId()));
+        mapAttributes.put(SolidFireUtil.CloudStackVolumeSize, NumberFormat.getInstance().format(volumeInfo.getSize()));
+
+        return mapAttributes;
+    }
+
+    private Map<String, String> getSnapshotAttributes(SnapshotInfo snapshotInfo) {
+        Map<String, String> mapAttributes = new HashMap<>();
+
+        mapAttributes.put(SolidFireUtil.CloudStackSnapshotId, String.valueOf(snapshotInfo.getId()));
+        mapAttributes.put(SolidFireUtil.CloudStackSnapshotSize, NumberFormat.getInstance().format(snapshotInfo.getSize()));
+
+        return mapAttributes;
+    }
+
+    private Map<String, String> getTemplateAttributes(TemplateInfo templateInfo) {
+        Map<String, String> mapAttributes = new HashMap<>();
+
+        mapAttributes.put(SolidFireUtil.CloudStackTemplateId, String.valueOf(templateInfo.getId()));
+        mapAttributes.put(SolidFireUtil.CloudStackTemplateSize, NumberFormat.getInstance().format(templateInfo.getSize()));
+
+        return mapAttributes;
+    }
+
+    private SolidFireUtil.SolidFireVolume createCloneFromSnapshot(SolidFireUtil.SolidFireConnection sfConnection, long csSnapshotId, long sfAccountId) {
+        SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID);
+
+        long sfVolumeId = Long.parseLong(snapshotDetails.getValue());
+
+        snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID);
+
+        long sfSnapshotId = Long.parseLong(snapshotDetails.getValue());
+
+        SolidFireUtil.SolidFireSnapshot sfSnapshot = SolidFireUtil.getSolidFireSnapshot(sfConnection, sfVolumeId, sfSnapshotId);
+
+        long newSfVolumeId = SolidFireUtil.createSolidFireClone(sfConnection, sfVolumeId, sfSnapshotId, sfAccountId, sfSnapshot.getName(), null);
+
+        snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.STORAGE_POOL_ID);
+
+        long storagePoolId = Long.parseLong(snapshotDetails.getValue());
+
+        final Iops iops = getIops(MIN_IOPS_FOR_TEMP_VOLUME, MAX_IOPS_FOR_TEMP_VOLUME, storagePoolId);
+
+        SolidFireUtil.modifySolidFireVolume(sfConnection, newSfVolumeId, null, null, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
+
+        return SolidFireUtil.getSolidFireVolume(sfConnection, newSfVolumeId);
+    }
+
+    private void updateVolumeDetails(long volumeId, long sfVolumeSize) {
+        volumeDetailsDao.removeDetail(volumeId, SolidFireUtil.VOLUME_SIZE);
 
-                errMsg = ex.getMessage();
+        VolumeDetailVO 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;
+
+        try {
+            if (dataObject.getType() == DataObjectType.VOLUME) {
+                deleteVolume((VolumeInfo)dataObject, dataStore.getId());
+            } else if (dataObject.getType() == DataObjectType.SNAPSHOT) {
+                deleteSnapshot((SnapshotInfo)dataObject, dataStore.getId());
+            } else if (dataObject.getType() == DataObjectType.TEMPLATE) {
+                deleteTemplate((TemplateInfo)dataObject, dataStore.getId());
+            } else {
+                errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync";
             }
-        } 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";
+        }
+        catch (Exception ex) {
+            errMsg = ex.getMessage();
+
+            LOGGER.error(errMsg);
         }
 
         CommandResult result = new CommandResult();
@@ -577,49 +811,67 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
 
         try {
             VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
-            VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
+            VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId());
 
             long sfVolumeId = Long.parseLong(volumeVO.getFolder());
             long storagePoolId = volumeVO.getPoolId();
 
-            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
 
             SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId);
 
-            StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
+            StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
 
             long capacityBytes = storagePool.getCapacityBytes();
-            // getUsedBytes(StoragePool) will not include the bytes of the proposed new volume because
-            // updateSnapshotDetails(long, long, long, long, String) has not yet been called for this new volume
+            // getUsedBytes(StoragePool) will not include the bytes of the proposed new volume or snapshot because
+            // updateSnapshotDetails has not yet been called for this new volume or snapshot
             long usedBytes = getUsedBytes(storagePool);
             long sfVolumeSize = sfVolume.getTotalSize();
 
             usedBytes += sfVolumeSize;
 
-            // For creating a volume, we need to check to make sure a sufficient amount of space remains in the primary storage.
-            // For the purpose of "charging" these bytes against storage_pool.capacityBytes, we take the full size of the SolidFire volume.
+            // For creating a volume or 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.capacity_bytes, we take the full size of the SolidFire volume
+            // that is serving as the volume the snapshot is of (either a new SolidFire volume or a SolidFire snapshot).
             if (usedBytes > capacityBytes) {
                 throw new CloudRuntimeException("Insufficient amount of space remains in this primary storage to take a snapshot");
             }
 
             storagePool.setUsedBytes(usedBytes);
 
-            String volumeName = volumeInfo.getName() + "-" + snapshotInfo.getUuid();
+            SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO();
 
-            long sfNewVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, volumeName, sfVolume.getAccountId(), sfVolumeSize,
-                    sfVolume.isEnable512e(), NumberFormat.getInstance().format(volumeInfo.getSize()), sfVolume.getMinIops(), 50000, 75000);
+            if (shouldTakeSnapshot(snapshotInfo.getId())) {
+                // We are supposed to take a SolidFire snapshot to serve as the back-end for our CloudStack volume snapshot.
 
-            // Now that we have successfully created a volume, update the space usage in the storage_pool table
-            // (even though storage_pool.used_bytes is likely no longer in use).
-            _storagePoolDao.update(storagePoolId, storagePool);
+                String sfNewSnapshotName = volumeInfo.getName() + "-" + snapshotInfo.getUuid();
 
-            SolidFireUtil.SolidFireVolume sfNewVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfNewVolumeId);
+                long sfNewSnapshotId = SolidFireUtil.createSolidFireSnapshot(sfConnection, sfVolumeId, sfNewSnapshotName, getSnapshotAttributes(snapshotInfo));
 
-            updateSnapshotDetails(snapshotInfo.getId(), sfNewVolumeId, storagePoolId, sfVolumeSize, sfNewVolume.getIqn());
+                updateSnapshotDetails(snapshotInfo.getId(), sfVolumeId, sfNewSnapshotId, storagePoolId, sfVolumeSize);
 
-            SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO();
+                snapshotObjectTo.setPath("SfSnapshotId=" + sfNewSnapshotId);
+            }
+            else {
+                // We are supposed to create a new SolidFire volume to serve as the back-end for our CloudStack volume snapshot.
+
+                String sfNewVolumeName = volumeInfo.getName() + "-" + snapshotInfo.getUuid();
 
-            snapshotObjectTo.setPath(String.valueOf(sfNewVolumeId));
+                final Iops iops = getIops(MIN_IOPS_FOR_SNAPSHOT_VOLUME, MAX_IOPS_FOR_SNAPSHOT_VOLUME, storagePoolId);
+
+                long sfNewVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, sfNewVolumeName, sfVolume.getAccountId(), sfVolumeSize,
+                        sfVolume.isEnable512e(), getSnapshotAttributes(snapshotInfo), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
+
+                SolidFireUtil.SolidFireVolume sfNewVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfNewVolumeId);
+
+                updateSnapshotDetails(snapshotInfo.getId(), sfNewVolumeId, storagePoolId, sfVolumeSize, sfNewVolume.getIqn());
+
+                snapshotObjectTo.setPath("SfVolumeId=" + sfNewVolumeId);
+            }
+
+            // Now that we have successfully created a volume or a snapshot, update the space usage in the cloud.storage_pool table
+            // (even though cloud.storage_pool.used_bytes is likely no longer in use).
+            storagePoolDao.update(storagePoolId, storagePool);
 
             CreateObjectAnswer createObjectAnswer = new CreateObjectAnswer(snapshotObjectTo);
 
@@ -628,7 +880,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             result.setResult(null);
         }
         catch (Exception ex) {
-            s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex);
+            LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex);
 
             result = new CreateCmdResult(null, new CreateObjectAnswer(ex.toString()));
 
@@ -638,69 +890,293 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         callback.complete(result);
     }
 
+    private void updateSnapshotDetails(long csSnapshotId, long sfVolumeId, long sfNewSnapshotId, long storagePoolId, long sfNewVolumeSize) {
+        SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
+                SolidFireUtil.VOLUME_ID,
+                String.valueOf(sfVolumeId),
+                false);
+
+        snapshotDetailsDao.persist(snapshotDetail);
+
+        snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
+                SolidFireUtil.SNAPSHOT_ID,
+                String.valueOf(sfNewSnapshotId),
+                false);
+
+        snapshotDetailsDao.persist(snapshotDetail);
+
+        snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
+                SolidFireUtil.STORAGE_POOL_ID,
+                String.valueOf(storagePoolId),
+                false);
+
+        snapshotDetailsDao.persist(snapshotDetail);
+
+        snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
+                SolidFireUtil.VOLUME_SIZE,
+                String.valueOf(sfNewVolumeSize),
+                false);
+
+        snapshotDetailsDao.persist(snapshotDetail);
+    }
+
     private void updateSnapshotDetails(long csSnapshotId, long sfNewVolumeId, long storagePoolId, long sfNewVolumeSize, String sfNewVolumeIqn) {
         SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
                 SolidFireUtil.VOLUME_ID,
                 String.valueOf(sfNewVolumeId),
                 false);
 
-        _snapshotDetailsDao.persist(snapshotDetail);
+        snapshotDetailsDao.persist(snapshotDetail);
 
         snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
                 SolidFireUtil.STORAGE_POOL_ID,
                 String.valueOf(storagePoolId),
                 false);
 
-        _snapshotDetailsDao.persist(snapshotDetail);
+        snapshotDetailsDao.persist(snapshotDetail);
 
         snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
                 SolidFireUtil.VOLUME_SIZE,
                 String.valueOf(sfNewVolumeSize),
                 false);
 
-        _snapshotDetailsDao.persist(snapshotDetail);
+        snapshotDetailsDao.persist(snapshotDetail);
 
         snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
                 DiskTO.IQN,
                 sfNewVolumeIqn,
                 false);
 
-        _snapshotDetailsDao.persist(snapshotDetail);
+        snapshotDetailsDao.persist(snapshotDetail);
     }
 
-    // return null for no error message
-    private String deleteSnapshot(SnapshotInfo snapshotInfo, long storagePoolId) {
-        String errMsg = null;
+    private String createVolume(VolumeInfo volumeInfo, long storagePoolId) {
+        verifySufficientBytesForStoragePool(volumeInfo, storagePoolId);
+        verifySufficientIopsForStoragePool(volumeInfo.getMinIops() != null ? volumeInfo.getMinIops() : getDefaultMinIops(storagePoolId), storagePoolId);
+
+        SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
+
+        long sfAccountId = getCreateSolidFireAccountId(sfConnection, volumeInfo.getAccountId(), storagePoolId);
+
+        long csSnapshotId = getCsIdForCloning(volumeInfo.getId(), "cloneOfSnapshot");
+        long csTemplateId = getCsIdForCloning(volumeInfo.getId(), "cloneOfTemplate");
+
+        SolidFireUtil.SolidFireVolume sfVolume;
+
+        if (csSnapshotId > 0) {
+            // We are supposed to create a clone of the underlying volume or snapshot that supports the CloudStack snapshot.
+            sfVolume = createClone(sfConnection, csSnapshotId, volumeInfo, sfAccountId, storagePoolId, DataObjectType.SNAPSHOT);
+        } else if (csTemplateId > 0) {
+            // Clone from template.
+            sfVolume = createClone(sfConnection, csTemplateId, volumeInfo, sfAccountId, storagePoolId, DataObjectType.TEMPLATE);
+
+            long volumeSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, storagePoolDao.findById(storagePoolId));
+
+            if (volumeSize > sfVolume.getTotalSize()) {
+                // Expand the volume to include HSR.
+                SolidFireUtil.modifySolidFireVolume(sfConnection, sfVolume.getId(), volumeSize, getVolumeAttributes(volumeInfo),
+                        sfVolume.getMinIops(), sfVolume.getMaxIops(), sfVolume.getBurstIops());
+
+                // Get the SolidFire volume from the SAN again because we just updated its size.
+                sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolume.getId());
+            }
+        }
+        else {
+            sfVolume = createSolidFireVolume(sfConnection, volumeInfo, sfAccountId);
+        }
+
+        String iqn = sfVolume.getIqn();
+
+        VolumeVO volume = volumeDao.findById(volumeInfo.getId());
+
+        volume.set_iScsiName(iqn);
+        volume.setFolder(String.valueOf(sfVolume.getId()));
+        volume.setPoolType(StoragePoolType.IscsiLUN);
+        volume.setPoolId(storagePoolId);
+
+        volumeDao.update(volume.getId(), volume);
+
+        updateVolumeDetails(volume.getId(), sfVolume.getTotalSize());
+
+        StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+
+        long capacityBytes = storagePool.getCapacityBytes();
+        // 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);
+
+        storagePoolDao.update(storagePoolId, storagePool);
+
+        return iqn;
+    }
 
-        long snapshotId = snapshotInfo.getId();
+    private void createTempVolume(SnapshotInfo snapshotInfo, long storagePoolId) {
+        long csSnapshotId = snapshotInfo.getId();
 
+        SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID);
+
+        if (snapshotDetails == null || snapshotDetails.getValue() == null) {
+            throw new CloudRuntimeException("'createTempVolume(SnapshotInfo, long)' should not be invoked unless " + SolidFireUtil.SNAPSHOT_ID + " exists.");
+        }
+
+        SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
+
+        snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, "tempVolume");
+
+        if (snapshotDetails != null && snapshotDetails.getValue() != null && snapshotDetails.getValue().equalsIgnoreCase("create")) {
+            long sfAccountId = getCreateSolidFireAccountId(sfConnection, snapshotInfo.getAccountId(), storagePoolId);
+
+            SolidFireUtil.SolidFireVolume sfVolume = createCloneFromSnapshot(sfConnection, csSnapshotId, sfAccountId);
+
+            addTempVolumeId(csSnapshotId, String.valueOf(sfVolume.getId()));
+
+            handleSnapshotDetails(csSnapshotId, DiskTO.IQN, sfVolume.getIqn());
+        }
+        else if (snapshotDetails != null && snapshotDetails.getValue() != null && snapshotDetails.getValue().equalsIgnoreCase("delete")) {
+            snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID);
+
+            SolidFireUtil.deleteSolidFireVolume(sfConnection, Long.parseLong(snapshotDetails.getValue()));
+
+            removeTempVolumeId(csSnapshotId);
+
+            snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, DiskTO.IQN);
+
+            snapshotDetailsDao.remove(snapshotDetails.getId());
+        }
+        else {
+            throw new CloudRuntimeException("Invalid state in 'createTempVolume(SnapshotInfo, long)'");
+        }
+    }
+
+    private String createTemplateVolume(TemplateInfo templateInfo, long storagePoolId) {
+        verifySufficientBytesForStoragePool(templateInfo, storagePoolId);
+
+        SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
+
+        long sfAccountId = getCreateSolidFireAccountId(sfConnection, templateInfo.getAccountId(), storagePoolId);
+
+        SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(sfConnection, templateInfo, sfAccountId);
+
+        String iqn = sfVolume.getIqn();
+
+        VMTemplateStoragePoolVO templatePoolRef = tmpltPoolDao.findByPoolTemplate(storagePoolId, templateInfo.getId());
+
+        templatePoolRef.setInstallPath(iqn);
+        templatePoolRef.setLocalDownloadPath(Long.toString(sfVolume.getId()));
+        templatePoolRef.setTemplateSize(sfVolume.getTotalSize());
+
+        tmpltPoolDao.update(templatePoolRef.getId(), templatePoolRef);
+
+        StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+
+        long capacityBytes = storagePool.getCapacityBytes();
+        // getUsedBytes(StoragePool) will include the bytes of the newly created template volume because
+        // _tmpltPoolDao.update(Long, VMTemplateStoragePoolVO) has already been invoked
+        long usedBytes = getUsedBytes(storagePool);
+
+        storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes);
+
+        storagePoolDao.update(storagePoolId, storagePool);
+
+        return iqn;
+    }
+
+    private void deleteVolume(VolumeInfo volumeInfo, long storagePoolId) {
         try {
-            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+            long volumeId = volumeInfo.getId();
+
+            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
 
-            SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, SolidFireUtil.VOLUME_ID);
+            deleteSolidFireVolume(sfConnection, volumeInfo);
 
-            long volumeId = Long.parseLong(snapshotDetails.getValue());
+            volumeDetailsDao.removeDetails(volumeId);
 
-            SolidFireUtil.deleteSolidFireVolume(sfConnection, volumeId);
+            StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
 
-            _snapshotDetailsDao.removeDetails(snapshotId);
+            long usedBytes = getUsedBytes(storagePool, volumeId);
 
-            StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
+            storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);
+
+            storagePoolDao.update(storagePoolId, storagePool);
+        }
+        catch (Exception ex) {
+            LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume. CloudStack volume ID: " + volumeInfo.getId(), ex);
+
+            throw ex;
+        }
+    }
+
+    private void deleteSnapshot(SnapshotInfo snapshotInfo, long storagePoolId) {
+        long csSnapshotId = snapshotInfo.getId();
+
+        try {
+            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
+
+            SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID);
+
+            if (snapshotDetails != null && snapshotDetails.getValue() != null) {
+                // A SolidFire snapshot is being used to support the CloudStack volume snapshot.
+
+                long sfSnapshotId = Long.parseLong(snapshotDetails.getValue());
+
+                deleteSolidFireSnapshot(sfConnection, csSnapshotId, sfSnapshotId);
+            }
+            else {
+                // A SolidFire volume is being used to support the CloudStack volume snapshot.
+
+                snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID);
+
+                long sfVolumeId = Long.parseLong(snapshotDetails.getValue());
+
+                SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId);
+            }
+
+            snapshotDetailsDao.removeDetails(csSnapshotId);
+
+            StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
 
             // getUsedBytes(StoragePool) will not include the snapshot to delete because it has already been deleted by this point
             long usedBytes = getUsedBytes(storagePool);
 
             storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);
 
-            _storagePoolDao.update(storagePoolId, storagePool);
+            storagePoolDao.update(storagePoolId, storagePool);
         }
         catch (Exception ex) {
-            s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume. CloudStack snapshot ID: " + snapshotId, ex);
+            LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Issue in 'deleteSnapshot(SnapshotInfo, long)'. CloudStack snapshot ID: " + csSnapshotId, ex);
 
-            errMsg = ex.getMessage();
+            throw ex;
+        }
+    }
+
+    private void deleteTemplate(TemplateInfo template, long storagePoolId) {
+        try {
+            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
+
+            long sfTemplateVolumeId = getVolumeIdFrom_iScsiPath(template.getInstallPath());
+
+            SolidFireUtil.deleteSolidFireVolume(sfConnection, sfTemplateVolumeId);
+
+            VMTemplateStoragePoolVO templatePoolRef = tmpltPoolDao.findByPoolTemplate(storagePoolId, template.getId());
+
+            tmpltPoolDao.remove(templatePoolRef.getId());
+
+            StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+
+            // getUsedBytes(StoragePool) will not include the template to delete because the "template_spool_ref" table has already been updated by this point
+            long usedBytes = getUsedBytes(storagePool);
+
+            storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);
+
+            storagePoolDao.update(storagePoolId, storagePool);
         }
+        catch (Exception ex) {
+            LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire template volume. CloudStack template ID: " + template.getId(), ex);
 
-        return errMsg;
+            throw ex;
+        }
     }
 
     @Override
@@ -720,26 +1196,49 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
             long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
             ResizeVolumePayload payload = (ResizeVolumePayload)volumeInfo.getpayload();
 
-            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+            SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao);
             SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId);
 
             verifySufficientIopsForStoragePool(storagePoolId, volumeInfo.getId(), payload.newMinIops);
+            verifySufficientBytesForStoragePool(storagePoolId, volumeInfo.getId(), payload.newSize, payload.newHypervisorSnapshotReserve);
+
+            long sfNewVolumeSize = sfVolume.getTotalSize();
+
+            Integer hsr = volumeInfo.getHypervisorSnapshotReserve();
+
+            if (payload.newSize != null || payload.newHypervisorSnapshotReserve != null) {
+                if (payload.newHypervisorSnapshotReserve != null) {
+                    if (hsr != null) {
+                        if (payload.newHypervisorSnapshotReserve > hsr) {
+                            hsr = payload.newHypervisorSnapshotReserve;
+                        }
+                    }
+                    else {
+                        hsr = payload.newHypervisorSnapshotReserve;
+                    }
+                }
+
+                sfNewVolumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(payload.newSize, hsr);
+            }
+
+            Map<String, String> mapAttributes = new HashMap<>();
+
+            mapAttributes.put(SolidFireUtil.CloudStackVolumeId, String.valueOf(volumeInfo.getId()));
+            mapAttributes.put(SolidFireUtil.CloudStackVolumeSize, NumberFormat.getInstance().format(payload.newSize));
 
-            SolidFireUtil.modifySolidFireVolume(sfConnection, sfVolumeId, sfVolume.getTotalSize(), NumberFormat.getInstance().format(payload.newSize),
+            SolidFireUtil.modifySolidFireVolume(sfConnection, sfVolumeId, sfNewVolumeSize, mapAttributes,
                     payload.newMinIops, payload.newMaxIops, getDefaultBurstIops(storagePoolId, payload.newMaxIops));
 
-            VolumeVO volume = _volumeDao.findById(volumeInfo.getId());
+            VolumeVO volume = volumeDao.findById(volumeInfo.getId());
 
             volume.setMinIops(payload.newMinIops);
             volume.setMaxIops(payload.newMaxIops);
+            volume.setHypervisorSnapshotReserve(hsr);
 
-            _volumeDao.update(volume.getId(), volume);
+            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());
+            updateVolumeDetails(volume.getId(), sfNewVolumeSize);
         } else {
             errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to resize";
         }
@@ -751,21 +1250,140 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
         callback.complete(result);
     }
 
+    private void verifySufficientBytesForStoragePool(long requestedBytes, long storagePoolId) {
+        StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+
+        long capacityBytes = storagePool.getCapacityBytes();
+        long usedBytes = getUsedBytes(storagePool);
+
+        usedBytes += requestedBytes;
+
+        if (usedBytes > capacityBytes) {
+            throw new CloudRuntimeException("Insufficient amount of space remains in this primary storage");
+        }
+    }
+
+    private void verifySufficientBytesForStoragePool(DataObject dataObject, long storagePoolId) {
+        StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+
+        long requestedBytes = getDataObjectSizeIncludingHypervisorSnapshotReserve(dataObject, storagePool);
+
+        verifySufficientBytesForStoragePool(requestedBytes, storagePoolId);
+    }
+
+    private void verifySufficientBytesForStoragePool(long storagePoolId, long volumeId, long newSize, Integer newHypervisorSnapshotReserve) {
+        DataStore primaryDataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
+        VolumeInfo volumeInfo = volumeFactory.getVolume(volumeId, primaryDataStore);
+        StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+        long currentSizeWithHsr = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, storagePool);
+
+        newHypervisorSnapshotReserve = newHypervisorSnapshotReserve == null ? LOWEST_HYPERVISOR_SNAPSHOT_RESERVE :
+                Math.max(newHypervisorSnapshotReserve, LOWEST_HYPERVISOR_SNAPSHOT_RESERVE);
+
+        long newSizeWithHsr = (long)(newSize + newSize * (newHypervisorSnapshotReserve / 100f));
+
+        if (newSizeWithHsr < currentSizeWithHsr) {
+            throw new CloudRuntimeException("Storage pool " + storagePoolId + " does not support shrinking a volume.");
+        }
+
+        long availableBytes = storagePool.getCapacityBytes() - getUsedBytes(storagePool);
+
+        if ((newSizeWithHsr - currentSizeWithHsr) > availableBytes) {
+            throw new CloudRuntimeException("Storage pool " + storagePoolId + " does not have enough space to expand the volume.");
+        }
+    }
+
+    private void verifySufficientIopsForStoragePool(long requestedIops, long storagePoolId) {
+        StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
+
+        long usedIops = getUsedIops(storagePool);
+        long capacityIops = storagePool.getCapacityIops();
+
+        if (usedIops + requestedIops > capacityIops) {
+            throw new CloudRuntimeException("Insufficient number of IOPS available in this storage pool");
+        }
+    }
+
     private void verifySufficientIopsForStoragePool(long storagePoolId, long volumeId, long newMinIops) {
-        StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
-        VolumeVO volume = _volumeDao.findById(volumeId);
+        VolumeVO volume = volumeDao.findById(volumeId);
 
         long currentMinIops = volume.getMinIops();
         long diffInMinIops = newMinIops - currentMinIops;
 
         // if the desire is for more IOPS
         if (diffInMinIops > 0) {
-            long usedIops = getUsedIops(storagePool);
-            long capacityIops = storagePool.getCapacityIops();
+            verifySufficientIopsForStoragePool(diffInMinIops, storagePoolId);
+        }
+    }
+
+    private void deleteSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, long csVolumeId, long sfVolumeId) {
+        List<SnapshotVO> lstSnapshots = getNonDestroyedSnapshots(csVolumeId);
+
+        boolean deleteVolume = true;
+
+        for (SnapshotVO snapshot : lstSnapshots) {
+            SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.SNAPSHOT_ID);
 
-            if (usedIops + diffInMinIops > capacityIops) {
-                throw new CloudRuntimeException("Insufficient number of IOPS available in this storage pool");
+            if (snapshotDetails != null && snapshotDetails.getValue() != null) {
+                deleteVolume = false;
+
+                break;
             }
         }
+
+        if (deleteVolume) {
+            SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId);
+        }
+    }
+
+    private void deleteSolidFireSnapshot(SolidFireUtil.SolidFireConnection sfConnection, long csSnapshotId, long sfSnapshotId) {
+        SolidFireUtil.deleteSolidFireSnapshot(sfConnection, sfSnapshotId);
+
+        SnapshotVO snapshot = snapshotDao.findById(csSnapshotId);
+        VolumeVO volume = volumeDao.findById(snapshot.getVolumeId());
+
+        if (volume == null) { // if the CloudStack volume has been deleted
+            List<SnapshotVO> lstSnapshots = getNonDestroyedSnapshots(snapshot.getVolumeId());
+
+            List<SnapshotVO> lstSnapshots2 = new ArrayList<>();
+
+            for (SnapshotVO snapshotVo : lstSnapshots) {
+                // The CloudStack volume snapshot has not yet been set to the DESTROYED state, so check to make
+                // sure snapshotVo.getId() != csSnapshotId when determining if any volume snapshots remain for the given CloudStack volume.
+                if (snapshotVo.getId() != csSnapshotId) {
+                    SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotVo.getId(), SolidFireUtil.SNAPSHOT_ID);
+
+                    // We are only interested here in volume snapshots that make use of SolidFire snapshots (as opposed to ones
+                    // that make use of SolidFire volumes).
+                    if (snapshotDetails != null && snapshotDetails.getValue() != null) {
+                        lstSnapshots2.add(snapshotVo);
+                    }
+                }
+            }
+
+            if (lstSnapshots2.isEmpty()) {
+                volume = volumeDao.findByIdIncludingRemoved(snapshot.getVolumeId());
+
+                SolidFireUtil.deleteSolidFireVolume(sfConnection, Long.parseLong(volume.getFolder()));
+            }
+        }
+    }
+
+    private List<SnapshotVO> getNonDestroyedSnapshots(long csVolumeId) {
+        List<SnapshotVO> lstSnapshots = snapshotDao.listByVolumeId(csVolumeId);
+
+        if (lstSnapshots == null) {
+            lstSnapshots = new ArrayList<>();
+        }
+
+        List<SnapshotVO> lstSnapshots2 = new ArrayList<>();
+
+        for (SnapshotVO snapshot : lstSnapshots) {
+            if (!State.Destroyed.equals(snapshot.getState())) {
+                lstSnapshots2.add(snapshot);
+            }
+        }
+
+        return lstSnapshots2;
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
----------------------------------------------------------------------
diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
index f89c97a..c47411e 100644
--- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
+++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java
@@ -33,6 +33,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCy
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
 import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+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.volume.datastore.PrimaryDataStoreHelper;
@@ -49,23 +50,27 @@ import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.dao.SnapshotDao;
 import com.cloud.storage.dao.SnapshotDetailsDao;
 import com.cloud.storage.dao.SnapshotDetailsVO;
+import com.cloud.storage.dao.VMTemplatePoolDao;
 import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePoolAutomation;
+import com.cloud.storage.VMTemplateStoragePoolVO;
 import com.cloud.utils.exception.CloudRuntimeException;
 
 public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle {
     private static final Logger s_logger = Logger.getLogger(SolidFirePrimaryDataStoreLifeCycle.class);
 
     @Inject private CapacityManager _capacityMgr;
-    @Inject private DataCenterDao zoneDao;
-    @Inject private PrimaryDataStoreDao storagePoolDao;
-    @Inject private PrimaryDataStoreHelper dataStoreHelper;
+    @Inject private DataCenterDao _zoneDao;
+    @Inject private PrimaryDataStoreDao _storagePoolDao;
+    @Inject private PrimaryDataStoreHelper _dataStoreHelper;
     @Inject private ResourceManager _resourceMgr;
     @Inject private SnapshotDao _snapshotDao;
     @Inject private SnapshotDetailsDao _snapshotDetailsDao;
     @Inject private StorageManager _storageMgr;
-    @Inject private StoragePoolAutomation storagePoolAutomation;
+    @Inject private StoragePoolAutomation _storagePoolAutomation;
+    @Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
+    @Inject private VMTemplatePoolDao _tmpltPoolDao;
 
     // invoked to add primary storage that is based on the SolidFire plug-in
     @Override
@@ -83,7 +88,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
         String storageVip = SolidFireUtil.getStorageVip(url);
         int storagePort = SolidFireUtil.getStoragePort(url);
 
-        DataCenterVO zone = zoneDao.findById(zoneId);
+        DataCenterVO zone = _zoneDao.findById(zoneId);
 
         String uuid = SolidFireUtil.PROVIDER_NAME + "_" + zone.getUuid() + "_" + storageVip;
 
@@ -179,7 +184,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
         details.put(SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS, String.valueOf(fClusterDefaultBurstIopsPercentOfMaxIops));
 
         // this adds a row in the cloud.storage_pool table for this SolidFire cluster
-        return dataStoreHelper.createPrimaryDataStore(parameters);
+        return _dataStoreHelper.createPrimaryDataStore(parameters);
     }
 
     // do not implement this method for SolidFire's plug-in
@@ -196,7 +201,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
 
     @Override
     public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
-        dataStoreHelper.attachZone(dataStore);
+        _dataStoreHelper.attachZone(dataStore);
 
         List<HostVO> xenServerHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.XenServer, scope.getScopeId());
         List<HostVO> vmWareServerHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.VMware, scope.getScopeId());
@@ -220,23 +225,25 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
 
     @Override
     public boolean maintain(DataStore dataStore) {
-        storagePoolAutomation.maintain(dataStore);
-        dataStoreHelper.maintain(dataStore);
+        _storagePoolAutomation.maintain(dataStore);
+        _dataStoreHelper.maintain(dataStore);
 
         return true;
     }
 
     @Override
     public boolean cancelMaintain(DataStore store) {
-        dataStoreHelper.cancelMaintain(store);
-        storagePoolAutomation.cancelMaintain(store);
+        _dataStoreHelper.cancelMaintain(store);
+        _storagePoolAutomation.cancelMaintain(store);
 
         return true;
     }
 
     // invoked to delete primary storage that is based on the SolidFire plug-in
     @Override
-    public boolean deleteDataStore(DataStore store) {
+    public boolean deleteDataStore(DataStore dataStore) {
+        long storagePoolId = dataStore.getId();
+
         List<SnapshotVO> lstSnapshots = _snapshotDao.listAll();
 
         if (lstSnapshots != null) {
@@ -244,13 +251,39 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
                 SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID);
 
                 // if this snapshot belongs to the storagePool that was passed in
-                if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == store.getId()) {
+                if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePoolId) {
                     throw new CloudRuntimeException("This primary storage cannot be deleted because it currently contains one or more snapshots.");
                 }
             }
         }
 
-        return dataStoreHelper.deletePrimaryDataStore(store);
+        List<VMTemplateStoragePoolVO> lstTemplatePoolRefs = _tmpltPoolDao.listByPoolId(storagePoolId);
+
+        if (lstTemplatePoolRefs != null) {
+            for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) {
+                try {
+                    SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
+                    long sfTemplateVolumeId = Long.parseLong(templatePoolRef.getLocalDownloadPath());
+
+                    SolidFireUtil.deleteSolidFireVolume(sfConnection, sfTemplateVolumeId);
+                }
+                catch (Exception ex) {
+                    s_logger.error(ex.getMessage() != null ? ex.getMessage() : "Error deleting SolidFire template volume");
+                }
+
+                _tmpltPoolDao.remove(templatePoolRef.getId());
+            }
+        }
+
+        StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
+
+        storagePool.setUsedBytes(0);
+
+        _storagePoolDao.update(storagePoolId, storagePool);
+
+        _storagePoolDetailsDao.removeDetails(storagePoolId);
+
+        return _dataStoreHelper.deletePrimaryDataStore(dataStore);
     }
 
     /* (non-Javadoc)
@@ -263,7 +296,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
 
     @Override
     public void updateStoragePool(StoragePool storagePool, Map<String, String> details) {
-        StoragePoolVO storagePoolVo = storagePoolDao.findById(storagePool.getId());
+        StoragePoolVO storagePoolVo = _storagePoolDao.findById(storagePool.getId());
 
         String strCapacityBytes = details.get(PrimaryDataStoreLifeCycle.CAPACITY_BYTES);
         Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null;
@@ -290,11 +323,11 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC
 
     @Override
     public void enableStoragePool(DataStore dataStore) {
-        dataStoreHelper.enable(dataStore);
+        _dataStoreHelper.enable(dataStore);
     }
 
     @Override
     public void disableStoragePool(DataStore dataStore) {
-        dataStoreHelper.disable(dataStore);
+        _dataStoreHelper.disable(dataStore);
     }
 }


Mime
View raw message