http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
 
b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
index df2d37f..0d96660 100644
--- 
a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
+++ 
b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
@@ -918,6 +918,25 @@ public class TemplateServiceImpl implements 
TemplateService {
         return copyAsync(srcTemplate, srcTemplate, (DataStore)pool);
     }
 
+    @Override
+    public AsyncCallFuture<TemplateApiResult> 
deleteTemplateOnPrimary(TemplateInfo template, StoragePool pool) {
+        TemplateObject templateObject = 
(TemplateObject)_templateFactory.getTemplate(template.getId(), (DataStore)pool);
+
+        
templateObject.processEvent(ObjectInDataStoreStateMachine.Event.DestroyRequested);
+
+        DataStore dataStore = _storeMgr.getPrimaryDataStore(pool.getId());
+
+        AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<>();
+        TemplateOpContext<TemplateApiResult> context = new 
TemplateOpContext<>(null, templateObject, future);
+        AsyncCallbackDispatcher<TemplateServiceImpl, CommandResult> caller = 
AsyncCallbackDispatcher.create(this);
+
+        caller.setCallback(caller.getTarget().deleteTemplateCallback(null, 
null)).setContext(context);
+
+        dataStore.getDriver().deleteAsync(dataStore, templateObject, caller);
+
+        return future;
+    }
+
     protected Void 
