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 2015/01/20 23:38:32 UTC

git commit: updated refs/heads/master to 0f84e04

Repository: cloudstack
Updated Branches:
  refs/heads/master 240e8ef8a -> 0f84e042b


Adding support for creating a volume from a snapshot when the snapshot is on managed storage


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/0f84e042
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/0f84e042
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/0f84e042

Branch: refs/heads/master
Commit: 0f84e042b99e58e3d2293c0a988e1baa019c8c56
Parents: 240e8ef
Author: Mike Tutkowski <mi...@solidfire.com>
Authored: Wed Jan 7 17:21:52 2015 -0700
Committer: Mike Tutkowski <mi...@solidfire.com>
Committed: Tue Jan 20 15:24:33 2015 -0700

----------------------------------------------------------------------
 .../cloudstack/storage/command/CopyCommand.java |  13 +-
 .../service/VolumeOrchestrationService.java     |   1 +
 .../orchestration/VolumeOrchestrator.java       |  54 +++-
 .../src/com/cloud/storage/SnapshotVO.java       |  18 +-
 .../schema/src/com/cloud/storage/VolumeVO.java  |   3 +-
 .../motion/StorageSystemDataMotionStrategy.java | 279 +++++++++++++++----
 .../cloudstack/storage/volume/VolumeObject.java |   4 -
 .../resource/XenServerStorageProcessor.java     |  49 ++++
 .../resource/Xenserver625StorageProcessor.java  |   6 +-
 server/src/com/cloud/api/ApiResponseHelper.java |   8 +-
 .../com/cloud/storage/VolumeApiServiceImpl.java |   3 +
 .../storage/snapshot/SnapshotManagerImpl.java   |   8 +-
 .../com/cloud/template/TemplateManagerImpl.java |  29 +-
 setup/db/db/schema-450to460.sql                 |   3 +
 14 files changed, 378 insertions(+), 100 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/core/src/org/apache/cloudstack/storage/command/CopyCommand.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java
index 08b4b51..cfede14 100644
--- a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java
+++ b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java
@@ -29,8 +29,9 @@ public final class CopyCommand extends Command implements StorageSubSystemComman
     private DataTO srcTO;
     private DataTO destTO;
     private DataTO cacheTO;
-    boolean executeInSequence = false;
-    Map<String, String> options = new HashMap<String, String>();
+    private boolean executeInSequence = false;
+    private Map<String, String> options = new HashMap<String, String>();
+    private Map<String, String> options2 = new HashMap<String, String>();
 
     public CopyCommand(DataTO srcData, DataTO destData, int timeout, boolean executeInSequence) {
         super();
@@ -81,6 +82,14 @@ public final class CopyCommand extends Command implements StorageSubSystemComman
         return options;
     }
 
