You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ml...@apache.org on 2014/03/08 01:22:49 UTC

[16/50] [abbrv] git commit: updated refs/heads/resize-root to 0eb9967

CLOUDSTACK-6170


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

Branch: refs/heads/resize-root
Commit: b06e66c50a43337d28c4157c396e39adb488902f
Parents: bbaec7b
Author: Mike Tutkowski <mi...@solidfire.com>
Authored: Thu Feb 27 13:20:56 2014 -0700
Committer: Mike Tutkowski <mi...@solidfire.com>
Committed: Wed Mar 5 13:55:12 2014 -0700

----------------------------------------------------------------------
 .../offering/CreateServiceOfferingCmd.java      |  32 ++++
 .../storage/to/PrimaryDataStoreTO.java          |  18 ++
 .../api/storage/DataMotionService.java          |   2 +
 .../api/storage/DataMotionStrategy.java         |   2 +
 .../api/storage/PrimaryDataStoreInfo.java       |  18 ++
 .../subsystem/api/storage/VolumeService.java    |   6 +-
 .../orchestration/VolumeOrchestrator.java       |  19 +-
 .../motion/AncientDataMotionStrategy.java       |  25 ++-
 .../storage/motion/DataMotionServiceImpl.java   |   9 +-
 .../storage/image/store/TemplateObject.java     |  15 ++
 .../storage/test/MockStorageMotionStrategy.java |   5 +
 .../storage/datastore/PrimaryDataStoreImpl.java |  20 ++-
 .../datastore/provider/DefaultHostListener.java |   2 +-
 .../storage/volume/VolumeServiceImpl.java       | 156 ++++++++++++++++-
 .../motion/SimulatorDataMotionStrategy.java     |   5 +
 .../motion/VmwareStorageMotionStrategy.java     |   5 +
 .../xen/resource/CitrixResourceBase.java        |  11 +-
 .../xen/resource/XenServerStorageProcessor.java | 126 ++++++++++----
 .../motion/XenServerStorageMotionStrategy.java  |   5 +
 .../configuration/ConfigurationManagerImpl.java |  49 +++++-
 ui/scripts/configuration.js                     | 174 +++++++++++++++++--
 21 files changed, 638 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
index 1d8cbff..4143c2e 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
@@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.Parameter;
 import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.BaseCmd.CommandType;
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
 
@@ -117,6 +118,21 @@ public class CreateServiceOfferingCmd extends BaseCmd {
     @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "io requests write rate of the disk offering")
     private Long iopsWriteRate;
 
+    @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "whether compute offering iops is custom or not")
+    private Boolean customizedIops;
+
+    @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "min iops of the compute offering")
+    private Long minIops;
+
+    @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "max iops of the compute offering")
+    private Long maxIops;
+
+    @Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE,
+            type = CommandType.INTEGER,
+            required = false,
+            description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)")
+    private Integer hypervisorSnapshotReserve;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -215,6 +231,22 @@ public class CreateServiceOfferingCmd extends BaseCmd {
         return iopsWriteRate;
     }
 