copyTemplateCallBack(AsyncCallbackDispatcher<TemplateServiceImpl, 
CopyCommandResult> callback, TemplateOpContext<TemplateApiResult> context) {
         TemplateInfo destTemplate = context.getTemplate();
         CopyCommandResult result = callback.getResult();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
 
b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
index 473959b..6e78f19 100644
--- 
a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ 
b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@ -28,7 +28,6 @@ import org.apache.log4j.Logger;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import 
org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
-import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.storage.command.CopyCmdAnswer;
 import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
@@ -54,6 +53,9 @@ import com.cloud.utils.component.ComponentContext;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.fsm.NoTransitionException;
 
+import com.google.common.base.Strings;
+
+@SuppressWarnings("serial")
 public class TemplateObject implements TemplateInfo {
     private static final Logger s_logger = 
Logger.getLogger(TemplateObject.class);
     private VMTemplateVO imageVO;
@@ -189,12 +191,15 @@ public class TemplateObject implements TemplateInfo {
                     TemplateObjectTO newTemplate = 
(TemplateObjectTO)cpyAnswer.getNewData();
                     VMTemplateStoragePoolVO templatePoolRef = 
templatePoolDao.findByPoolTemplate(getDataStore().getId(), getId());
                     templatePoolRef.setDownloadPercent(100);
-                    if (newTemplate.getSize() != null) {
-                        templatePoolRef.setTemplateSize(newTemplate.getSize());
-                    }
+
+                    setTemplateSizeIfNeeded(newTemplate, templatePoolRef);
+
                     templatePoolRef.setDownloadState(Status.DOWNLOADED);
-                    
templatePoolRef.setLocalDownloadPath(newTemplate.getPath());
-                    templatePoolRef.setInstallPath(newTemplate.getPath());
+
+                    setDownloadPathIfNeeded(newTemplate, templatePoolRef);
+
+                    setInstallPathIfNeeded(newTemplate, templatePoolRef);
+
                     templatePoolDao.update(templatePoolRef.getId(), 
templatePoolRef);
                 }
             } else if (getDataStore().getRole() == DataStoreRole.Image || 
getDataStore().getRole() == DataStoreRole.ImageCache) {
@@ -243,6 +248,33 @@ public class TemplateObject implements TemplateInfo {
         }
     }
 
+    /**
+     * In the case of managed storage, the install path may already be 
specified (by the storage plug-in), so do not overwrite it.
+     */
+    private void setInstallPathIfNeeded(TemplateObjectTO template, 
VMTemplateStoragePoolVO templatePoolRef) {
+        if (Strings.isNullOrEmpty(templatePoolRef.getInstallPath())) {
+            templatePoolRef.setInstallPath(template.getPath());
+        }
+    }
+
+    /**
+     * In the case of managed storage, the local download path may already be 
specified (by the storage plug-in), so do not overwrite it.
+     */
+    private void setDownloadPathIfNeeded(TemplateObjectTO template, 
VMTemplateStoragePoolVO templatePoolRef) {
+        if (Strings.isNullOrEmpty(templatePoolRef.getLocalDownloadPath())) {
+            templatePoolRef.setLocalDownloadPath(template.getPath());
+        }
+    }
+
+    /**
+     *  In the case of managed storage, the template size may already be 
specified (by the storage plug-in), so do not overwrite it.
+     */
+    private void setTemplateSizeIfNeeded(TemplateObjectTO template, 
VMTemplateStoragePoolVO templatePoolRef) {
+        if (templatePoolRef.getTemplateSize() == 0 && template.getSize() != 
null) {
+            templatePoolRef.setTemplateSize(template.getSize());
+        }
+    }
+
     @Override
     public void incRefCount() {
         if (dataStore == null) {
@@ -299,28 +331,17 @@ public class TemplateObject implements TemplateInfo {
 
     @Override
     public String getInstallPath() {
-        if (installPath != null)
+        if (installPath != null) {
             return installPath;
+        }
 
         if (dataStore == null) {
             return null;
         }
 
-        // managed primary data stores should not have an install path
-        if (dataStore instanceof PrimaryDataStore) {
-            PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore;
-
-            Map<String, String> details = primaryDataStore.getDetails();
-
-            boolean managed = details != null && 
Boolean.parseBoolean(details.get(PrimaryDataStore.MANAGED));
-
-            if (managed) {
-                return null;
-            }
-        }
-
         DataObjectInStore obj = objectInStoreMgr.findObject(this, dataStore);
-        return obj.getInstallPath();
+
+        return obj != null ? obj.getInstallPath() : null;
     }
 
     public void setInstallPath(String installPath) {
@@ -435,7 +456,7 @@ public class TemplateObject implements TemplateInfo {
     }
 
     @Override
-    public Map getDetails() {
+    public Map<String, String> getDetails() {
         return imageVO.getDetails();
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java
 
b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java
index 1f1ba24..a86b321 100644
--- 
a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java
+++ 
b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java
@@ -28,6 +28,7 @@ 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.TemplateInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.command.CommandResult;
@@ -44,8 +45,8 @@ public class FakePrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
     boolean snapshotResult = true;
 
     @Override
-    public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
-        return null; // To change body of implemented methods, use File | 
Settings | File Templates.
+    public ChapInfo getChapInfo(DataObject dataObject) {
+        return null;
     }
 
     @Override
@@ -65,8 +66,13 @@ public class FakePrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
     }
 
     @Override
-    public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, 
StoragePool pool) {
-        return volume.getSize();
+    public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject 
dataObject, StoragePool pool) {
+        return dataObject.getSize();
+    }
+
+    @Override
+    public long getBytesRequiredForTemplate(TemplateInfo templateInfo, 
StoragePool storagePool) {
+        return 0L;
     }
 
     @Override
@@ -90,23 +96,21 @@ public class FakePrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
     }
 
     @Override
-    public void revertSnapshot(SnapshotInfo snapshot, 
AsyncCompletionCallback<CommandResult> callback) {
-        //To change body of implemented methods use File | Settings | File 
Templates.
+    public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo 
snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) {
     }
 
     @Override
     public DataTO getTO(DataObject data) {
-        return null;  //To change body of implemented methods use File | 
Settings | File Templates.
+        return null;
     }
 
     @Override
     public DataStoreTO getStoreTO(DataStore store) {
-        return null;  //To change body of implemented methods use File | 
Settings | File Templates.
+        return null;
     }
 
     @Override
     public void createAsync(DataStore store, DataObject data, 
AsyncCompletionCallback<CreateCmdResult> callback) {
-        //To change body of implemented methods use File | Settings | File 
Templates.
     }
 
     @Override
@@ -119,22 +123,19 @@ public class FakePrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
 
     @Override
     public void copyAsync(DataObject srcdata, DataObject destData, 
AsyncCompletionCallback<CopyCommandResult> callback) {
-        //To change body of implemented methods use File | Settings | File 
Templates.
     }
 
     @Override
     public boolean canCopy(DataObject srcData, DataObject destData) {
-        return false;  //To change body of implemented methods use File | 
Settings | File Templates.
+        return false;
     }
 
     @Override
     public void resize(DataObject data, 
AsyncCompletionCallback<CreateCmdResult> callback) {
-        //To change body of implemented methods use File | Settings | File 
Templates.
     }
 
     @Override
     public Map<String, String> getCapabilities() {
-        // TODO Auto-generated method stub
         return null;
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
 
b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
index c6960eb..d74ee57 100644
--- 
a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
+++ 
b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
@@ -16,14 +16,16 @@
 // under the License.
 package org.apache.cloudstack.storage.snapshot;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 import javax.inject.Inject;
 
 import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
 import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import 
org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
@@ -38,11 +40,15 @@ import 
org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
 import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
 import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 
+import org.springframework.stereotype.Component;
+
+import com.google.common.base.Optional;
+
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.to.DiskTO;
+import com.cloud.dc.dao.ClusterDao;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
@@ -71,18 +77,18 @@ import com.cloud.vm.dao.VMInstanceDao;
 public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
     private static final Logger s_logger = 
Logger.getLogger(StorageSystemSnapshotStrategy.class);
 
-    @Inject private AgentManager _agentMgr;
-    @Inject private DataStoreManager _dataStoreMgr;
-    @Inject private HostDao _hostDao;
-    @Inject private ManagementService _mgr;
-    @Inject private PrimaryDataStoreDao _storagePoolDao;
-    @Inject private SnapshotDao _snapshotDao;
-    @Inject private SnapshotDataFactory _snapshotDataFactory;
-    @Inject private SnapshotDataStoreDao _snapshotStoreDao;
-    @Inject private SnapshotDetailsDao _snapshotDetailsDao;
-    @Inject private VMInstanceDao _vmInstanceDao;
-    @Inject private VolumeDao _volumeDao;
-    @Inject private VolumeService _volService;
+    @Inject private AgentManager agentMgr;
+    @Inject private ClusterDao clusterDao;
+    @Inject private DataStoreManager dataStoreMgr;
+    @Inject private HostDao hostDao;
+    @Inject private ManagementService mgr;
+    @Inject private PrimaryDataStoreDao storagePoolDao;
+    @Inject private SnapshotDao snapshotDao;
+    @Inject private SnapshotDataFactory snapshotDataFactory;
+    @Inject private SnapshotDetailsDao snapshotDetailsDao;
+    @Inject private VMInstanceDao vmInstanceDao;
+    @Inject private VolumeDao volumeDao;
+    @Inject private VolumeService volService;
 
     @Override
     public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
@@ -91,14 +97,14 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
 
     @Override
     public boolean deleteSnapshot(Long snapshotId) {
-        SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId);
+        SnapshotVO snapshotVO = snapshotDao.findById(snapshotId);
 
         if (Snapshot.State.Destroyed.equals(snapshotVO.getState())) {
             return true;
         }
 
         if (Snapshot.State.Error.equals(snapshotVO.getState())) {
-            _snapshotDao.remove(snapshotId);
+            snapshotDao.remove(snapshotId);
 
             return true;
         }
@@ -107,12 +113,12 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
             throw new InvalidParameterValueException("Unable to delete 
snapshotshot " + snapshotId + " because it is in the following state: " + 
snapshotVO.getState());
         }
 
-        SnapshotObject snapshotObj = 
(SnapshotObject)_snapshotDataFactory.getSnapshot(snapshotId, 
DataStoreRole.Primary);
+        SnapshotObject snapshotObj = 
(SnapshotObject)snapshotDataFactory.getSnapshot(snapshotId, 
DataStoreRole.Primary);
 
         if (snapshotObj == null) {
             s_logger.debug("Can't find snapshot; deleting it in DB");
 
-            _snapshotDao.remove(snapshotId);
+            snapshotDao.remove(snapshotId);
 
             return true;
         }
@@ -165,7 +171,7 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
             throw new CloudRuntimeException("Only the " + 
ImageFormat.VHD.toString() + " image type is currently supported.");
         }
 
-        SnapshotVO snapshotVO = 
_snapshotDao.acquireInLockTable(snapshotInfo.getId());
+        SnapshotVO snapshotVO = 
snapshotDao.acquireInLockTable(snapshotInfo.getId());
 
         if (snapshotVO == null) {
             throw new CloudRuntimeException("Failed to acquire lock on the 
following snapshot: " + snapshotInfo.getId());
@@ -176,7 +182,24 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
         try {
             volumeInfo.stateTransit(Volume.Event.SnapshotRequested);
 
-            // tell the storage driver to create a back-end volume (eventually 
used to create a new SR on and to copy the VM snapshot VDI to)
+            // only XenServer is currently supported
+            HostVO hostVO = getHost(volumeInfo.getId());
+
+            boolean canStorageSystemCreateVolumeFromSnapshot = 
canStorageSystemCreateVolumeFromSnapshot(volumeInfo.getPoolId());
+            boolean computeClusterSupportsResign = 
clusterDao.computeWhetherClusterSupportsResigning(hostVO.getClusterId());
+
+            // if canStorageSystemCreateVolumeFromSnapshot && 
computeClusterSupportsResign, then take a back-end snapshot or create a 
back-end clone;
+            // else, just create a new back-end volume (eventually used to 
create a new SR on and to copy a VDI to)
+
+            if (canStorageSystemCreateVolumeFromSnapshot && 
computeClusterSupportsResign) {
+                SnapshotDetailsVO snapshotDetail = new 
SnapshotDetailsVO(snapshotInfo.getId(),
+                    "takeSnapshot",
+                    Boolean.TRUE.toString(),
+                    false);
+
+                snapshotDetailsDao.persist(snapshotDetail);
+            }
+
             result = snapshotSvr.takeSnapshot(snapshotInfo);
 
             if (result.isFailed()) {
@@ -185,9 +208,9 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
                 throw new CloudRuntimeException(result.getResult());
             }
 
-            // send a command to XenServer to create a VM snapshot on the 
applicable SR (get back the VDI UUID of the VM snapshot)
-
-            performSnapshotAndCopyOnHostSide(volumeInfo, snapshotInfo);
+            if (!canStorageSystemCreateVolumeFromSnapshot || 
!computeClusterSupportsResign) {
+                performSnapshotAndCopyOnHostSide(volumeInfo, snapshotInfo);
+            }
 
             markAsBackedUp((SnapshotObject)result.getSnashot());
         }
@@ -199,19 +222,35 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
                 volumeInfo.stateTransit(Volume.Event.OperationFailed);
             }
 
-            _snapshotDao.releaseFromLockTable(snapshotInfo.getId());
+            snapshotDao.releaseFromLockTable(snapshotInfo.getId());
         }
 
         return snapshotInfo;
     }
 
+    private boolean canStorageSystemCreateVolumeFromSnapshot(long 
storagePoolId) {
+        boolean supportsCloningVolumeFromSnapshot = false;
+
+        DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, 
DataStoreRole.Primary);
+
+        Map<String, String> mapCapabilities = 
dataStore.getDriver().getCapabilities();
+
+        if (mapCapabilities != null) {
+            String value = 
mapCapabilities.get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString());
+
+            supportsCloningVolumeFromSnapshot = Boolean.valueOf(value);
+        }
+
+        return supportsCloningVolumeFromSnapshot;
+    }
+
     private void performSnapshotAndCopyOnHostSide(VolumeInfo volumeInfo, 
SnapshotInfo snapshotInfo) {
         Map<String, String> sourceDetails = null;
 
-        VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
+        VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId());
 
         Long vmInstanceId = volumeVO.getInstanceId();
-        VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vmInstanceId);
+        VMInstanceVO vmInstanceVO = vmInstanceDao.findById(vmInstanceId);
 
         Long hostId = null;
 
@@ -233,11 +272,30 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
             sourceDetails = getSourceDetails(volumeInfo);
         }
 