+    public void setOptions2(Map<String, String> options2) {
+        this.options2 = options2;
+    }
+
+    public Map<String, String> getOptions2() {
+        return options2;
+    }
+
     @Override
     public void setExecuteInSequence(boolean inSeq) {
         this.executeInSequence = inSeq;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
index 1f198a2..497db3d 100644
--- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
@@ -125,4 +125,5 @@ public interface VolumeOrchestrationService {
 
     void updateVolumeDiskChain(long volumeId, String path, String chainInfo);
 
+    VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 396a49c..ab90fa7 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer
 import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
 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.DataStoreDriver;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
@@ -59,6 +60,7 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
 import org.apache.cloudstack.storage.command.CommandResult;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
@@ -374,16 +376,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
 
         VolumeInfo vol = volFactory.getVolume(volume.getId());
         DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
-        SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
-        // sync snapshot to region store if necessary
-        DataStore snapStore = snapInfo.getDataStore();
-        long snapVolId = snapInfo.getVolumeId();
-        try {
-            _snapshotSrv.syncVolumeSnapshotsToRegionStore(snapVolId, snapStore);
-        } catch (Exception ex) {
-            // log but ignore the sync error to avoid any potential S3 down issue, it should be sync next time
-            s_logger.warn(ex.getMessage(), ex);
+        DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
+        SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), dataStoreRole);
+
+        // don't try to perform a sync if the DataStoreRole of the snapshot is equal to DataStoreRole.Primary
+        if (!DataStoreRole.Primary.equals(dataStoreRole)) {
+            try {
+                // sync snapshot to region store if necessary
+                DataStore snapStore = snapInfo.getDataStore();
+                long snapVolId = snapInfo.getVolumeId();
+
+                _snapshotSrv.syncVolumeSnapshotsToRegionStore(snapVolId, snapStore);
+            } catch (Exception ex) {
+                // log but ignore the sync error to avoid any potential S3 down issue, it should be sync next time
+                s_logger.warn(ex.getMessage(), ex);
+            }
         }
+
         // create volume on primary from snapshot
         AsyncCallFuture<VolumeApiResult> future = volService.createVolumeFromSnapshot(vol, store, snapInfo);
         try {
@@ -403,6 +412,30 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
 
     }
 
+    public DataStoreRole getDataStoreRole(Snapshot snapshot) {
+        SnapshotDataStoreVO snapshotStore = _snapshotDataStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
+
+        if (snapshotStore == null) {
+            return DataStoreRole.Image;
+        }
+
+        long storagePoolId = snapshotStore.getDataStoreId();
+        DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
+
+        Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
+
+        if (mapCapabilities != null) {
+            String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
+            Boolean supportsStorageSystemSnapshots = new Boolean(value);
+
+            if (supportsStorageSystemSnapshots) {
+                return DataStoreRole.Primary;
+            }
+        }
+
+        return DataStoreRole.Image;
+    }
+
     protected DiskProfile createDiskCharacteristics(VolumeInfo volume, VirtualMachineTemplate template, DataCenter dc, DiskOffering diskOffering) {
         if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) {
             TemplateDataStoreVO ss = _vmTemplateStoreDao.findByTemplateZoneDownloadStatus(template.getId(), dc.getId(), VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
@@ -517,7 +550,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
     // The disk offering can collect this information and pass it on to the volume that's about to be created.
     // Ex. if you want a 10 GB CloudStack volume to reside on managed storage on Xen, this leads to an SR
     // that is a total size of (10 GB * (hypervisorSnapshotReserveSpace / 100) + 10 GB).
-    private VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
+    @Override
+    public VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
         Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve();
 
         if (hyperType == HypervisorType.KVM) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/schema/src/com/cloud/storage/SnapshotVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/SnapshotVO.java b/engine/schema/src/com/cloud/storage/SnapshotVO.java
index dae049f..950c5e9 100644
--- a/engine/schema/src/com/cloud/storage/SnapshotVO.java
+++ b/engine/schema/src/com/cloud/storage/SnapshotVO.java
@@ -92,12 +92,18 @@ public class SnapshotVO implements Snapshot {
     @Column(name = "uuid")
     String uuid;
 
+    @Column(name = "min_iops")
+    Long minIops;
+
+    @Column(name = "max_iops")
+    Long maxIops;
+
     public SnapshotVO() {
         uuid = UUID.randomUUID().toString();
     }
 
     public SnapshotVO(long dcId, long accountId, long domainId, Long volumeId, Long diskOfferingId, String name, short snapshotType, String typeDescription, long size,
-            HypervisorType hypervisorType) {
+            Long minIops, Long maxIops, HypervisorType hypervisorType) {
         dataCenterId = dcId;
         this.accountId = accountId;
         this.domainId = domainId;
@@ -107,6 +113,8 @@ public class SnapshotVO implements Snapshot {
         this.snapshotType = snapshotType;
         this.typeDescription = typeDescription;
         this.size = size;
+        this.minIops = minIops;
+        this.maxIops = maxIops;
         state = State.Allocated;
         this.hypervisorType = hypervisorType;
         version = "2.2";
@@ -184,6 +192,14 @@ public class SnapshotVO implements Snapshot {
         return size;
     }
 
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
     public String getTypeDescription() {
         return typeDescription;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/schema/src/com/cloud/storage/VolumeVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java
index e2f717d..d8323e9 100644
--- a/engine/schema/src/com/cloud/storage/VolumeVO.java
+++ b/engine/schema/src/com/cloud/storage/VolumeVO.java
@@ -164,7 +164,7 @@ public class VolumeVO implements Volume {
     String reservationId;
 
     @Column(name = "hv_ss_reserve")
-    Integer hypervisorSnapshotReserve;
+    private Integer hypervisorSnapshotReserve;
 
     // Real Constructor
     public VolumeVO(Type type, String name, long dcId, long domainId,
@@ -628,7 +628,6 @@ public class VolumeVO implements Volume {
     @Override
     public Integer getHypervisorSnapshotReserve() {
         return hypervisorSnapshotReserve;
-
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
index cf9e9dc..525aa70 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
@@ -27,6 +27,8 @@ import javax.inject.Inject;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
+import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
+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.DataMotionStrategy;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
@@ -34,8 +36,13 @@ 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.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
+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.engine.subsystem.api.storage.VolumeService;
+import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
+import org.apache.cloudstack.framework.async.AsyncCallFuture;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 import org.apache.cloudstack.storage.command.CopyCommand;
@@ -56,8 +63,15 @@ import com.cloud.org.Grouping.AllocationState;
 import com.cloud.resource.ResourceState;
 import com.cloud.server.ManagementService;
 import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.dao.DiskOfferingDao;
+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.utils.NumbersUtil;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.VirtualMachineManager;
@@ -68,16 +82,30 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
     @Inject private AgentManager _agentMgr;
     @Inject private ConfigurationDao _configDao;
+    @Inject private DiskOfferingDao _diskOfferingDao;
     @Inject private HostDao _hostDao;
     @Inject private ManagementService _mgr;
     @Inject private PrimaryDataStoreDao _storagePoolDao;
+    @Inject private SnapshotDao _snapshotDao;
     @Inject private SnapshotDetailsDao _snapshotDetailsDao;
-    @Inject private VolumeService _volService;
+    @Inject private VolumeDao _volumeDao;
+    @Inject private VolumeDataFactory _volumeDataFactory;
+    @Inject private VolumeOrchestrationService _volumeMgr;
+    @Inject private VolumeService _volumeService;
 
     @Override
     public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
-        if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) {
-            DataStore dataStore = srcData.getDataStore();
+        if (srcData instanceof SnapshotInfo) {
+            if (canHandle(srcData.getDataStore()) || canHandle(destData.getDataStore())) {
+                return StrategyPriority.HIGHEST;
+            }
+        }
+
+        return StrategyPriority.CANT_HANDLE;
+    }
+
+    private boolean canHandle(DataStore dataStore) {
+        if (dataStore.getRole() == DataStoreRole.Primary) {
             Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
 
             if (mapCapabilities != null) {
@@ -87,12 +115,12 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
                 if (supportsStorageSystemSnapshots) {
                     s_logger.info("Using 'StorageSystemDataMotionStrategy'");
 
-                    return StrategyPriority.HIGHEST;
+                    return true;
                 }
             }
         }
 
-        return StrategyPriority.CANT_HANDLE;
+        return false;
     }
 
     @Override
@@ -102,75 +130,207 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
     @Override
     public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
-        if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) {
+        if (srcData instanceof SnapshotInfo) {
             SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
-            HostVO hostVO = getHost(srcData);
-            DataStore srcDataStore = srcData.getDataStore();
 
-            String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
-            int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
-            CopyCommand copyCommand = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
+            validate(snapshotInfo);
 
-            CopyCmdAnswer copyCmdAnswer = null;
+            boolean canHandleSrc = canHandle(srcData.getDataStore());
 
-            try {
-                _volService.grantAccess(snapshotInfo, hostVO, srcDataStore);
+            if (canHandleSrc && destData instanceof TemplateInfo &&
+                    (destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache)) {
+                return handleCreateTemplateFromSnapshot(snapshotInfo, (TemplateInfo)destData, callback);
+            }
 
-                Map<String, String> srcDetails = getSourceDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
+            if (destData instanceof VolumeInfo) {
+                VolumeInfo volumeInfo = (VolumeInfo)destData;
+
+                boolean canHandleDest = canHandle(destData.getDataStore());
+
+                if (canHandleSrc && canHandleDest) {
+                    return handleCreateVolumeFromSnapshotBothOnStorageSystem(snapshotInfo, volumeInfo, callback);
+                }
 
-                copyCommand.setOptions(srcDetails);
+                if (canHandleSrc) {
+                    // return handleCreateVolumeFromSnapshotOnlySourceOnStorageSystem();
+                }
 
-                copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
+                if (canHandleDest) {
+                    // return handleCreateVolumeFromSnapshotOnlyDestinationOnStorageSystem();
+                }
+            }
+        }
+
+        throw new UnsupportedOperationException("This operation is not supported.");
+    }
+
+    private void validate(SnapshotInfo snapshotInfo) {
+        long volumeId = snapshotInfo.getVolumeId();
+
+        VolumeVO volumeVO = _volumeDao.findByIdIncludingRemoved(volumeId);
+
+        if (volumeVO.getFormat() != ImageFormat.VHD) {
+            throw new CloudRuntimeException("Only the " + ImageFormat.VHD.toString() + " image type is currently supported.");
+        }
+    }
+
+    private Void handleCreateTemplateFromSnapshot(SnapshotInfo snapshotInfo, TemplateInfo templateInfo, AsyncCompletionCallback<CopyCommandResult> callback) {
+        HostVO hostVO = getHost(snapshotInfo.getDataStore().getId());
+        DataStore srcDataStore = snapshotInfo.getDataStore();
+
+        String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
+        int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
+        CopyCommand copyCommand = new CopyCommand(snapshotInfo.getTO(), templateInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
+
+        CopyCmdAnswer copyCmdAnswer = null;
+
+        try {
+            _volumeService.grantAccess(snapshotInfo, hostVO, srcDataStore);
+
+            Map<String, String> srcDetails = getSnapshotDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
+
+            copyCommand.setOptions(srcDetails);
+
+            copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
+        }
+        catch (Exception ex) {
+            throw new CloudRuntimeException(ex.getMessage());
+        }
+        finally {
+            try {
+                _volumeService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
             }
             catch (Exception ex) {
-                throw new CloudRuntimeException(ex.getMessage());
+                s_logger.debug(ex.getMessage(), ex);
             }
-            finally {
-                try {
-                    _volService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
-                }
-                catch (Exception ex) {
-                    s_logger.debug(ex.getMessage(), ex);
-                }
+        }
+
+        String errMsg = null;
+
+        if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
+            if (copyCmdAnswer != null && copyCmdAnswer.getDetails() != null && !copyCmdAnswer.getDetails().isEmpty()) {
+                errMsg = copyCmdAnswer.getDetails();
+            }
+            else {
+                errMsg = "Unable to perform host-side operation";
             }
+        }
 
-            String errMsg = null;
+        CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
 
-            if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
-                if (copyCmdAnswer != null && copyCmdAnswer.getDetails() != null && !copyCmdAnswer.getDetails().isEmpty()) {
-                    errMsg = copyCmdAnswer.getDetails();
-                }
-                else {
-                    errMsg = "Unable to perform host-side operation";
-                }
+        result.setResult(errMsg);
+
+        callback.complete(result);
+
+        return null;
+    }
+
+    private Void handleCreateVolumeFromSnapshotBothOnStorageSystem(SnapshotInfo snapshotInfo, VolumeInfo volumeInfo, AsyncCompletionCallback<CopyCommandResult> callback) {
+        try {
+            // at this point, the snapshotInfo and volumeInfo should have the same disk offering ID (so either one should be OK to get a DiskOfferingVO instance)
+            DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(volumeInfo.getDiskOfferingId());
+            SnapshotVO snapshot = _snapshotDao.findById(snapshotInfo.getId());
+
+            // update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage)
+            _volumeMgr.updateHypervisorSnapshotReserveForVolume(diskOffering, volumeInfo, snapshot.getHypervisorType());
+
+            AsyncCallFuture<VolumeApiResult> future = _volumeService.createVolumeAsync(volumeInfo, volumeInfo.getDataStore());
+
+            VolumeApiResult result = future.get();
+
+            if (result.isFailed()) {
+                s_logger.debug("Failed to create a volume: " + result.getResult());
+
+                throw new CloudRuntimeException(result.getResult());
+            }
+        }
+        catch (Exception ex) {
+            throw new CloudRuntimeException(ex.getMessage());
+        }
+
+        volumeInfo = _volumeDataFactory.getVolume(volumeInfo.getId(), volumeInfo.getDataStore());
+
+        volumeInfo.processEvent(Event.MigrationRequested);
+
+        volumeInfo = _volumeDataFactory.getVolume(volumeInfo.getId(), volumeInfo.getDataStore());
+
+        HostVO hostVO = getHost(snapshotInfo.getDataStore().getId());
+
+        String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
+        int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
+        CopyCommand copyCommand = new CopyCommand(snapshotInfo.getTO(), volumeInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
+
+        CopyCmdAnswer copyCmdAnswer = null;
+
+        try {
+            _volumeService.grantAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
+            _volumeService.grantAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
+
+            Map<String, String> srcDetails = getSnapshotDetails(_storagePoolDao.findById(snapshotInfo.getDataStore().getId()), snapshotInfo);
+
+            copyCommand.setOptions(srcDetails);
+
+            Map<String, String> destDetails = getVolumeDetails(volumeInfo);
+
+            copyCommand.setOptions2(destDetails);
+
+            copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
+        }
+        catch (Exception ex) {
+            throw new CloudRuntimeException(ex.getMessage());
+        }
+        finally {
+            try {
+                _volumeService.revokeAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
+            }
+            catch (Exception ex) {
+                s_logger.debug(ex.getMessage(), ex);
             }
 
-            CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
+            try {
+                _volumeService.revokeAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
+            }
+            catch (Exception ex) {
+                s_logger.debug(ex.getMessage(), ex);
+            }
+        }
 
-            result.setResult(errMsg);
+        String errMsg = null;
 
-            callback.complete(result);
+        if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
+            if (copyCmdAnswer != null && copyCmdAnswer.getDetails() != null && !copyCmdAnswer.getDetails().isEmpty()) {
+                errMsg = copyCmdAnswer.getDetails();
+            }
+            else {
+                errMsg = "Unable to perform host-side operation";
+            }
         }
 
+        CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
+
+        result.setResult(errMsg);
+
+        callback.complete(result);
+
         return null;
     }
 
-    private Map<String, String> getSourceDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
-        Map<String, String> srcDetails = new HashMap<String, String>();
+    private Map<String, String> getSnapshotDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
+        Map<String, String> details = new HashMap<String, String>();
 
-        srcDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
-        srcDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
+        details.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
+        details.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
 
         long snapshotId = snapshotInfo.getId();
 
-        srcDetails.put(DiskTO.IQN, getProperty(snapshotId, DiskTO.IQN));
+        details.put(DiskTO.IQN, getProperty(snapshotId, DiskTO.IQN));
 
-        srcDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
-        srcDetails.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
-        srcDetails.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME));
-        srcDetails.put(DiskTO.CHAP_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
+        details.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
+        details.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
+        details.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME));
+        details.put(DiskTO.CHAP_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
 
-        return srcDetails;
+        return details;
     }
 
     private String getProperty(long snapshotId, String property) {
@@ -183,8 +343,31 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
         return null;
     }
 
-    public HostVO getHost(DataObject srcData) {
-        long dataStoreId = srcData.getDataStore().getId();
+    private Map<String, String> getVolumeDetails(VolumeInfo volumeInfo) {
+        Map<String, String> sourceDetails = new HashMap<String, String>();
+
+        VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
+
+        long storagePoolId = volumeVO.getPoolId();
+        StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
+
+        sourceDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
+        sourceDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
+        sourceDetails.put(DiskTO.IQN, volumeVO.get_iScsiName());
+
+        ChapInfo chapInfo = _volumeService.getChapInfo(volumeInfo, volumeInfo.getDataStore());
+
+        if (chapInfo != null) {
+            sourceDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername());
+            sourceDetails.put(DiskTO.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret());
+            sourceDetails.put(DiskTO.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername());
+            sourceDetails.put(DiskTO.CHAP_TARGET_SECRET, chapInfo.getTargetSecret());
+        }
+
+        return sourceDetails;
+    }
+
+    public HostVO getHost(long dataStoreId) {
         StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStoreId);
 
         List<? extends Cluster> clusters = _mgr.searchForClusters(storagePoolVO.getDataCenterId(), new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
index 6a42435..1f574d5 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
@@ -154,10 +154,6 @@ public class VolumeObject implements VolumeInfo {
         return volumeVO.getMaxIops();
     }
 
-    public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
-        volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
-    }
-
     @Override
     public Integer getHypervisorSnapshotReserve() {
         return volumeVO.getHypervisorSnapshotReserve();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
index f261410..9d483d7 100644
--- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
+++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
@@ -1589,6 +1589,10 @@ public class XenServerStorageProcessor implements StorageProcessor {
         DataTO destData = cmd.getDestTO();
         DataStoreTO imageStore = srcData.getDataStore();
 
+        if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
+            return createVolumeFromSnapshot2(cmd);
+        }
+
         if (!(imageStore instanceof NfsTO)) {
             return new CopyCmdAnswer("unsupported protocol");
         }
@@ -1647,6 +1651,51 @@ public class XenServerStorageProcessor implements StorageProcessor {
         return new CopyCmdAnswer(details);
     }
 
+    protected Answer createVolumeFromSnapshot2(CopyCommand cmd) {
+        try {
+            Connection conn = hypervisorResource.getConnection();
+
+            Map<String, String> srcOptions = cmd.getOptions();
+
+            String src_iScsiName = srcOptions.get(DiskTO.IQN);
+            String srcStorageHost = srcOptions.get(DiskTO.STORAGE_HOST);
+            String srcChapInitiatorUsername = srcOptions.get(DiskTO.CHAP_INITIATOR_USERNAME);
+            String srcChapInitiatorSecret = srcOptions.get(DiskTO.CHAP_INITIATOR_SECRET);
+
+            SR srcSr = hypervisorResource.getIscsiSR(conn, src_iScsiName, srcStorageHost, src_iScsiName, srcChapInitiatorUsername, srcChapInitiatorSecret, false);
+
+            Map<String, String> destOptions = cmd.getOptions2();
+
+            String dest_iScsiName = destOptions.get(DiskTO.IQN);
+            String destStorageHost = destOptions.get(DiskTO.STORAGE_HOST);
+            String destChapInitiatorUsername = destOptions.get(DiskTO.CHAP_INITIATOR_USERNAME);
+            String destChapInitiatorSecret = destOptions.get(DiskTO.CHAP_INITIATOR_SECRET);
+
+            SR destSr = hypervisorResource.getIscsiSR(conn, dest_iScsiName, destStorageHost, dest_iScsiName, destChapInitiatorUsername, destChapInitiatorSecret, false);
+
+            // there should only be one VDI in this SR
+            VDI srcVdi = srcSr.getVDIs(conn).iterator().next();
+
+            VDI vdiCopy = srcVdi.copy(conn, destSr);
+
+            VolumeObjectTO newVol = new VolumeObjectTO();
+
+            newVol.setSize(vdiCopy.getVirtualSize(conn));
+            newVol.setPath(vdiCopy.getUuid(conn));
+            newVol.setFormat(ImageFormat.VHD);
+
+            hypervisorResource.removeSR(conn, srcSr);
+            hypervisorResource.removeSR(conn, destSr);
+
+            return new CopyCmdAnswer(newVol);
+        }
+        catch (Exception ex) {
+            s_logger.warn("Failed to copy snapshot to volume: " + ex.toString(), ex);
+
+            return new CopyCmdAnswer(ex.getMessage());
+        }
+    }
+
     @Override
     public Answer deleteSnapshot(DeleteCommand cmd) {
         SnapshotObjectTO snapshot = (SnapshotObjectTO) cmd.getData();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
index 0bb02c3..043514b 100644
--- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
+++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
@@ -656,7 +656,11 @@ public class Xenserver625StorageProcessor extends XenServerStorageProcessor {
         DataTO destData = cmd.getDestTO();
         PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore();
         VolumeObjectTO volume = (VolumeObjectTO)destData;
-                DataStoreTO imageStore = srcData.getDataStore();
+        DataStoreTO imageStore = srcData.getDataStore();
+
+        if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
+            return createVolumeFromSnapshot2(cmd);
+        }
 
         if (!(imageStore instanceof NfsTO)) {
             return new CopyCmdAnswer("unsupported protocol");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 4726c24..98a4a85 100644
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -483,7 +483,7 @@ public class ApiResponseHelper implements ResponseGenerator {
         if (snapshot instanceof SnapshotInfo) {
             snapshotInfo = (SnapshotInfo)snapshot;
         } else {
-            DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
+            DataStoreRole dataStoreRole = getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
 
             snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole);
         }
@@ -509,15 +509,15 @@ public class ApiResponseHelper implements ResponseGenerator {
         return snapshotResponse;
     }
 
-    private DataStoreRole getDataStoreRole(Snapshot snapshot) {
-        SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
+    public static DataStoreRole getDataStoreRole(Snapshot snapshot, SnapshotDataStoreDao snapshotStoreDao, DataStoreManager dataStoreMgr) {
+        SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
 
         if (snapshotStore == null) {
             return DataStoreRole.Image;
         }
 
         long storagePoolId = snapshotStore.getDataStoreId();
-        DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
+        DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
 
         Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
index ca83890..0abb629 100644
--- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
@@ -522,6 +522,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
             size = snapshotCheck.getSize(); // ; disk offering is used for tags
             // purposes
 
+            minIops = snapshotCheck.getMinIops();
+            maxIops = snapshotCheck.getMaxIops();
+
             provisioningType = diskOffering.getProvisioningType();
             // check snapshot permissions
             _accountMgr.checkAccess(caller, null, true, snapshotCheck);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
index 0591175..baa0488 100644
--- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
+++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
@@ -87,6 +87,7 @@ import com.cloud.storage.SnapshotPolicyVO;
 import com.cloud.storage.SnapshotScheduleVO;
 import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.Storage;
+import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateVO;
@@ -1132,13 +1133,18 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
         StoragePoolVO storagePool = _storagePoolDao.findById(volume.getDataStore().getId());
         if (storagePool.getScope() == ScopeType.ZONE) {
             hypervisorType = storagePool.getHypervisor();
+
+            // at the time being, managed storage only supports XenServer, ESX(i), and KVM (i.e. not Hyper-V), so the VHD file type can be mapped to XenServer
+            if (storagePool.isManaged() && HypervisorType.Any.equals(hypervisorType) && ImageFormat.VHD.equals(volume.getFormat())) {
+                hypervisorType = HypervisorType.XenServer;
+            }
         } else {
             hypervisorType = volume.getHypervisorType();
         }
 
         SnapshotVO snapshotVO =
             new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName,
-                (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType);
+                (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType);
 
         SnapshotVO snapshot = _snapshotDao.persist(snapshotVO);
         if (snapshot == null) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/template/TemplateManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index f244673..330b19e 100644
--- a/server/src/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/com/cloud/template/TemplateManagerImpl.java
@@ -54,7 +54,6 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissions
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 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.EndPoint;
 import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
@@ -81,7 +80,6 @@ import org.apache.cloudstack.storage.command.CommandResult;
 import org.apache.cloudstack.storage.command.DettachCommand;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
@@ -97,6 +95,7 @@ import com.cloud.agent.api.to.DataTO;
 import com.cloud.agent.api.to.DiskTO;
 import com.cloud.agent.api.to.NfsTO;
 import com.cloud.api.ApiDBUtils;
+import com.cloud.api.ApiResponseHelper;
 import com.cloud.api.query.dao.UserVmJoinDao;
 import com.cloud.api.query.vo.UserVmJoinVO;
 import com.cloud.configuration.Config;
@@ -1379,7 +1378,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
             AsyncCallFuture<TemplateApiResult> future = null;
 
             if (snapshotId != null) {
-                DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
+                DataStoreRole dataStoreRole = ApiResponseHelper.getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
 
                 SnapshotInfo snapInfo = _snapshotFactory.getSnapshot(snapshotId, dataStoreRole);
 
@@ -1474,30 +1473,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
         }
     }
 
-    private DataStoreRole getDataStoreRole(Snapshot snapshot) {
-        SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
-
-        if (snapshotStore == null) {
-            return DataStoreRole.Image;
-        }
-
-        long storagePoolId = snapshotStore.getDataStoreId();
-        DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
-
-        Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
-
-        if (mapCapabilities != null) {
-            String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
-            Boolean supportsStorageSystemSnapshots = new Boolean(value);
-
-            if (supportsStorageSystemSnapshots) {
-                return DataStoreRole.Primary;
-            }
-        }
-
-        return DataStoreRole.Image;
-    }
-
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true)
     public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/setup/db/db/schema-450to460.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-450to460.sql b/setup/db/db/schema-450to460.sql
index 0493184..95d6532 100644
--- a/setup/db/db/schema-450to460.sql
+++ b/setup/db/db/schema-450to460.sql
@@ -19,6 +19,9 @@
 -- Schema upgrade from 4.5.0 to 4.6.0
 --
 
+ALTER TABLE `cloud`.`snapshots` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
+ALTER TABLE `cloud`.`snapshots` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
+
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.uri", "", "URI to additionally send StatsCollector statistics to", "", NULL, NULL, 0);
 
 DROP VIEW IF EXISTS `cloud`.`domain_view`;