+    public Boolean isCustomizedIops() {
+        return customizedIops;
+    }
+
+    public Long getMinIops() {
+        return minIops;
+    }
+
+    public Long getMaxIops() {
+        return maxIops;
+    }
+
+    public Integer getHypervisorSnapshotReserve() {
+        return hypervisorSnapshotReserve;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
index 01c05f0..6a5d679 100644
--- a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
+++ b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
@@ -16,6 +16,8 @@
 // under the License.
 package org.apache.cloudstack.storage.to;
 
+import java.util.Map;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 
 import com.cloud.agent.api.to.DataStoreTO;
@@ -23,6 +25,16 @@ import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.Storage.StoragePoolType;
 
 public class PrimaryDataStoreTO implements DataStoreTO {
+    public static final String MANAGED = PrimaryDataStore.MANAGED;
+    public static final String STORAGE_HOST = PrimaryDataStore.STORAGE_HOST;
+    public static final String MANAGED_STORE_TARGET = PrimaryDataStore.MANAGED_STORE_TARGET;
+    public static final String MANAGED_STORE_TARGET_ROOT_VOLUME = PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME;
+    public static final String CHAP_INITIATOR_USERNAME = PrimaryDataStore.CHAP_INITIATOR_USERNAME;
+    public static final String CHAP_INITIATOR_SECRET = PrimaryDataStore.CHAP_INITIATOR_SECRET;
+    public static final String CHAP_TARGET_USERNAME = PrimaryDataStore.CHAP_TARGET_USERNAME;
+    public static final String CHAP_TARGET_SECRET = PrimaryDataStore.CHAP_TARGET_SECRET;
+    public static final String VOLUME_SIZE = PrimaryDataStore.VOLUME_SIZE;
+
     private final String uuid;
     private final String name;
     private String type;
@@ -32,6 +44,7 @@ public class PrimaryDataStoreTO implements DataStoreTO {
     private String path;
     private int port;
     private final String url;
+    private Map<String, String> details;
 
     public PrimaryDataStoreTO(PrimaryDataStore dataStore) {
         this.uuid = dataStore.getUuid();
@@ -42,6 +55,7 @@ public class PrimaryDataStoreTO implements DataStoreTO {
         this.setPath(dataStore.getPath());
         this.setPort(dataStore.getPort());
         this.url = dataStore.getUri();
+        this.details = dataStore.getDetails();
     }
 
     public long getId() {
@@ -58,6 +72,10 @@ public class PrimaryDataStoreTO implements DataStoreTO {
         return this.url;
     }
 
+    public Map<String, String> getDetails() {
+        return this.details;
+    }
+
     public String getName() {
         return this.name;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java
index f20e188..a8f8b30 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java
@@ -26,6 +26,8 @@ import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.host.Host;
 
 public interface DataMotionService {
+    void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
+
     void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
 
     void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java
index 39a257d..b5601e9 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java
@@ -30,6 +30,8 @@ public interface DataMotionStrategy {
 
     StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost);
 
+    Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);
+
     Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
 
     Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java
index 19893d0..bb9f2da 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cloudstack.engine.subsystem.api.storage;
 
+import java.util.Map;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat;
 
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
@@ -25,6 +27,16 @@ import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.StoragePool;
 
 public interface PrimaryDataStoreInfo extends StoragePool {
+    static final String MANAGED = "managed";
+    static final String STORAGE_HOST= "storageHost";
+    static final String MANAGED_STORE_TARGET = "managedStoreTarget";
+    static final String MANAGED_STORE_TARGET_ROOT_VOLUME = "managedStoreTargetRootVolume";
+    static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername";
+    static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret";
+    static final String CHAP_TARGET_USERNAME = "chapTargetUsername";
+    static final String CHAP_TARGET_SECRET = "chapTargetSecret";
+    static final String VOLUME_SIZE = "volumeSize";
+
     boolean isHypervisorSupported(HypervisorType hypervisor);
 
     boolean isLocalStorageSupported();
@@ -37,5 +49,11 @@ public interface PrimaryDataStoreInfo extends StoragePool {
     @Override
     StoragePoolType getPoolType();
 
+    boolean isManaged();
+
+    void setDetails(Map<String, String> details);
+
+    Map<String, String> getDetails();
+
     PrimaryDataStoreLifeCycle getLifeCycle();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
index 3f676ae..b6e6106 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java
@@ -78,7 +78,11 @@ public interface VolumeService {
 
     VolumeEntity getVolumeEntity(long volumeId);
 
-    AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template);
+    AsyncCallFuture<VolumeApiResult> createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId,
+            TemplateInfo srcTemplateInfo, long destHostId);
+
+    AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId,
+            TemplateInfo template);
 
     AsyncCallFuture<VolumeApiResult> copyVolume(VolumeInfo srcVolume, DataStore destStore);
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/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 d3eda8a..4d0c787 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.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService;
@@ -1113,7 +1114,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
                 future = volService.createVolumeAsync(volume, destPool);
             } else {
                 TemplateInfo templ = tmplFactory.getTemplate(templateId, DataStoreRole.Image);
-                future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ);
+
+                PrimaryDataStore primaryDataStore = (PrimaryDataStore)destPool;
+
+                if (primaryDataStore.isManaged()) {
+                    DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId());
+                    HypervisorType hyperType = vm.getVirtualMachine().getHypervisorType();
+
+                    // update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage)
+                    updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType);
+
+                    long hostId = vm.getVirtualMachine().getHostId();
+
+                    future = volService.createManagedStorageAndVolumeFromTemplateAsync(volume, destPool.getId(), templ, hostId);
+                }
+                else {
+                    future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ);
+                }
             }
             VolumeApiResult result = null;
             try {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
index 0556479..df81199 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
@@ -46,6 +46,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.storage.RemoteHostEndPoint;
 import org.apache.cloudstack.storage.command.CopyCommand;
 import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
 
@@ -94,15 +95,17 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
 
     protected boolean needCacheStorage(DataObject srcData, DataObject destData) {
         DataTO srcTO = srcData.getTO();
-        DataTO destTO = destData.getTO();
         DataStoreTO srcStoreTO = srcTO.getDataStore();
-        DataStoreTO destStoreTO = destTO.getDataStore();
+
         if (srcStoreTO instanceof NfsTO || srcStoreTO.getRole() == DataStoreRole.ImageCache) {
             //||
             //    (srcStoreTO instanceof PrimaryDataStoreTO && ((PrimaryDataStoreTO)srcStoreTO).getPoolType() == StoragePoolType.NetworkFilesystem)) {
             return false;
         }
 
+        DataTO destTO = destData.getTO();
+        DataStoreTO destStoreTO = destTO.getDataStore();
+
         if (destStoreTO instanceof NfsTO || destStoreTO.getRole() == DataStoreRole.ImageCache) {
             return false;
         }
@@ -147,7 +150,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         return selectedScope;
     }
 
-    protected Answer copyObject(DataObject srcData, DataObject destData) {
+    protected Answer copyObject(DataObject srcData, DataObject destData, Host destHost) {
         String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
         int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
         Answer answer = null;
@@ -160,7 +163,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
             }
 
             CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), destData.getTO(), _primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
-            EndPoint ep = selector.select(srcForCopy, destData);
+            EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcForCopy, destData);
             if (ep == null) {
                 String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
                 s_logger.error(errMsg);
@@ -193,6 +196,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         }
     }
 