-        HostVO hostVO = getHost(hostId, volumeVO);
+        HostVO hostVO = null;
+
+        if (hostId != null) {
+            hostVO = hostDao.findById(hostId);
+        }
+        else {
+            Optional<HostVO> optHostVO = getHost(volumeInfo.getDataCenterId(), 
false);
+
+            if (optHostVO.isPresent()) {
+                hostVO = optHostVO.get();
+            }
+        }
+
+        if (hostVO == null) {
+            final String errMsg = "Unable to locate an applicable host";
+
+            s_logger.error("performSnapshotAndCopyOnHostSide: " + errMsg);
+
+            throw new CloudRuntimeException(errMsg);
+        }
 
         long storagePoolId = volumeVO.getPoolId();
-        StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
-        DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, 
DataStoreRole.Primary);
+        StoragePoolVO storagePoolVO = storagePoolDao.findById(storagePoolId);
+        DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, 
DataStoreRole.Primary);
 
         Map<String, String> destDetails = getDestDetails(storagePoolVO, 
snapshotInfo);
 
@@ -248,23 +306,23 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
         try {
             // if sourceDetails != null, we need to connect the host(s) to the 
volume
             if (sourceDetails != null) {
-                _volService.grantAccess(volumeInfo, hostVO, dataStore);
+                volService.grantAccess(volumeInfo, hostVO, dataStore);
             }
 
-            _volService.grantAccess(snapshotInfo, hostVO, dataStore);
+            volService.grantAccess(snapshotInfo, hostVO, dataStore);
 
-            snapshotAndCopyAnswer = 
(SnapshotAndCopyAnswer)_agentMgr.send(hostVO.getId(), snapshotAndCopyCommand);
+            snapshotAndCopyAnswer = 
(SnapshotAndCopyAnswer)agentMgr.send(hostVO.getId(), snapshotAndCopyCommand);
         }
         catch (Exception ex) {
             throw new CloudRuntimeException(ex.getMessage());
         }
         finally {
             try {
-                _volService.revokeAccess(snapshotInfo, hostVO, dataStore);
+                volService.revokeAccess(snapshotInfo, hostVO, dataStore);
 
                 // if sourceDetails != null, we need to disconnect the host(s) 
from the volume
                 if (sourceDetails != null) {
-                    _volService.revokeAccess(volumeInfo, hostVO, dataStore);
+                    volService.revokeAccess(volumeInfo, hostVO, dataStore);
                 }
             }
             catch (Exception ex) {
@@ -292,22 +350,22 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
                 path,
                 false);
 
-        _snapshotDetailsDao.persist(snapshotDetail);
+        snapshotDetailsDao.persist(snapshotDetail);
     }
 
     private Map<String, String> getSourceDetails(VolumeInfo volumeInfo) {
-        Map<String, String> sourceDetails = new HashMap<String, String>();
+        Map<String, String> sourceDetails = new HashMap<>();
 
-        VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
+        VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId());
 
         long storagePoolId = volumeVO.getPoolId();