+    protected Answer copyObject(DataObject srcData, DataObject destData) {
+        return copyObject(srcData, destData, null);
+    }
+
     protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) {
         DataObject leafData = null;
         DataStore store = cacheMgr.getCacheStorage(snapshot, scope);
@@ -392,8 +399,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         return answer;
     }
 
+    // Note: destHost is currently only used if the copyObject method is invoked
     @Override
-    public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
+    public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
         Answer answer = null;
         String errMsg = null;
         try {
@@ -416,7 +424,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
             } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) {
                 answer = copySnapshot(srcData, destData);
             } else {
-                answer = copyObject(srcData, destData);
+                answer = copyObject(srcData, destData, destHost);
             }
 
             if (answer != null && !answer.getResult()) {
@@ -432,6 +440,11 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         return null;
     }
 
+    @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
+        return copyAsync(srcData, destData, null, callback);
+    }
+
     @DB
     protected Answer createTemplateFromSnapshot(DataObject srcData, DataObject destData) {
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
index 37e8baa..eed1e08 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
@@ -46,7 +46,7 @@ public class DataMotionServiceImpl implements DataMotionService {
     StorageStrategyFactory storageStrategyFactory;
 
     @Override
-    public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
+    public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
         if (srcData.getDataStore() == null || destData.getDataStore() == null) {
             throw new CloudRuntimeException("can't find data store");
         }
@@ -65,7 +65,12 @@ public class DataMotionServiceImpl implements DataMotionService {
                 destData.getType().name() + " '" + destData.getUuid() + "'");
         }
 
-        strategy.copyAsync(srcData, destData, callback);
+        strategy.copyAsync(srcData, destData, destHost, callback);
+    }
+
+    @Override
+    public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
+        copyAsync(srcData, destData, null, callback);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/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 8db21cc..34db481 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
@@ -30,6 +30,7 @@ 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.ObjectInDataStoreStateMachine.Event;
+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;
@@ -299,6 +300,20 @@ public class TemplateObject implements TemplateInfo {
         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();
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java
index 2a11e13..accfeb5 100644
--- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java
+++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java
@@ -58,6 +58,11 @@ public class MockStorageMotionStrategy implements DataMotionStrategy {
     }
 
     @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
         CopyCmdAnswer answer = null;
         DataTO data = null;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/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 649e689..4fb37e2 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
@@ -20,6 +20,7 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import javax.inject.Inject;
 
@@ -67,6 +68,7 @@ import com.cloud.utils.storage.encoding.EncodingType;
 
 public class PrimaryDataStoreImpl implements PrimaryDataStore {
     private static final Logger s_logger = Logger.getLogger(PrimaryDataStoreImpl.class);
+
     protected PrimaryDataStoreDriver driver;
     protected StoragePoolVO pdsv;
     @Inject
@@ -86,6 +88,7 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
 
     @Inject
     private VolumeDao volumeDao;
+    private Map<String, String> _details;
 
     public PrimaryDataStoreImpl() {
 
@@ -136,6 +139,16 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
     }
 
     @Override
+    public void setDetails(Map<String, String> details) {
+        _details = details;
+    }
+
+    @Override
+    public Map<String, String> getDetails() {
+        return _details;
+    }
+
+    @Override
     public String getUri() {
         String path = pdsv.getPath().replaceFirst("/*", "");
         StringBuilder builder = new StringBuilder();
@@ -223,9 +236,14 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
     }
 
     @Override
+    public boolean isManaged() {
+        return pdsv.isManaged();
+    }
+
+    @Override
     public DataObject create(DataObject obj) {
         // create template on primary storage
-        if (obj.getType() == DataObjectType.TEMPLATE) {
+        if (obj.getType() == DataObjectType.TEMPLATE && !isManaged()) {
             try {
                 String templateIdPoolIdString = "templateId:" + obj.getId() + "poolId:" + getId();
                 VMTemplateStoragePoolVO templateStoragePoolRef;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
index 4838bf6..fffd1e8 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
@@ -85,7 +85,7 @@ public class DefaultHostListener implements HypervisorHostListener {
         poolVO.setCapacityBytes(mspAnswer.getPoolInfo().getCapacityBytes());
         primaryStoreDao.update(pool.getId(), poolVO);
 
-        s_logger.info("Connection established between " + pool + " host + " + hostId);
+        s_logger.info("Connection established between storage pool " + pool + " and host " + hostId);
         return true;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/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 fa0fd95..d47ee27 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
@@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.volume;
 
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -40,6 +41,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
 import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
 import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
@@ -60,6 +62,7 @@ import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.cloudstack.storage.to.TemplateObjectTO;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.storage.ListVolumeAnswer;
@@ -72,6 +75,7 @@ import com.cloud.event.EventTypes;
 import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.ResourceAllocationException;
+import com.cloud.host.dao.HostDao;
 import com.cloud.host.Host;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ScopeType;
@@ -122,6 +126,8 @@ public class VolumeServiceImpl implements VolumeService {
     VolumeDao _volumeDao;
     @Inject
     EndPointSelector _epSelector;
+    @Inject
+    HostDao _hostDao;
 
     public VolumeServiceImpl() {
     }
@@ -349,6 +355,39 @@ public class VolumeServiceImpl implements VolumeService {
         return null;
     }
 
+    private class ManagedCreateBaseImageContext<T> extends AsyncRpcContext<T> {
+        private final VolumeInfo _volumeInfo;
+        private final PrimaryDataStore _primaryDataStore;
+        private final TemplateInfo _templateInfo;
+        private final AsyncCallFuture<VolumeApiResult> _future;
+
+        public ManagedCreateBaseImageContext(AsyncCompletionCallback<T> callback, VolumeInfo volumeInfo,
+                PrimaryDataStore primaryDatastore, TemplateInfo templateInfo, AsyncCallFuture<VolumeApiResult> future) {
+            super(callback);
+
+            _volumeInfo = volumeInfo;
+            _primaryDataStore = primaryDatastore;
+            _templateInfo = templateInfo;
+            _future = future;
+        }
+
+        public VolumeInfo getVolumeInfo() {
+            return _volumeInfo;
+        }
+
+        public PrimaryDataStore getPrimaryDataStore() {
+            return _primaryDataStore;
+        }
+
+        public TemplateInfo getTemplateInfo() {
+            return _templateInfo;
+        }
+
+        public AsyncCallFuture<VolumeApiResult> getFuture() {
+            return _future;
+        }
+    }
+
     class CreateBaseImageContext<T> extends AsyncRpcContext<T> {
         private final VolumeInfo volume;
         private final PrimaryDataStore dataStore;
@@ -411,7 +450,6 @@ public class VolumeServiceImpl implements VolumeService {
 
     @DB
     protected void createBaseImageAsync(VolumeInfo volume, PrimaryDataStore dataStore, TemplateInfo template, AsyncCallFuture<VolumeApiResult> future) {
-
         DataObject templateOnPrimaryStoreObj = dataStore.create(template);
 
         VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(dataStore.getId(), template.getId());
@@ -476,6 +514,37 @@ public class VolumeServiceImpl implements VolumeService {
         return;
     }
 
+    protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback,
+            ManagedCreateBaseImageContext<VolumeApiResult> context) {
+        CopyCommandResult result = callback.getResult();
+        VolumeInfo volumeInfo = context.getVolumeInfo();
+        VolumeApiResult res = new VolumeApiResult(volumeInfo);
+
+        if (result.isSuccess()) {
+            // volumeInfo.processEvent(Event.OperationSuccessed, result.getAnswer());
+
+            VolumeVO volume = volDao.findById(volumeInfo.getId());
+            CopyCmdAnswer answer = (CopyCmdAnswer)result.getAnswer();
+            TemplateObjectTO templateObjectTo = (TemplateObjectTO)answer.getNewData();
+
+            volume.setPath(templateObjectTo.getPath());
+            volume.setFormat(templateObjectTo.getFormat());
+
+            volDao.update(volume.getId(), volume);
+        }
+        else {
+            volumeInfo.processEvent(Event.DestroyRequested);
+
+            res.setResult(result.getResult());
+        }
+
+        AsyncCallFuture<VolumeApiResult> future = context.getFuture();
+
+        future.complete(res);
+
+        return null;
+    }
+
     @DB
     protected Void copyBaseImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback, CreateBaseImageContext<VolumeApiResult> context) {
         CopyCommandResult result = callback.getResult();
@@ -577,6 +646,91 @@ 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);
+
+        if (destHost == null) {
+            throw new CloudRuntimeException("Destinatin host should not be null.");
+        }
+
+        AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
+
+        try {
+            // must call driver to have a volume created
+            AsyncCallFuture<VolumeApiResult> createVolumeFuture = createVolumeAsync(volumeInfo, destPrimaryDataStore);
+
+            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);
+
+            connectVolumeToHost(volumeInfo, destHost, destPrimaryDataStore);
+
+            ManagedCreateBaseImageContext<CreateCmdResult> context = new ManagedCreateBaseImageContext<CreateCmdResult>(null, volumeInfo,
+                    destPrimaryDataStore, 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());
+            // 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);
+
+            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());
+            }
+
+            destPrimaryDataStore.setDetails(details);
+
+            motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller);
+        }
+        catch (Throwable t) {
+            String errMsg = t.toString();
+
+            volumeInfo.processEvent(Event.DestroyRequested);
+
+            disconnectVolumeFromHost(volumeInfo, destHost, destPrimaryDataStore);
+
+            try {
+                AsyncCallFuture<VolumeApiResult> expungeVolumeFuture = expungeVolumeAsync(volumeInfo);
+
+                VolumeApiResult expungeVolumeResult = expungeVolumeFuture.get();
+
+                if (expungeVolumeResult.isFailed()) {
+                    errMsg += " : Failed to expunge a volume that was created";
+                }
+            }
+            catch (Exception ex) {
+                errMsg += " : " + ex.getMessage();
+            }
+
+            VolumeApiResult result = new VolumeApiResult(volumeInfo);
+
+            result.setResult(errMsg);
+
+            future.complete(result);
+        }
+
+        return future;
+    }
+
     @DB
     @Override
     public AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java b/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java
index 3eb2cf6..4090962 100644
--- a/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java
+++ b/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java
@@ -43,6 +43,11 @@ public class SimulatorDataMotionStrategy implements DataMotionStrategy {
     }
 
     @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
         CopyCommandResult result = new CopyCommandResult("something", null);
         callback.complete(result);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
index 09f1068..24efde7 100644
--- a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
+++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
@@ -86,6 +86,11 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy {
     }
 
     @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
         CopyCommandResult result = new CopyCommandResult(null, null);
         result.setResult("Unsupported operation requested for copying data.");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index c0d691a..2b441e5 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -1869,18 +1869,23 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         return prepareManagedStorage(conn, details, null, vdiNameLabel);
     }
 
-    protected VDI prepareManagedStorage(Connection conn, Map<String, String> details, String path, String vdiNameLabel) throws Exception {
+    protected SR prepareManagedSr(Connection conn, Map<String, String> details) {
         String iScsiName = details.get(DiskTO.IQN);
         String storageHost = details.get(DiskTO.STORAGE_HOST);
         String chapInitiatorUsername = details.get(DiskTO.CHAP_INITIATOR_USERNAME);
         String chapInitiatorSecret = details.get(DiskTO.CHAP_INITIATOR_SECRET);
-        Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE));
 
-        SR sr = getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true);
+        return getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true);
+    }
+
+    protected VDI prepareManagedStorage(Connection conn, Map<String, String> details, String path, String vdiNameLabel) throws Exception {
+        SR sr = prepareManagedSr(conn, details);
 
         VDI vdi = getVDIbyUuid(conn, path, false);
 
         if (vdi == null) {
+            Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE));
+
             vdi = createVdi(sr, vdiNameLabel, volumeSize);
         }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