-        StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
+        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 = _volService.getChapInfo(volumeInfo, 
volumeInfo.getDataStore());
+        ChapInfo chapInfo = volService.getChapInfo(volumeInfo, 
volumeInfo.getDataStore());
 
         if (chapInfo != null) {
             sourceDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, 
chapInfo.getInitiatorUsername());
@@ -320,7 +378,7 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
     }
 
     private Map<String, String> getDestDetails(StoragePoolVO storagePoolVO, 
SnapshotInfo snapshotInfo) {
-        Map<String, String> destDetails = new HashMap<String, String>();
+        Map<String, String> destDetails = new HashMap<>();
 
         destDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
         destDetails.put(DiskTO.STORAGE_PORT, 
String.valueOf(storagePoolVO.getPort()));
@@ -338,7 +396,7 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
     }
 
     private String getProperty(long snapshotId, String property) {
-        SnapshotDetailsVO snapshotDetails = 
_snapshotDetailsDao.findDetail(snapshotId, property);
+        SnapshotDetailsVO snapshotDetails = 
snapshotDetailsDao.findDetail(snapshotId, property);
 
         if (snapshotDetails != null) {
             return snapshotDetails.getValue();
@@ -347,38 +405,87 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
         return null;
     }
 
-    private HostVO getHost(Long hostId, VolumeVO volumeVO) {
-        HostVO hostVO = _hostDao.findById(hostId);
+    private HostVO getHost(long volumeId) {
+        VolumeVO volumeVO = volumeDao.findById(volumeId);
+
+        Long vmInstanceId = volumeVO.getInstanceId();
+        VMInstanceVO vmInstanceVO = vmInstanceDao.findById(vmInstanceId);
+
+        Long hostId = null;
+
+        // if the volume to snapshot is associated with a VM
+        if (vmInstanceVO != null) {
+            hostId = vmInstanceVO.getHostId();
+
+            // if the VM is not associated with a host
+            if (hostId == null) {
+                hostId = vmInstanceVO.getLastHostId();
+            }
+        }
+
+        return getHost(volumeVO.getDataCenterId(), hostId);
+    }
+
+    private HostVO getHost(long zoneId, Long hostId) {
+        Optional<HostVO> optHostVO = getHost(zoneId, true);
+
+        if (optHostVO.isPresent()) {
+            return optHostVO.get();
+        }
+
+        HostVO hostVO = hostDao.findById(hostId);
 
         if (hostVO != null) {
             return hostVO;
         }
 
-        // pick a host in any XenServer cluster that's in the applicable zone
+        optHostVO = getHost(zoneId, false);
 
-        long zoneId = volumeVO.getDataCenterId();
+        if (optHostVO.isPresent()) {
+            return optHostVO.get();
+        }
 
-        List<? extends Cluster> clusters = _mgr.searchForClusters(zoneId, new 
Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString());
+        throw new CloudRuntimeException("Unable to locate an applicable host");
+    }
+
+    private Optional<HostVO> getHost(long zoneId, boolean 
computeClusterMustSupportResign) {
+        List<? extends Cluster> clusters = mgr.searchForClusters(zoneId, 0L, 
Long.MAX_VALUE, HypervisorType.XenServer.toString());
 
         if (clusters == null) {
-            throw new CloudRuntimeException("Unable to locate an applicable 
cluster");
+            clusters = new ArrayList<>();
         }
 
+        Collections.shuffle(clusters, new Random(System.nanoTime()));
+
+        clusters:
         for (Cluster cluster : clusters) {
             if (cluster.getAllocationState() == AllocationState.Enabled) {
-                List<HostVO> hosts = _hostDao.findByClusterId(cluster.getId());
+                List<HostVO> hosts = hostDao.findByClusterId(cluster.getId());
 
                 if (hosts != null) {
+                    Collections.shuffle(hosts, new Random(System.nanoTime()));
+
                     for (HostVO host : hosts) {
                         if (host.getResourceState() == ResourceState.Enabled) {
-                            return host;
+                            if (computeClusterMustSupportResign) {
+                                if 
(clusterDao.computeWhetherClusterSupportsResigning(cluster.getId())) {
+                                    return Optional.of(host);
+                                }
+                                else {
+                                    // no other host in the cluster in 
question should be able to satisfy our requirements here, so move on to the 
next cluster
+                                    continue clusters;
+                                }
+                            }
+                            else {
+                                return Optional.of(host);
+                            }
                         }
                     }
                 }
             }
         }
 
-        throw new CloudRuntimeException("Unable to locate an applicable 
cluster");
+        return Optional.absent();
     }
 
     private void markAsBackedUp(SnapshotObject snapshotObj) {
@@ -406,18 +513,18 @@ public class StorageSystemSnapshotStrategy extends 
SnapshotStrategyBase {
 
         long volumeId = snapshot.getVolumeId();
 
-        VolumeVO volumeVO = _volumeDao.findByIdIncludingRemoved(volumeId);
+        VolumeVO volumeVO = volumeDao.findByIdIncludingRemoved(volumeId);
 
         long storagePoolId = volumeVO.getPoolId();
 
-        DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, 
DataStoreRole.Primary);
+        DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, 
DataStoreRole.Primary);
 
         if (dataStore != null) {
             Map<String, String> mapCapabilities = 
dataStore.getDriver().getCapabilities();
 
             if (mapCapabilities != null) {
                 String value = 
mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
-                Boolean supportsStorageSystemSnapshots = new Boolean(value);
+                Boolean supportsStorageSystemSnapshots = 
Boolean.valueOf(value);
 
                 if (supportsStorageSystemSnapshots) {
                     return StrategyPriority.HIGHEST;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
 
b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
index 99fc161..2544484 100644
--- 
a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
+++ 
b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
@@ -78,6 +78,8 @@ public class XenserverSnapshotStrategy extends 
SnapshotStrategyBase {
     VolumeDao volumeDao;
     @Inject
     SnapshotDataFactory snapshotDataFactory;
+    @Inject
+    private SnapshotDao _snapshotDao;
 
     @Override
     public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) {
@@ -289,7 +291,7 @@ public class XenserverSnapshotStrategy extends 
SnapshotStrategyBase {
     @Override
     public boolean revertSnapshot(SnapshotInfo snapshot) {
         if (canHandle(snapshot,SnapshotOperation.REVERT) == 
StrategyPriority.CANT_HANDLE) {
-            throw new UnsupportedOperationException("Reverting not supported. 
Create a template or volume based on the snapshot instead.");
+            throw new CloudRuntimeException("Reverting not supported. Create a 
template or volume based on the snapshot instead.");
         }
 
         SnapshotVO snapshotVO = 
snapshotDao.acquireInLockTable(snapshot.getId());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
 
b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
index 73a8544..194f7bd 100644
--- 
a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
+++ 
b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
@@ -223,7 +223,7 @@ public abstract class AbstractStoragePoolAllocator extends 
AdapterBase implement
         Volume volume = _volumeDao.findById(dskCh.getVolumeId());
         List<Volume> requestVolumes = new ArrayList<Volume>();
         requestVolumes.add(volume);
-        return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) && 
storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool);
+        return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) && 
storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool, plan.getClusterId());
     }
 
     /*

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/volume/pom.xml
----------------------------------------------------------------------
diff --git a/engine/storage/volume/pom.xml b/engine/storage/volume/pom.xml
index e790df1..340010b 100644
--- a/engine/storage/volume/pom.xml
+++ b/engine/storage/volume/pom.xml
@@ -25,6 +25,11 @@
       <artifactId>cloud-engine-storage</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-engine-storage-image</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
   <build>
     <plugins>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
 
b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
index f3c9e79..8196678 100644
--- 
a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
+++ 
b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
@@ -26,6 +26,7 @@ import javax.inject.Inject;
 
 import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
+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.DataStoreProvider;
 import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
@@ -65,6 +66,7 @@ import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.storage.encoding.EncodingType;
 
+@SuppressWarnings("serial")
 public class PrimaryDataStoreImpl implements PrimaryDataStore {
     private static final Logger s_logger = 
Logger.getLogger(PrimaryDataStoreImpl.class);
 
@@ -239,10 +241,34 @@ public class PrimaryDataStoreImpl implements 
PrimaryDataStore {
         return pdsv.isManaged();
     }
 
+    private boolean canCloneVolume() {
+        return 
Boolean.valueOf(getDriver().getCapabilities().get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString()));
+    }
+
+    /**
+     * The parameter createEntryInTempSpoolRef in the overloaded 
create(DataObject, boolean) method only applies to managed storage. We pass
+     * in "true" here.
+     *
+     * In the case of managed storage that can create a volume from a volume 
(clone), if the DataObject passed in is a TemplateInfo,
+     * we do want to create an entry in the cloud.template_spool_ref table (so 
that multiple uses of the template can be leveraged from
+     * the one copy on managed storage).
+     *
+     * In cases where UUID resigning is not available, then the code calling 
"create" should invoke the overloaded "create" method whose second
+     * parameter is a boolean. This code can pass in "false" so that an entry 
in the cloud.template_spool_ref table is not created (no template to share
+     * on the primary storage).
+     */
+    @Override
+    public DataObject create(DataObject dataObject) {
+        return create(dataObject, true);
+    }
+
+    /**
+     * Please read the comment for the create(DataObject) method if you are 
planning on passing in "false" for createEntryInTempSpoolRef.
+     */
     @Override
-    public DataObject create(DataObject obj) {
+    public DataObject create(DataObject obj, boolean 
createEntryInTempSpoolRef) {
         // create template on primary storage
-        if (obj.getType() == DataObjectType.TEMPLATE && !isManaged()) {
+        if (obj.getType() == DataObjectType.TEMPLATE && (!isManaged() || 
(createEntryInTempSpoolRef && canCloneVolume()))) {
             try {
                 String templateIdPoolIdString = "templateId:" + obj.getId() + 
"poolId:" + getId();
                 VMTemplateStoragePoolVO templateStoragePoolRef;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
 
b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
index a2fd656..54e5105 100644
--- 
a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
+++ 
b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
@@ -19,14 +19,21 @@
 package org.apache.cloudstack.storage.volume;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 import javax.inject.Inject;
 
+import com.cloud.dc.dao.ClusterDao;
 import com.cloud.offering.DiskOffering;
+import com.cloud.org.Cluster;
+import com.cloud.org.Grouping.AllocationState;
+import com.cloud.resource.ResourceState;
+import com.cloud.server.ManagementService;
 import com.cloud.storage.RegisterVolumePayload;
 import com.cloud.utils.Pair;
 import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity;
@@ -36,6 +43,7 @@ import 
org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
 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.EndPoint;
 import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
@@ -59,8 +67,10 @@ import org.apache.cloudstack.storage.command.CommandResult;
 import org.apache.cloudstack.storage.command.CopyCmdAnswer;
 import org.apache.cloudstack.storage.command.DeleteCommand;
 import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
+import org.apache.cloudstack.storage.image.store.TemplateObject;
 import org.apache.cloudstack.storage.to.TemplateObjectTO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
 import org.apache.log4j.Logger;
@@ -80,7 +90,9 @@ import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.host.Host;
+import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
+import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ScopeType;
@@ -134,6 +146,14 @@ public class VolumeServiceImpl implements VolumeService {
     EndPointSelector _epSelector;
     @Inject
     HostDao _hostDao;
+    @Inject
+    private PrimaryDataStoreDao storagePoolDao;
+    @Inject
+    private HostDetailsDao hostDetailsDao;
+    @Inject
+    private ManagementService mgr;
+    @Inject
+    private ClusterDao clusterDao;
 
     public VolumeServiceImpl() {
     }
@@ -160,11 +180,11 @@ public class VolumeServiceImpl implements VolumeService {
     }
 
     @Override
-    public ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore) {
+    public ChapInfo getChapInfo(DataObject dataObject, DataStore dataStore) {
         DataStoreDriver dataStoreDriver = dataStore.getDriver();
 
         if (dataStoreDriver instanceof PrimaryDataStoreDriver) {
-            return 
((PrimaryDataStoreDriver)dataStoreDriver).getChapInfo(volumeInfo);
+            return 
((PrimaryDataStoreDriver)dataStoreDriver).getChapInfo(dataObject);
         }
 
         return null;
@@ -554,6 +574,49 @@ public class VolumeServiceImpl implements VolumeService {
         return null;
     }
 
+    protected Void 
createManagedTemplateImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, 
CreateCmdResult> callback, CreateVolumeContext<CreateCmdResult> context) {
+        CreateCmdResult result = callback.getResult();
+        VolumeApiResult res = new VolumeApiResult(null);
+
+        res.setResult(result.getResult());
+
+        AsyncCallFuture<VolumeApiResult> future = context.getFuture();
+        DataObject templateOnPrimaryStoreObj = context.getVolume();
+
+        if (result.isSuccess()) {
+            
((TemplateObject)templateOnPrimaryStoreObj).setInstallPath(result.getPath());
+            templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, 
result.getAnswer());
+        }
+        else {
+            templateOnPrimaryStoreObj.processEvent(Event.OperationFailed);
+        }
+
+        future.complete(res);
+
+        return null;
+    }
+
+    protected Void 
copyManagedTemplateCallback(AsyncCallbackDispatcher<VolumeServiceImpl, 
CopyCommandResult> callback, CreateBaseImageContext<VolumeApiResult> context) {
+        CopyCommandResult result = callback.getResult();
+        VolumeApiResult res = new VolumeApiResult(context.getVolume());
+
+        res.setResult(result.getResult());
+
+        AsyncCallFuture<VolumeApiResult> future = context.getFuture();
+        DataObject templateOnPrimaryStoreObj = context.destObj;
+
+        if (result.isSuccess()) {
+            templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, 
result.getAnswer());
+        }
+        else {
+            templateOnPrimaryStoreObj.processEvent(Event.OperationFailed);
+        }
+
+        future.complete(res);
+
+        return null;
+    }
+
     @DB
     protected Void 
copyBaseImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, 
CopyCommandResult> callback, CreateBaseImageContext<VolumeApiResult> context) {
         CopyCommandResult result = callback.getResult();
@@ -636,8 +699,10 @@ public class VolumeServiceImpl implements VolumeService {
                             if (templatePoolRef == null) {
                                 s_logger.warn("Reset Template State On Pool 
failed - unable to lock TemplatePoolRef " + templatePoolRefId);
                             } else {
+                                templatePoolRef.setTemplateSize(0);
                                 
templatePoolRef.setDownloadState(VMTemplateStorageResourceAssoc.Status.NOT_DOWNLOADED);
                                 
templatePoolRef.setState(ObjectInDataStoreStateMachine.State.Allocated);
+
                                 _tmpltPoolDao.update(templatePoolRefId, 
templatePoolRef);
                             }
                         }finally {
@@ -653,50 +718,253 @@ public class VolumeServiceImpl implements VolumeService {
         return null;
     }
 
-    @Override
-    public AsyncCallFuture<VolumeApiResult> 
createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long 
destDataStoreId,
-            TemplateInfo srcTemplateInfo, long destHostId) {
-        PrimaryDataStore destPrimaryDataStore = 
dataStoreMgr.getPrimaryDataStore(destDataStoreId);
-        TemplateInfo destTemplateInfo = 
(TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo);
-        Host destHost = _hostDao.findById(destHostId);
+    /**
+     * Creates a template volume on managed storage, which will be used for 
creating ROOT volumes by cloning.
+     *
+     * @param srcTemplateInfo Source template on secondary storage
+     * @param destPrimaryDataStore Managed storage on which we need to create 
the volume
+     */
+    private TemplateInfo createManagedTemplateVolume(TemplateInfo 
srcTemplateInfo, PrimaryDataStore destPrimaryDataStore) {
+        // create a template volume on primary storage
+        AsyncCallFuture<VolumeApiResult> createTemplateFuture = new 
AsyncCallFuture<>();
+        TemplateInfo templateOnPrimary = 
(TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo);
 
-        if (destHost == null) {
-            throw new CloudRuntimeException("Destinatin host should not be 
null.");
+        VMTemplateStoragePoolVO templatePoolRef = 
_tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), 
templateOnPrimary.getId());
+
+        if (templatePoolRef == null) {
+            throw new CloudRuntimeException("Failed to find template " + 
srcTemplateInfo.getUniqueName() + " in storage pool " + 
destPrimaryDataStore.getId());
         }
 
-        AsyncCallFuture<VolumeApiResult> future = new 
AsyncCallFuture<VolumeApiResult>();
+        // At this point, we have an entry in the DB that points to our cached 
template.
+        // We need to lock it as there may be other VMs that may get started 
using the same template.
+        // We want to avoid having to create multiple cache copies of the same 
template.
+
+        int storagePoolMaxWaitSeconds = 
NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()),
 3600);
+        long templatePoolRefId = templatePoolRef.getId();
+
+        templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, 
storagePoolMaxWaitSeconds);
+
+        if (templatePoolRef == null) {
+            throw new CloudRuntimeException("Unable to acquire lock on 
VMTemplateStoragePool: " + templatePoolRefId);
+        }
+
+        // Template already exists
+        if (templatePoolRef.getState() == 
ObjectInDataStoreStateMachine.State.Ready) {
+            _tmpltPoolDao.releaseFromLockTable(templatePoolRefId);
+
+            return templateOnPrimary;
+        }
 
         try {
-            // must call driver to have a volume created
-            AsyncCallFuture<VolumeApiResult> createVolumeFuture = 
createVolumeAsync(volumeInfo, destPrimaryDataStore);
+            // create a cache volume on the back-end
+
+            templateOnPrimary.processEvent(Event.CreateOnlyRequested);
+
+            CreateVolumeContext<CreateCmdResult> createContext = new 
CreateVolumeContext<>(null, templateOnPrimary, createTemplateFuture);
+            AsyncCallbackDispatcher<VolumeServiceImpl, CreateCmdResult> 
createCaller = AsyncCallbackDispatcher.create(this);
+
+            
createCaller.setCallback(createCaller.getTarget().createManagedTemplateImageCallback(null,
 null)).setContext(createContext);
+
+            destPrimaryDataStore.getDriver().createAsync(destPrimaryDataStore, 
templateOnPrimary, createCaller);
+
+            VolumeApiResult result = createTemplateFuture.get();
+
+            if (result.isFailed()) {
+                String errMesg =  result.getResult();
+
+                throw new CloudRuntimeException("Unable to create template " + 
 templateOnPrimary.getId() +
+                        " on primary storage " + destPrimaryDataStore.getId() 
+ ":" + errMesg);
+            }
+        } catch (Throwable e) {
+            s_logger.debug("Failed to create template volume on storage", e);
+
+            templateOnPrimary.processEvent(Event.OperationFailed);
+
+            throw new CloudRuntimeException(e.getMessage());
+        }
+        finally {
+            _tmpltPoolDao.releaseFromLockTable(templatePoolRefId);
+        }
+
+        return templateOnPrimary;
+    }
+
+    /**
+     * This function copies a template from secondary storage to a template 
volume
+     * created on managed storage. This template volume will be used as a 
cache.
+     * Instead of copying the template to a ROOT volume every time, a clone is 
performed instead.
+     *
+     * @param srcTemplateInfo Source from which to copy the template
+     * @param templateOnPrimary Dest to copy to
+     * @param templatePoolRef Template reference on primary storage (entry in 
the template_spool_ref)
+     * @param destPrimaryDataStore The managed primary storage
+     * @param destHost The host that we will use for the copy
+     */
+    private void copyTemplateToManagedTemplateVolume(TemplateInfo 
srcTemplateInfo, TemplateInfo templateOnPrimary, VMTemplateStoragePoolVO 
templatePoolRef,
+            PrimaryDataStore destPrimaryDataStore, Host destHost)
+    {
+        AsyncCallFuture<VolumeApiResult> copyTemplateFuture = new 
AsyncCallFuture<>();
+        int storagePoolMaxWaitSeconds = 
NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()),
 3600);
+        long templatePoolRefId = templatePoolRef.getId();
 
+        templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, 
storagePoolMaxWaitSeconds);
+
+        if (templatePoolRef == null) {
+            throw new CloudRuntimeException("Unable to acquire lock on 
VMTemplateStoragePool: " + templatePoolRefId);
+        }
+
+        if (templatePoolRef.getDownloadState() == Status.DOWNLOADED) {
+            // There can be cases where we acquired the lock, but the template
+            // was already copied by a previous thread. Just return in that 
case.
+
+            s_logger.debug("Template already downloaded, nothing to do");
+
+            return;
+        }
+
+        try {
+           // copy the template from sec storage to the created volume
+            CreateBaseImageContext<CreateCmdResult> copyContext = new 
CreateBaseImageContext<>(
+                    null, null, destPrimaryDataStore, srcTemplateInfo,
+                    copyTemplateFuture, templateOnPrimary, templatePoolRefId
+            );
+
+            AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> 
copyCaller = AsyncCallbackDispatcher.create(this);
+            
copyCaller.setCallback(copyCaller.getTarget().copyManagedTemplateCallback(null, 
null)).setContext(copyContext);
+
+            // Populate details which will be later read by the storage 
subsystem.
+            Map<String, String> details = new HashMap<String, String>();
+
+            details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString());
+            details.put(PrimaryDataStore.STORAGE_HOST, 
destPrimaryDataStore.getHostAddress());
+            details.put(PrimaryDataStore.STORAGE_PORT, 
String.valueOf(destPrimaryDataStore.getPort()));
+            details.put(PrimaryDataStore.MANAGED_STORE_TARGET, 
((TemplateObject)templateOnPrimary).getInstallPath());
+            details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, 
srcTemplateInfo.getUniqueName());
+            details.put(PrimaryDataStore.REMOVE_AFTER_COPY, 
Boolean.TRUE.toString());
+            details.put(PrimaryDataStore.VOLUME_SIZE, 
String.valueOf(templateOnPrimary.getSize()));
+
+            ChapInfo chapInfo = getChapInfo(templateOnPrimary, 
destPrimaryDataStore);
+
+            if (chapInfo != null) {
+                details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, 
chapInfo.getInitiatorUsername());
+                details.put(PrimaryDataStore.CHAP_INITIATOR_SECRET, 
chapInfo.getInitiatorSecret());
+                details.put(PrimaryDataStore.CHAP_TARGET_USERNAME, 
chapInfo.getTargetUsername());
+                details.put(PrimaryDataStore.CHAP_TARGET_SECRET, 
chapInfo.getTargetSecret());
+            }
+
+            templateOnPrimary.processEvent(Event.CopyingRequested);
+
+            destPrimaryDataStore.setDetails(details);
+
+            grantAccess(templateOnPrimary, destHost, destPrimaryDataStore);
+
+            VolumeApiResult result = null;
+
+            try {
+                motionSrv.copyAsync(srcTemplateInfo, templateOnPrimary, 
destHost, copyCaller);
+
+                result = copyTemplateFuture.get();
+            }
+            finally {
+                revokeAccess(templateOnPrimary, destHost, 
destPrimaryDataStore);
+            }
+
+            if (result.isFailed()) {
+                throw new CloudRuntimeException("Failed to copy template " + 
templateOnPrimary.getId() +
+                        " to primary storage " + destPrimaryDataStore.getId() 
+ ": " + result.getResult());
+                // XXX: I find it is useful to destroy the volume on primary 
storage instead of another thread trying the copy again because I've seen
+                // something weird happens to the volume (XenServer creates an 
SR, but the VDI copy can fail).
+                // For now, I just retry the copy.
+            }
+        }
+        catch (Throwable e) {
+            s_logger.debug("Failed to create a template on primary storage", 
e);
+
+            templateOnPrimary.processEvent(Event.OperationFailed);
+
+            throw new CloudRuntimeException(e.getMessage());
+        }
+        finally {
+            _tmpltPoolDao.releaseFromLockTable(templatePoolRefId);
+        }
+    }
+
+    /**
+     * Clones the template volume on managed storage to the ROOT volume
+     *
+     * @param volumeInfo ROOT volume to create
+     * @param templateOnPrimary Template from which to clone the ROOT volume
+     * @param destPrimaryDataStore Primary storage of the volume
+     * @param future For async
+     */
+    private void createManagedVolumeCloneTemplateAsync(VolumeInfo volumeInfo, 
TemplateInfo templateOnPrimary, PrimaryDataStore destPrimaryDataStore,
+            AsyncCallFuture<VolumeApiResult> future) {
+        VMTemplateStoragePoolVO templatePoolRef = 
_tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), 
templateOnPrimary.getId());
+
+        if (templatePoolRef == null) {
+            throw new CloudRuntimeException("Failed to find template " + 
templateOnPrimary.getUniqueName() + " in storage pool " + 
destPrimaryDataStore.getId());
+        }
+
+        //XXX: not sure if this the right thing to do here. We can always 
fallback to the "copy from sec storage"
+        if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) {
+            throw new CloudRuntimeException("Template " + 
templateOnPrimary.getUniqueName() + " has not been downloaded to primary 
storage.");
+        }
+
+        try {
+            volumeInfo.processEvent(Event.CreateOnlyRequested);
+
+            CreateVolumeFromBaseImageContext<VolumeApiResult> context =
+                    new CreateVolumeFromBaseImageContext<>(null, volumeInfo, 
destPrimaryDataStore, templateOnPrimary, future, null);
+
+            AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> 
caller = AsyncCallbackDispatcher.create(this);
+
+            
caller.setCallback(caller.getTarget().createVolumeFromBaseImageCallBack(null, 
null));
+            caller.setContext(context);
+
+            motionSrv.copyAsync(templateOnPrimary, volumeInfo, caller);
+        } catch (Throwable e) {
+            s_logger.debug("Failed to clone template on primary storage", e);
+
+            volumeInfo.processEvent(Event.OperationFailed);
+
+            throw new CloudRuntimeException(e.getMessage());
+        }
+    }
+
+    private void createManagedVolumeCopyTemplateAsync(VolumeInfo volumeInfo, 
PrimaryDataStore primaryDataStore, TemplateInfo srcTemplateInfo, Host destHost,
+            AsyncCallFuture<VolumeApiResult> future) {
+        try {
+            // Create a volume on managed storage.
+
+            TemplateInfo destTemplateInfo = 
(TemplateInfo)primaryDataStore.create(srcTemplateInfo, false);
+
+            AsyncCallFuture<VolumeApiResult> createVolumeFuture = 
createVolumeAsync(volumeInfo, primaryDataStore);
             VolumeApiResult createVolumeResult = createVolumeFuture.get();
 
             if (createVolumeResult.isFailed()) {
                 throw new CloudRuntimeException("Creation of a volume failed: 
" + createVolumeResult.getResult());
             }
 
-            // refresh the volume from the DB
-            volumeInfo = volFactory.getVolume(volumeInfo.getId(), 
destPrimaryDataStore);
-
-            grantAccess(volumeInfo, destHost, destPrimaryDataStore);
+            // Refresh the volume info from the DB.
+            volumeInfo = volFactory.getVolume(volumeInfo.getId(), 
primaryDataStore);
 
             ManagedCreateBaseImageContext<CreateCmdResult> context = new 
ManagedCreateBaseImageContext<CreateCmdResult>(null, volumeInfo,
-                    destPrimaryDataStore, srcTemplateInfo, future);
+                    primaryDataStore, srcTemplateInfo, future);
             AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> 
caller = AsyncCallbackDispatcher.create(this);
+
             
caller.setCallback(caller.getTarget().managedCopyBaseImageCallback(null, 
null)).setContext(context);
 
             Map<String, String> details = new HashMap<String, String>();
 
             details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString());
-            details.put(PrimaryDataStore.STORAGE_HOST, 
destPrimaryDataStore.getHostAddress());
-            details.put(PrimaryDataStore.STORAGE_PORT, 
String.valueOf(destPrimaryDataStore.getPort()));
+            details.put(PrimaryDataStore.STORAGE_HOST, 
primaryDataStore.getHostAddress());
+            details.put(PrimaryDataStore.STORAGE_PORT, 
String.valueOf(primaryDataStore.getPort()));
             // for managed storage, the storage repository (XenServer) or 
datastore (ESX) name is based off of the iScsiName property of a volume
             details.put(PrimaryDataStore.MANAGED_STORE_TARGET, 
volumeInfo.get_iScsiName());
             details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, 
volumeInfo.getName());
             details.put(PrimaryDataStore.VOLUME_SIZE, 
String.valueOf(volumeInfo.getSize()));
 
-            ChapInfo chapInfo = getChapInfo(volumeInfo, destPrimaryDataStore);
+            ChapInfo chapInfo = getChapInfo(volumeInfo, primaryDataStore);
 
             if (chapInfo != null) {
                 details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, 
chapInfo.getInitiatorUsername());
@@ -705,17 +973,21 @@ public class VolumeServiceImpl implements VolumeService {
                 details.put(PrimaryDataStore.CHAP_TARGET_SECRET, 
chapInfo.getTargetSecret());
             }
 
-            destPrimaryDataStore.setDetails(details);
+            primaryDataStore.setDetails(details);
 
-            motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, 
caller);
-        }
-        catch (Throwable t) {
+            grantAccess(volumeInfo, destHost, primaryDataStore);
+
+            try {
+                motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, 
destHost, caller);
+            }
+            finally {
+                revokeAccess(volumeInfo, destHost, primaryDataStore);
+            }
+        } catch (Throwable t) {
             String errMsg = t.toString();
 
             volumeInfo.processEvent(Event.DestroyRequested);
 
-            revokeAccess(volumeInfo, destHost, destPrimaryDataStore);
-
             try {
                 AsyncCallFuture<VolumeApiResult> expungeVolumeFuture = 
expungeVolumeAsync(volumeInfo);
 
@@ -735,10 +1007,112 @@ public class VolumeServiceImpl implements VolumeService 
{
 
             future.complete(result);
         }
+    }
+
+    @Override
+    public AsyncCallFuture<VolumeApiResult> 
createManagedStorageVolumeFromTemplateAsync(VolumeInfo volumeInfo, long 
destDataStoreId,
+            TemplateInfo srcTemplateInfo, long destHostId) {
+        PrimaryDataStore destPrimaryDataStore = 
dataStoreMgr.getPrimaryDataStore(destDataStoreId);
+        Host destHost = _hostDao.findById(destHostId);
+
+        if (destHost == null) {
+            throw new CloudRuntimeException("Destination host should not be 
null.");
+        }
+
+        Boolean storageCanCloneVolume = new Boolean(
+                
destPrimaryDataStore.getDriver().getCapabilities().get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString())
+        );
+
+        boolean computeZoneSupportsResign = 
computeZoneSupportsResign(destHost.getDataCenterId(), 
destHost.getHypervisorType());
+
+        AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<>();
+
+        if (storageCanCloneVolume && computeZoneSupportsResign) {
+            s_logger.debug("Storage " + destDataStoreId + " can support 
cloning using a cached template and host cluster can perform UUID resigning.");
+
+            TemplateInfo templateOnPrimary = 
destPrimaryDataStore.getTemplate(srcTemplateInfo.getId());
+
+            if (templateOnPrimary == null) {
+                templateOnPrimary = 
createManagedTemplateVolume(srcTemplateInfo, destPrimaryDataStore);
+
+                if (templateOnPrimary == null) {
+                    throw new CloudRuntimeException("Failed to create template 
" + srcTemplateInfo.getUniqueName() + " on primary storage: " + 
destDataStoreId);
+                }
+            }
+
+            // Copy the template to the template volume.
+            VMTemplateStoragePoolVO templatePoolRef = 
_tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), 
templateOnPrimary.getId());
+
+            if (templatePoolRef == null) {
+                throw new CloudRuntimeException("Failed to find template " +
+                    srcTemplateInfo.getUniqueName() + " in storage pool " +
+                    destPrimaryDataStore.getId()
+                );
+            }
+
+            if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) {
+                copyTemplateToManagedTemplateVolume(srcTemplateInfo, 
templateOnPrimary, templatePoolRef, destPrimaryDataStore, destHost);
+            }
+
+            // We have a template on primary storage. Clone it to new volume.
+            s_logger.debug("Creating a clone from template on primary storage 
" + destDataStoreId);
+            createManagedVolumeCloneTemplateAsync(volumeInfo, 
templateOnPrimary, destPrimaryDataStore, future);
+        } else {
+            s_logger.debug("Primary storage does not support cloning or no 
support for UUID resigning on the host side; copying the template normally");
+            createManagedVolumeCopyTemplateAsync(volumeInfo, 
destPrimaryDataStore, srcTemplateInfo, destHost, future);
+        }
 
         return future;
     }
 
+    private boolean computeZoneSupportsResign(long zoneId, HypervisorType 
hypervisorType) {
+        return getHost(zoneId, hypervisorType, true) != null;
+    }
+
+    private HostVO getHost(Long zoneId, HypervisorType hypervisorType, boolean 
computeClusterMustSupportResign) {
+        if (zoneId == null) {
+            throw new CloudRuntimeException("Zone ID cannot be null.");
+        }
+
+        List<? extends Cluster> clusters = mgr.searchForClusters(zoneId, new 
Long(0), Long.MAX_VALUE, hypervisorType.toString());
+
+        if (clusters == null) {
+            clusters = new ArrayList<>();
+        }
+
+        Collections.shuffle(clusters, new Random(System.nanoTime()));
+
+        clusters:
+        for (Cluster cluster : clusters) {
+            if (cluster.getAllocationState() == AllocationState.Enabled) {
+                List<HostVO> hosts = _hostDao.findByClusterId(cluster.getId());
+
+                if (hosts != null) {
+                    Collections.shuffle(hosts, new Random(System.nanoTime()));
+
+                    for (HostVO host : hosts) {
+                        if (host.getResourceState() == ResourceState.Enabled) {
+                            if (computeClusterMustSupportResign) {
+                                if 
(clusterDao.computeWhetherClusterSupportsResigning(cluster.getId())) {
+                                    return host;
+                                }
+                                else {
+                                    // no other host in the cluster in 
question should be able to satisfy our requirements here, so move on to the 
next cluster
+                                    continue clusters;
+                                }
+                            }
+                            else {
+                                return host;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
     @DB
     @Override
     public AsyncCallFuture<VolumeApiResult> 
createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo 
template) {
@@ -1332,7 +1706,8 @@ public class VolumeServiceImpl implements VolumeService {
             if (ep != null) {
                 VolumeVO volume = volDao.findById(volumeId);
                 PrimaryDataStore primaryDataStore = 
this.dataStoreMgr.getPrimaryDataStore(volume.getPoolId());
-                ResizeVolumeCommand resizeCmd = new 
ResizeVolumeCommand(volume.getPath(), new StorageFilerTO(primaryDataStore), 
volume.getSize(), newSize, true, instanceName);
+                ResizeVolumeCommand resizeCmd = new 
ResizeVolumeCommand(volume.getPath(), new StorageFilerTO(primaryDataStore),
+                        volume.getSize(), newSize, true, instanceName, 
primaryDataStore.isManaged(), volume.get_iScsiName());
 
                 answer = ep.sendMessage(resizeCmd);
             } else {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/api/solidfire-intg-test/pom.xml 
b/plugins/api/solidfire-intg-test/pom.xml
index a15d7b3..fa5302b 100644
--- a/plugins/api/solidfire-intg-test/pom.xml
+++ b/plugins/api/solidfire-intg-test/pom.xml
@@ -19,7 +19,7 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>cloud-plugin-api-solidfire-intg-test</artifactId>
-  <name>Apache CloudStack Plugin - API SolidFire</name>
+  <name>Apache CloudStack Plugin - API SolidFire Integration Testing</name>
   <parent>
     <groupId>org.apache.cloudstack</groupId>
     <artifactId>cloudstack-plugins</artifactId>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml
----------------------------------------------------------------------
diff --git 
a/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml
 
b/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml
index 1bab734..2fe875a 100644
--- 
a/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml
+++ 
b/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml
@@ -27,6 +27,8 @@
                       
http://www.springframework.org/schema/context/spring-context-3.0.xsd";
                       >
 
-      <bean id="apiSolidFireServiceImpl" 
class="org.apache.cloudstack.solidfire.ApiSolidFireServiceImpl"/>
+      <bean id="sfIntgTestUtil" 
class="org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil"/>
+      <bean id="solidFireIntegrationTestManagerImpl" 
class="org.apache.cloudstack.solidfire.SolidFireIntegrationTestManagerImpl"/>
+      <bean id="apiSolidFireIntegrationTestServiceImpl" 
class="org.apache.cloudstack.api.solidfire.ApiSolidFireIntegrationTestServiceImpl"
 />
 
 </beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java
 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java
new file mode 100644
index 0000000..5ff178a
--- /dev/null
+++ 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.solidfire;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.solidfire.ApiPathForVolumeResponse;
+import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil;
+
+@APICommand(name = "getPathForVolume", responseObject = 
ApiPathForVolumeResponse.class, description = "Get the path associated with the 
provided volume UUID",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class GetPathForVolumeCmd extends BaseCmd {
+    private static final Logger LOGGER = 
Logger.getLogger(GetPathForVolumeCmd.class.getName());
+    private static final String NAME = "getpathforvolumeresponse";
+
+    @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.STRING, 
description = "CloudStack Volume UUID", required = true)
+    private String _volumeUuid;
+
+    @Inject private SolidFireIntegrationTestUtil _util;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return NAME;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return _util.getAccountIdForVolumeUuid(_volumeUuid);
+    }
+
+    @Override
+    public void execute() {
+        LOGGER.info("'GetPathForVolumeIdCmd.execute' method invoked");
+
+        String pathForVolume = _util.getPathForVolumeUuid(_volumeUuid);
+
+        ApiPathForVolumeResponse response = new 
ApiPathForVolumeResponse(pathForVolume);
+
+        response.setResponseName(getCommandName());
+        response.setObjectName("apipathforvolume");
+
+        setResponseObject(response);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java
 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java
new file mode 100644
index 0000000..9bb8481
--- /dev/null
+++ 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.solidfire;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import 
org.apache.cloudstack.api.response.solidfire.ApiSolidFireAccountIdResponse;
+import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager;
+import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil;
+
+@APICommand(name = "getSolidFireAccountId", responseObject = 
ApiSolidFireAccountIdResponse.class, description = "Get SolidFire Account ID",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class GetSolidFireAccountIdCmd extends BaseCmd {
+    private static final Logger LOGGER = 
Logger.getLogger(GetSolidFireAccountIdCmd.class.getName());
+    private static final String NAME = "getsolidfireaccountidresponse";
+
+    @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.STRING, 
description = "CloudStack Account UUID", required = true)
+    private String csAccountUuid;
+    @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.STRING, 
description = "Storage Pool UUID", required = true)
+    private String storagePoolUuid;
+
+    @Inject private SolidFireIntegrationTestManager manager;
+    @Inject private SolidFireIntegrationTestUtil util;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return NAME;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return util.getAccountIdForAccountUuid(csAccountUuid);
+    }
+
+    @Override
+    public void execute() {
+        LOGGER.info("'GetSolidFireAccountIdCmd.execute' method invoked");
+
+        long sfAccountId = manager.getSolidFireAccountId(csAccountUuid, 
storagePoolUuid);
+
+        ApiSolidFireAccountIdResponse response = new 
ApiSolidFireAccountIdResponse(sfAccountId);
+
+        response.setResponseName(getCommandName());
+        response.setObjectName("apisolidfireaccountid");
+
+        this.setResponseObject(response);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java
 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java
new file mode 100644
index 0000000..5c15e01
--- /dev/null
+++ 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.solidfire;
+
+import com.cloud.user.Account;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import 
org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeAccessGroupIdResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager;
+import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil;
+
+@APICommand(name = "getSolidFireVolumeAccessGroupId", responseObject = 
ApiSolidFireVolumeAccessGroupIdResponse.class, description = "Get the SF Volume 
Access Group ID",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class GetSolidFireVolumeAccessGroupIdCmd extends BaseCmd {
+    private static final Logger LOGGER = 
Logger.getLogger(GetSolidFireVolumeAccessGroupIdCmd.class.getName());
+    private static final String NAME = 
"getsolidfirevolumeaccessgroupidresponse";
+
+    @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.STRING, 
description = "Cluster UUID", required = true)
+    private String clusterUuid;
+    @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.STRING, 
description = "Storage Pool UUID", required = true)
+    private String storagePoolUuid;
+
+    @Inject private SolidFireIntegrationTestManager manager;
+    @Inject private SolidFireIntegrationTestUtil util;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return NAME;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        Account account = CallContext.current().getCallingAccount();
+
+        if (account != null) {
+            return account.getId();
+        }
+
+        return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent 
this command to SYSTEM so ERROR events are tracked
+    }
+
+    @Override
+    public void execute() {
+        LOGGER.info("'GetSolidFireVolumeAccessGroupIdCmd.execute' method 
invoked");
+
+        long sfVagId = manager.getSolidFireVolumeAccessGroupId(clusterUuid, 
storagePoolUuid);
+
+        ApiSolidFireVolumeAccessGroupIdResponse response = new 
ApiSolidFireVolumeAccessGroupIdResponse(sfVagId);
+
+        response.setResponseName(getCommandName());
+        response.setObjectName("apisolidfirevolumeaccessgroupid");
+
+        this.setResponseObject(response);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java
 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java
new file mode 100644
index 0000000..d7c8acf
--- /dev/null
+++ 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java
@@ -0,0 +1,70 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.solidfire;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import 
org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeSizeResponse;
+import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager;
+import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil;
+
+@APICommand(name = "getSolidFireVolumeSize", responseObject = 
ApiSolidFireVolumeSizeResponse.class, description = "Get the SF volume size 
including Hypervisor Snapshot Reserve",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class GetSolidFireVolumeSizeCmd extends BaseCmd {
+    private static final Logger LOGGER = 
Logger.getLogger(GetSolidFireVolumeSizeCmd.class.getName());
+    private static final String NAME = "getsolidfirevolumesizeresponse";
+
+    @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.STRING, 
description = "Volume UUID", required = true)
+    private String volumeUuid;
+
+    @Inject private SolidFireIntegrationTestManager manager;
+    @Inject private SolidFireIntegrationTestUtil util;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return NAME;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return util.getAccountIdForVolumeUuid(volumeUuid);
+    }
+
+    @Override
+    public void execute() {
+        LOGGER.info("'GetSolidFireVolumeSizeCmd.execute' method invoked");
+
+        long sfVolumeSize = manager.getSolidFireVolumeSize(volumeUuid);
+
+        ApiSolidFireVolumeSizeResponse response = new 
ApiSolidFireVolumeSizeResponse(sfVolumeSize);
+
+        response.setResponseName(getCommandName());
+        response.setObjectName("apisolidfirevolumesize");
+
+        this.setResponseObject(response);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java
 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java
new file mode 100644
index 0000000..5b9ce37
--- /dev/null
+++ 
b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java
@@ -0,0 +1,73 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.solidfire;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ListResponse;
+import 
org.apache.cloudstack.api.response.solidfire.ApiVolumeSnapshotDetailsResponse;
+import org.apache.cloudstack.api.response.solidfire.ApiVolumeiScsiNameResponse;
+import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil;
+
+@APICommand(name = "getVolumeSnapshotDetails", responseObject = 
ApiVolumeiScsiNameResponse.class, description = "Get Volume Snapshot Details",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+
+public class GetVolumeSnapshotDetailsCmd extends BaseCmd {
+    private static final Logger LOGGER = 
Logger.getLogger(GetVolumeSnapshotDetailsCmd.class.getName());
+    private static final String NAME = "getvolumesnapshotdetailsresponse";
+
+    @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.STRING, 
description = "CloudStack Snapshot UUID", required = true)
+    private String snapshotUuid;
+
+    @Inject private SolidFireIntegrationTestUtil util;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return NAME;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return util.getAccountIdForSnapshotUuid(snapshotUuid);
+    }
+
+    @Override
+    public void execute() {
+        LOGGER.info("'" + GetVolumeSnapshotDetailsCmd.class.getSimpleName() + 
".execute' method invoked");
+
+        List<ApiVolumeSnapshotDetailsResponse> responses = 
util.getSnapshotDetails(snapshotUuid);
+
+        ListResponse<ApiVolumeSnapshotDetailsResponse> listReponse = new 
ListResponse<>();
+
+        listReponse.setResponses(responses);
+        listReponse.setResponseName(getCommandName());
+        listReponse.setObjectName("apivolumesnapshotdetails");
+
+        this.setResponseObject(listReponse);
+    }
+}
\ No newline at end of file

Reply via email to