index f2abff7..e512046 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java
@@ -59,6 +59,7 @@ import org.apache.cloudstack.storage.command.ForgetObjectCmd;
 import org.apache.cloudstack.storage.command.IntroduceObjectAnswer;
 import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
 import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol;
+import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
 import org.apache.cloudstack.storage.to.SnapshotObjectTO;
 import org.apache.cloudstack.storage.to.TemplateObjectTO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
@@ -887,58 +888,121 @@ public class XenServerStorageProcessor implements StorageProcessor {
 
     @Override
     public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) {
-        DataTO srcData = cmd.getSrcTO();
-        DataTO destData = cmd.getDestTO();
+        DataTO srcDataTo = cmd.getSrcTO();
+        DataTO destDataTo = cmd.getDestTO();
         int wait = cmd.getWait();
-        DataStoreTO srcStore = srcData.getDataStore();
+        DataStoreTO srcDataStoreTo = srcDataTo.getDataStore();
+
         try {
-            if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) {
-                NfsTO srcImageStore = (NfsTO)srcStore;
-                TemplateObjectTO srcTemplate = (TemplateObjectTO)srcData;
+            if ((srcDataStoreTo instanceof NfsTO) && (srcDataTo.getObjectType() == DataObjectType.TEMPLATE)) {
+                NfsTO srcImageStore = (NfsTO)srcDataStoreTo;
+                TemplateObjectTO srcTemplateObjectTo = (TemplateObjectTO)srcDataTo;
                 String storeUrl = srcImageStore.getUrl();
-
                 URI uri = new URI(storeUrl);
-                String tmplpath = uri.getHost() + ":" + uri.getPath() + "/" + srcData.getPath();
-                String poolName = destData.getDataStore().getUuid();
+                String tmplPath = uri.getHost() + ":" + uri.getPath() + "/" + srcDataTo.getPath();
+                DataStoreTO destDataStoreTo = destDataTo.getDataStore();
+
+                boolean managed = false;
+                String storageHost = null;
+                String managedStoragePoolName = null;
+                String managedStoragePoolRootVolumeName = null;
+                String managedStoragePoolRootVolumeSize = null;
+                String chapInitiatorUsername = null;
+                String chapInitiatorSecret = null;
+
+                if (destDataStoreTo instanceof PrimaryDataStoreTO) {
+                    PrimaryDataStoreTO destPrimaryDataStoreTo = (PrimaryDataStoreTO)destDataStoreTo;
+
+                    Map<String, String> details = destPrimaryDataStoreTo.getDetails();
+
+                    if (details != null) {
+                        managed = Boolean.parseBoolean(details.get(PrimaryDataStoreTO.MANAGED));
+
+                        if (managed) {
+                            storageHost = details.get(PrimaryDataStoreTO.STORAGE_HOST);
+                            managedStoragePoolName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET);
+                            managedStoragePoolRootVolumeName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET_ROOT_VOLUME);
+                            managedStoragePoolRootVolumeSize = details.get(PrimaryDataStoreTO.VOLUME_SIZE);
+                            chapInitiatorUsername = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_USERNAME);
+                            chapInitiatorSecret = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_SECRET);
+                        }
+                    }
+                }
+
                 Connection conn = hypervisorResource.getConnection();
 
-                SR poolsr = null;
-                Set<SR> srs = SR.getByNameLabel(conn, poolName);
-                if (srs.size() != 1) {
-                    String msg = "There are " + srs.size() + " SRs with same name: " + poolName;
-                    s_logger.warn(msg);
-                    return new CopyCmdAnswer(msg);
-                } else {
-                    poolsr = srs.iterator().next();
+                final SR sr;
+
+                if (managed) {
+                    Map<String, String> details = new HashMap<String, String>();
+
+                    details.put(DiskTO.STORAGE_HOST, storageHost);
+                    details.put(DiskTO.IQN, managedStoragePoolName);
+                    details.put(DiskTO.VOLUME_SIZE, managedStoragePoolRootVolumeSize);
+                    details.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInitiatorUsername);
+                    details.put(DiskTO.CHAP_INITIATOR_SECRET, chapInitiatorSecret);
+
+                    sr = hypervisorResource.prepareManagedSr(conn, details);
+                }
+                else {
+                    String srName = destDataStoreTo.getUuid();
+                    Set<SR> srs = SR.getByNameLabel(conn, srName);
+
+                    if (srs.size() != 1) {
+                        String msg = "There are " + srs.size() + " SRs with same name: " + srName;
+
+                        s_logger.warn(msg);
+
+                        return new CopyCmdAnswer(msg);
+                    } else {
+                        sr = srs.iterator().next();
+                    }
+                }
+
+                String srUuid = sr.getUuid(conn);
+                String tmplUuid = copy_vhd_from_secondarystorage(conn, tmplPath, srUuid, wait);
+                VDI tmplVdi = getVDIbyUuid(conn, tmplUuid);
+
+                final String uuidToReturn;
+
+                if (managed) {
+                    uuidToReturn = tmplUuid;
+
+                    tmplVdi.setNameLabel(conn, managedStoragePoolRootVolumeName);
+                }
+                else {
+                    VDI snapshotVdi = tmplVdi.snapshot(conn, new HashMap<String, String>());
+
+                    uuidToReturn = snapshotVdi.getUuid(conn);
+
+                    snapshotVdi.setNameLabel(conn, "Template " + srcTemplateObjectTo.getName());
+
+                    tmplVdi.destroy(conn);
                 }
-                String pUuid = poolsr.getUuid(conn);
-                boolean isISCSI = IsISCSI(poolsr.getType(conn));
-                String uuid = copy_vhd_from_secondarystorage(conn, tmplpath, pUuid, wait);
-                VDI tmpl = getVDIbyUuid(conn, uuid);
-                VDI snapshotvdi = tmpl.snapshot(conn, new HashMap<String, String>());
-                String snapshotUuid = snapshotvdi.getUuid(conn);
-                snapshotvdi.setNameLabel(conn, "Template " + srcTemplate.getName());
-                String parentuuid = getVhdParent(conn, pUuid, snapshotUuid, isISCSI);
-                VDI parent = getVDIbyUuid(conn, parentuuid);
-                Long phySize = parent.getPhysicalUtilisation(conn);
-                tmpl.destroy(conn);
-                poolsr.scan(conn);
+
+                sr.scan(conn);
+
                 try {
                     Thread.sleep(5000);
                 } catch (Exception e) {
                 }
 
                 TemplateObjectTO newVol = new TemplateObjectTO();
-                newVol.setUuid(snapshotvdi.getUuid(conn));
-                newVol.setPath(newVol.getUuid());
+
+                newVol.setUuid(uuidToReturn);
+                newVol.setPath(uuidToReturn);
                 newVol.setFormat(ImageFormat.VHD);
+
                 return new CopyCmdAnswer(newVol);
             }
         } catch (Exception e) {
             String msg = "Catch Exception " + e.getClass().getName() + " for template + " + " due to " + e.toString();
+
             s_logger.warn(msg, e);
+
             return new CopyCmdAnswer(msg);
         }
+
         return new CopyCmdAnswer("not implemented yet");
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
index e8217f7..975deec 100644
--- a/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
+++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
@@ -91,6 +91,11 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy {
     }
 
     @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
         CopyCommandResult result = new CopyCommandResult(null, null);
         result.setResult("Unsupported operation requested for copying data.");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index 283aeea..08cc5a6 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -2016,15 +2016,54 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
         return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), localStorageRequired,
                 offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(),
-                cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate());
+                cmd.isCustomizedIops(), cmd.getMinIops(), cmd.getMaxIops(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(),
+                cmd.getHypervisorSnapshotReserve());
     }
 
     protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, Integer speed,
             String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag,
-            Integer networkRate, String deploymentPlanner, Map<String, String> details, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
+            Integer networkRate, String deploymentPlanner, Map<String, String> details, Boolean isCustomizedIops, Long minIops, Long maxIops,
+            Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) {
         tags = StringUtils.cleanupTags(tags);
         ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired,
                 false, tags, isSystem, vmType, domainId, hostTag, deploymentPlanner);
+
+        if (isCustomizedIops != null) {
+            bytesReadRate = null;
+            bytesWriteRate = null;
+            iopsReadRate = null;
+            iopsWriteRate = null;
+
+            if (isCustomizedIops) {
+                minIops = null;
+                maxIops = null;
+            } else {
+                if (minIops == null && maxIops == null) {
+                    minIops = 0L;
+                    maxIops = 0L;
+                } else {
+                    if (minIops == null || minIops <= 0) {
+                        throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
+                    }
+
+                    if (maxIops == null) {
+                        maxIops = 0L;
+                    }
+
+                    if (minIops > maxIops) {
+                        throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
+                    }
+                }
+            }
+        } else {
+            minIops = null;
+            maxIops = null;
+        }
+
+        offering.setCustomizedIops(isCustomizedIops);
+        offering.setMinIops(minIops);
+        offering.setMaxIops(maxIops);
+
         if ((bytesReadRate != null) && (bytesReadRate > 0))
             offering.setBytesReadRate(bytesReadRate);
         if ((bytesWriteRate != null) && (bytesWriteRate > 0))
@@ -2034,6 +2073,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         if ((iopsWriteRate != null) && (iopsWriteRate > 0))
             offering.setIopsWriteRate(iopsWriteRate);
 
+        if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) {
+            throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0.");
+        }
+
+        offering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
+
         if ((offering = _serviceOfferingDao.persist(offering)) != null) {
             if (details != null) {
                 List<ServiceOfferingDetailsVO> detailsVO = new ArrayList<ServiceOfferingDetailsVO>();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b06e66c5/ui/scripts/configuration.js
----------------------------------------------------------------------
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index 869b876..cb54c31 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -136,6 +136,113 @@
                                             number: true
                                         }
                                     },
+                                    qosType: {
+                                        label: 'label.qos.type',
+                                        docID: 'helpDiskOfferingQoSType',
+                                        select: function(args) {
+                                            var items = [];
+                                            items.push({
+                                                id: '',
+                                                description: ''
+                                            });
+                                            items.push({
+                                                id: 'hypervisor',
+                                                description: 'hypervisor'
+                                            });
+                                            items.push({
+                                                id: 'storage',
+                                                description: 'storage'
+                                            });
+                                            args.response.success({
+                                                data: items
+                                            });
+
+                                            args.$select.change(function() {
+                                                var $form = $(this).closest('form');
+                                                var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]');
+                                                var $minIops = $form.find('.form-item[rel=minIops]');
+                                                var $maxIops = $form.find('.form-item[rel=maxIops]');
+                                                var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]');
+                                                var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]');
+                                                var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]');
+                                                var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]');
+                                                var $diskIopsWriteRate = $form.find('.form-item[rel=diskIopsWriteRate]');
+
+                                                var qosId = $(this).val();
+
+                                                if (qosId == 'storage') { // Storage QoS
+                                                    $diskBytesReadRate.hide();
+                                                    $diskBytesWriteRate.hide();
+                                                    $diskIopsReadRate.hide();
+                                                    $diskIopsWriteRate.hide();
+
+                                                    $isCustomizedIops.css('display', 'inline-block');
+
+                                                    if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) {
+                                                        $minIops.hide();
+                                                        $maxIops.hide();
+                                                    } else {
+                                                        $minIops.css('display', 'inline-block');
+                                                        $maxIops.css('display', 'inline-block');
+                                                    }
+
+                                                    $hypervisorSnapshotReserve.css('display', 'inline-block');
+                                                } else if (qosId == 'hypervisor') { // Hypervisor Qos
+                                                    $isCustomizedIops.hide();
+                                                    $minIops.hide();
+                                                    $maxIops.hide();
+                                                    $hypervisorSnapshotReserve.hide();
+
+                                                    $diskBytesReadRate.css('display', 'inline-block');
+                                                    $diskBytesWriteRate.css('display', 'inline-block');
+                                                    $diskIopsReadRate.css('display', 'inline-block');
+                                                    $diskIopsWriteRate.css('display', 'inline-block');
+                                                } else { // No Qos
+                                                    $diskBytesReadRate.hide();
+                                                    $diskBytesWriteRate.hide();
+                                                    $diskIopsReadRate.hide();
+                                                    $diskIopsWriteRate.hide();
+                                                    $isCustomizedIops.hide();
+                                                    $minIops.hide();
+                                                    $maxIops.hide();
+                                                    $hypervisorSnapshotReserve.hide();
+                                                }
+                                            });
+                                        }
+                                    },
+                                    isCustomizedIops: {
+                                        label: 'label.custom.disk.iops',
+                                        docID: 'helpDiskOfferingCustomDiskIops',
+                                        isBoolean: true,
+                                        isReverse: true,
+                                        isChecked: false
+                                    },
+                                    minIops: {
+                                        label: 'label.disk.iops.min',
+                                        docID: 'helpDiskOfferingDiskIopsMin',
+                                        dependsOn: 'isCustomizedIops',
+                                        validation: {
+                                            required: false,
+                                            number: true
+                                        }
+                                    },
+                                    maxIops: {
+                                        label: 'label.disk.iops.max',
+                                        docID: 'helpDiskOfferingDiskIopsMax',
+                                        dependsOn: 'isCustomizedIops',
+                                        validation: {
+                                            required: false,
+                                            number: true
+                                        }
+                                    },
+                                    hypervisorSnapshotReserve: {
+                                        label: 'label.hypervisor.snapshot.reserve',
+                                        docID: 'helpDiskOfferingHypervisorSnapshotReserve',
+                                        validation: {
+                                            required: false,
+                                            number: true
+                                        }
+                                    },
                                     diskBytesReadRate: {
                                         label: 'label.disk.bytes.read.rate',
                                         docID: 'helpComputeOfferingDiskBytesReadRate',
@@ -326,26 +433,59 @@
                                         networkrate: args.data.networkRate
                                     });
                                 }
-                                if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
-                                    $.extend(data, {
-                                        bytesreadrate: args.data.diskBytesReadRate
-                                    });
-                                }
-                                if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
-                                    $.extend(data, {
-                                        byteswriterate: args.data.diskBytesWriteRate
-                                    });
-                                }
-                                if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
-                                    $.extend(data, {
-                                        iopsreadrate: args.data.diskIopsReadRate
-                                    });
-                                }
-                                if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
+
+                                if (args.data.qosType == 'storage') {
+                                    var customIops = args.data.isCustomizedIops == "on";
+
                                     $.extend(data, {
-                                        iopswriterate: args.data.diskIopsWriteRate
+                                        customizediops: customIops
                                     });
+
+                                    if (!customIops) {
+                                        if (args.data.minIops != null && args.data.minIops.length > 0) {
+                                            $.extend(data, {
+                                                miniops: args.data.minIops
+                                            });
+                                        }
+
+                                        if (args.data.maxIops != null && args.data.maxIops.length > 0) {
+                                            $.extend(data, {
+                                                maxiops: args.data.maxIops
+                                            });
+                                        }
+                                    }
+
+                                    if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) {
+                                        $.extend(data, {
+                                            hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve
+                                        });
+                                    }
+                                } else if (args.data.qosType == 'hypervisor') {
+                                    if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
+                                        $.extend(data, {
+                                            bytesreadrate: args.data.diskBytesReadRate
+                                        });
+                                    }
+
+                                    if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
+                                        $.extend(data, {
+                                            byteswriterate: args.data.diskBytesWriteRate
+                                        });
+                                    }
+
+                                    if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
+                                        $.extend(data, {
+                                            iopsreadrate: args.data.diskIopsReadRate
+                                        });
+                                    }
+
+                                    if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
+                                        $.extend(data, {
+                                            iopswriterate: args.data.diskIopsWriteRate
+                                        });
+                                    }
                                 }
+
                                 $.extend(data, {
                                     offerha: (args.data.offerHA == "on")
                                 });