You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ap...@apache.org on 2013/04/19 08:14:29 UTC

[3/4] Storage motion for Xenserver changes: 1. Implemented Api findStoragePoolsForMigration. Added a new response objects to list storage pools available for migration. 2. Updated migrateVolume api for allowing migrating volumes of running vms. These cha

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/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 b619ee9..a84f308 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
@@ -18,11 +18,18 @@
  */
 package org.apache.cloudstack.storage.test;
 
+import java.util.Map;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
 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.VolumeInfo;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.apache.cloudstack.storage.motion.DataMotionStrategy;
 
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.Host;
+
 public class MockStorageMotionStrategy implements DataMotionStrategy {
 
     @Override
@@ -32,6 +39,11 @@ public class MockStorageMotionStrategy implements DataMotionStrategy {
     }
 
     @Override
+    public boolean canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
+        return true;
+    }
+
+    @Override
     public Void copyAsync(DataObject srcData, DataObject destData,
             AsyncCompletionCallback<CopyCommandResult> callback) {
         CopyCommandResult result = new CopyCommandResult("something", null);
@@ -39,4 +51,11 @@ public class MockStorageMotionStrategy implements DataMotionStrategy {
         return null;
     }
 
+    @Override
+    public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost,
+            AsyncCompletionCallback<CopyCommandResult> callback) {
+        CopyCommandResult result = new CopyCommandResult("something", null);
+        callback.complete(result);
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
index 3602bb1..ad9238a 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
@@ -20,12 +20,14 @@ package org.apache.cloudstack.storage.motion;
 
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import javax.inject.Inject;
 
 import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType;
+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.DataStoreRole;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
@@ -36,6 +38,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
+import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.BackupSnapshotAnswer;
 import com.cloud.agent.api.BackupSnapshotCommand;
@@ -47,15 +50,21 @@ import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.UpgradeSnapshotCommand;
 import com.cloud.agent.api.storage.CopyVolumeAnswer;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
+import com.cloud.agent.api.storage.MigrateVolumeAnswer;
+import com.cloud.agent.api.storage.MigrateVolumeCommand;
 import com.cloud.agent.api.storage.CreateAnswer;
 import com.cloud.agent.api.storage.CreateCommand;
 import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
 import com.cloud.agent.api.to.S3TO;
 import com.cloud.agent.api.to.StorageFilerTO;
 import com.cloud.agent.api.to.SwiftTO;
+import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.OperationTimedoutException;
 import com.cloud.exception.StorageUnavailableException;
+import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.storage.DiskOfferingVO;
@@ -86,6 +95,8 @@ import com.cloud.utils.db.DB;
 import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.DiskProfile;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
 
 @Component
 public class AncientDataMotionStrategy implements DataMotionStrategy {
@@ -102,8 +113,12 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
     @Inject
     StorageManager storageMgr;
     @Inject
+    AgentManager agentMgr;
+    @Inject
     VolumeDao volDao;
     @Inject
+    VMInstanceDao instanceDao;
+    @Inject
     VMTemplateDao templateDao;
     @Inject
     SnapshotManager snapshotMgr;
@@ -130,6 +145,11 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         return true;
     }
 
+    @Override
+    public boolean canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
+        return false;
+    }
+
     @DB
     protected Answer copyVolumeFromImage(DataObject srcData, DataObject destData) {
         String value = configDao.getValue(Config.RecreateSystemVmEnabled.key());
@@ -393,6 +413,53 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         return cvAnswer;
     }
 
+    protected Answer migrateVolumeToPool(DataObject srcData, DataStore destStore) {
+        VolumeInfo volume = (VolumeInfo)srcData;
+        Long instanceId = volume.getInstanceId();
+        StoragePool destPool = (StoragePool)this.dataStoreMgr.getDataStore(destStore.getId(), DataStoreRole.Primary);
+        MigrateVolumeAnswer answer = null;
+        VMInstanceVO vmInstance = null;
+        if (instanceId != null) {
+            vmInstance = instanceDao.findById(instanceId);
+        }
+
+        Long hostId = null;
+        if (vmInstance != null) {
+            hostId = vmInstance.getHostId();
+        }
+
+        try {
+            if (hostId != null) {
+                MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool);
+                answer = (MigrateVolumeAnswer) this.agentMgr.send(hostId, command);
+            }
+        } catch (OperationTimedoutException e) {
+            s_logger.error("Operation timed out on storage motion for volume " + volume, e);
+            throw new CloudRuntimeException("Failed to live migrate volume " + volume + " to storage pool " +
+                    destPool, e);
+        } catch (AgentUnavailableException e) {
+            s_logger.error("Agent unavailable exception while doing storage motion for volume " + volume, e);
+            throw new CloudRuntimeException("Failed to live migrate volume " + volume + " to storage pool " +
+                    destPool, e);
+        }
+
+        if (answer == null || !answer.getResult()) {
+            throw new CloudRuntimeException("Failed to migrate volume " + volume + " to storage pool " + destPool);
+        } else {
+            // Update the volume details after migration.
+            VolumeVO volumeVo = this.volDao.findById(volume.getId());
+            Long oldPoolId = volume.getPoolId();
+            volumeVo.setPath(answer.getVolumePath());
+            volumeVo.setFolder(destPool.getPath());
+            volumeVo.setPodId(destPool.getPodId());
+            volumeVo.setPoolId(destPool.getId());
+            volumeVo.setLastPoolId(oldPoolId);
+            this.volDao.update(volume.getId(), volumeVo);
+        }
+
+        return answer;
+    }
+
     @Override
     public Void copyAsync(DataObject srcData, DataObject destData,
             AsyncCompletionCallback<CopyCommandResult> callback) {
@@ -419,7 +486,12 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
             	answer = cloneVolume(srcData, destData);
             } else if (destData.getType() == DataObjectType.VOLUME
                     && srcData.getType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) {
-            	answer = copyVolumeBetweenPools(srcData, destData);
+                if (srcData.getId() == destData.getId()) {
+                    // The volume has to be migrated across storage pools.
+                    answer = migrateVolumeToPool(srcData, destData.getDataStore());
+                } else {
+                    answer = copyVolumeBetweenPools(srcData, destData);
+                }
             } else if (srcData.getType() == DataObjectType.SNAPSHOT &&
             		destData.getType() == DataObjectType.SNAPSHOT) {
             	answer = copySnapshot(srcData, destData);
@@ -435,6 +507,16 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         return null;
     }
 
+    @Override
+    public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost,
+            AsyncCompletionCallback<CopyCommandResult> callback) {
+        CopyCommandResult result = new CopyCommandResult(null, null);
+        result.setResult("Unsupported operation requested for copying data.");
+        callback.complete(result);
+
+        return null;
+    }
+
     @DB
     protected Answer createTemplateFromSnashot(DataObject srcData,
             DataObject destData) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionService.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionService.java b/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionService.java
index db36f64..5ecbcb3 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionService.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionService.java
@@ -18,11 +18,20 @@
  */
 package org.apache.cloudstack.storage.motion;
 
+import java.util.Map;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
 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.VolumeInfo;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.Host;
+
 public interface DataMotionService {
     public void copyAsync(DataObject srcData, DataObject destData,
             AsyncCompletionCallback<CopyCommandResult> callback);
+    public 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/21ce3bef/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java b/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
index 343140f..b74e10c 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java
@@ -19,14 +19,19 @@
 package org.apache.cloudstack.storage.motion;
 
 import java.util.List;
+import java.util.Map;
 
 import javax.inject.Inject;
 
 import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
 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.VolumeInfo;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 import org.springframework.stereotype.Component;
 
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.Host;
 import com.cloud.utils.exception.CloudRuntimeException;
 
 @Component
@@ -58,4 +63,15 @@ public class DataMotionServiceImpl implements DataMotionService {
         throw new CloudRuntimeException("can't find strategy to move data");
     }
 
+    @Override
+    public void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo,
+            Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
+        for (DataMotionStrategy strategy : strategies) {
+            if (strategy.canHandle(volumeMap, srcHost, destHost)) {
+                strategy.copyAsync(volumeMap, vmTo, srcHost, destHost, callback);
+                return;
+            }
+        }
+        throw new CloudRuntimeException("can't find strategy to move data");
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionStrategy.java b/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionStrategy.java
index ba40c6d..e3859b4 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionStrategy.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/motion/DataMotionStrategy.java
@@ -18,13 +18,23 @@
  */
 package org.apache.cloudstack.storage.motion;
 
+import java.util.Map;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
 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.VolumeInfo;
 import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
 
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.Host;
+
 public interface DataMotionStrategy {
     public boolean canHandle(DataObject srcData, DataObject destData);
+    public boolean canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost);
 
     public Void copyAsync(DataObject srcData, DataObject destData,
             AsyncCompletionCallback<CopyCommandResult> callback);
+    public 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/21ce3bef/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
index ceadb25..ea31be3 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
@@ -176,6 +176,8 @@ public class VolumeObject implements VolumeInfo {
                     volEvent = Volume.Event.CreateRequested;
                 } else if (event == ObjectInDataStoreStateMachine.Event.CopyingRequested) {
                     volEvent = Volume.Event.CopyRequested;
+                } else if (event == ObjectInDataStoreStateMachine.Event.MigrationRequested) {
+                    volEvent = Volume.Event.MigrationRequested;
                 }
             }
             

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/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 32e7d27..e3526de 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
@@ -18,6 +18,10 @@
  */
 package org.apache.cloudstack.storage.volume;
 
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
 import javax.inject.Inject;
 
 import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity;
@@ -27,6 +31,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
@@ -40,11 +45,14 @@ import org.apache.cloudstack.storage.datastore.DataObjectManager;
 import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
 import org.apache.cloudstack.storage.datastore.PrimaryDataStore;
 import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.motion.DataMotionService;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
+import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.host.Host;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.Volume;
 import com.cloud.storage.Volume.Type;
@@ -561,7 +569,163 @@ public class VolumeServiceImpl implements VolumeService {
         
         return null;
     }
-    
+
+    private class MigrateVolumeContext<T> extends AsyncRpcConext<T> {
+        final VolumeInfo srcVolume;
+        final VolumeInfo destVolume;
+        final DataStore destStore;
+        final AsyncCallFuture<VolumeApiResult> future;
+        /**
+         * @param callback
+         */
+        public MigrateVolumeContext(AsyncCompletionCallback<T> callback, AsyncCallFuture<VolumeApiResult> future,
+                VolumeInfo srcVolume, VolumeInfo destVolume, DataStore destStore) {
+            super(callback);
+            this.srcVolume = srcVolume;
+            this.destVolume = destVolume;
+            this.destStore = destStore;
+            this.future = future;
+        }
+    }
+
+    @Override
+    public AsyncCallFuture<VolumeApiResult> migrateVolume(VolumeInfo srcVolume, DataStore destStore) {
+        AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
+        VolumeApiResult res = new VolumeApiResult(srcVolume);
+        try {
+            if (!this.snapshotMgr.canOperateOnVolume(srcVolume)) {
+                s_logger.debug("Snapshots are being created on this volume. This volume cannot be migrated now.");
+                res.setResult("Snapshots are being created on this volume. This volume cannot be migrated now.");
+                future.complete(res);
+                return future;
+            }
+
+            VolumeInfo destVolume = this.volFactory.getVolume(srcVolume.getId(), destStore);
+            srcVolume.processEvent(Event.MigrationRequested);
+            MigrateVolumeContext<VolumeApiResult> context = new MigrateVolumeContext<VolumeApiResult>(null, future,
+                    srcVolume, destVolume, destStore);
+            AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
+            caller.setCallback(caller.getTarget().migrateVolumeCallBack(null, null)).setContext(context);
+            this.motionSrv.copyAsync(srcVolume, destVolume, caller);
+        } catch (Exception e) {
+            s_logger.debug("Failed to copy volume", e);
+            res.setResult(e.toString());
+            future.complete(res);
+        }
+        return future;
+    }
+
+    protected Void migrateVolumeCallBack(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback,
+            MigrateVolumeContext<VolumeApiResult> context) {
+        VolumeInfo srcVolume = context.srcVolume;
+        VolumeInfo destVolume = context.destVolume;
+        CopyCommandResult result = callback.getResult();
+        AsyncCallFuture<VolumeApiResult> future = context.future;
+        VolumeApiResult res = new VolumeApiResult(srcVolume);
+        try {
+            if (result.isFailed()) {
+                res.setResult(result.getResult());
+                srcVolume.processEvent(Event.OperationFailed);
+                future.complete(res);
+            } else {
+                srcVolume.processEvent(Event.OperationSuccessed);
+                future.complete(res);
+            }
+        } catch (Exception e) {
+            s_logger.error("Failed to process copy volume callback", e);
+            res.setResult(e.toString());
+            future.complete(res);
+        }
+
+        return null;
+    }
+
+    private class MigrateVmWithVolumesContext<T> extends AsyncRpcConext<T> {
+        final Map<VolumeInfo, DataStore> volumeToPool;
+        final AsyncCallFuture<CommandResult> future;
+        /**
+         * @param callback
+         */
+        public MigrateVmWithVolumesContext(AsyncCompletionCallback<T> callback, AsyncCallFuture<CommandResult> future,
+                Map<VolumeInfo, DataStore> volumeToPool) {
+            super(callback);
+            this.volumeToPool = volumeToPool;
+            this.future = future;
+        }
+    }
+
+    @Override
+    public AsyncCallFuture<CommandResult> migrateVolumes(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo,
+            Host srcHost, Host destHost) {
+        AsyncCallFuture<CommandResult> future = new AsyncCallFuture<CommandResult>();
+        CommandResult res = new CommandResult();
+        try {
+            // Check to make sure there are no snapshot operations on a volume and
+            // put it in the migrating state.
+            List<VolumeInfo> volumesMigrating = new ArrayList<VolumeInfo>();
+            for (Map.Entry<VolumeInfo, DataStore> entry : volumeMap.entrySet()) {
+                VolumeInfo volume = entry.getKey();
+                if (!this.snapshotMgr.canOperateOnVolume(volume)) {
+                    s_logger.debug("Snapshots are being created on a volume. Volumes cannot be migrated now.");
+                    res.setResult("Snapshots are being created on a volume. Volumes cannot be migrated now.");
+                    future.complete(res);
+
+                    // All the volumes that are already in migrating state need to be put back in ready state.
+                    for (VolumeInfo volumeMigrating : volumesMigrating) {
+                        volumeMigrating.processEvent(Event.OperationFailed);
+                    }
+                    return future;
+                } else {
+                    volume.processEvent(Event.MigrationRequested);
+                    volumesMigrating.add(volume);
+                }
+            }
+
+            MigrateVmWithVolumesContext<CommandResult> context = new MigrateVmWithVolumesContext<CommandResult>(null,
+                    future, volumeMap);
+            AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
+            caller.setCallback(caller.getTarget().migrateVmWithVolumesCallBack(null, null)).setContext(context);
+            this.motionSrv.copyAsync(volumeMap, vmTo, srcHost, destHost, caller);
+
+        } catch (Exception e) {
+            s_logger.debug("Failed to copy volume", e);
+            res.setResult(e.toString());
+            future.complete(res);
+        }
+
+        return future;
+    }
+
+    protected Void migrateVmWithVolumesCallBack(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback,
+            MigrateVmWithVolumesContext<CommandResult> context) {
+        Map<VolumeInfo, DataStore> volumeToPool = context.volumeToPool;
+        CopyCommandResult result = callback.getResult();
+        AsyncCallFuture<CommandResult> future = context.future;
+        CommandResult res = new CommandResult();
+        try {
+            if (result.isFailed()) {
+                res.setResult(result.getResult());
+                for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+                    VolumeInfo volume = entry.getKey();
+                    volume.processEvent(Event.OperationFailed);
+                }
+                future.complete(res);
+            } else {
+                for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+                    VolumeInfo volume = entry.getKey();
+                    volume.processEvent(Event.OperationSuccessed);
+                }
+                future.complete(res);
+            }
+        } catch (Exception e) {
+            s_logger.error("Failed to process copy volume callback", e);
+            res.setResult(e.toString());
+            future.complete(res);
+        }
+
+        return null;
+    }
+
     @Override
     public AsyncCallFuture<VolumeApiResult> registerVolume(VolumeInfo volume, DataStore store) {
         

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java
----------------------------------------------------------------------
diff --git a/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java b/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java
index a672efd..8243f3a 100755
--- a/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java
+++ b/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java
@@ -55,6 +55,62 @@ public class RandomAllocator extends AdapterBase implements HostAllocator {
 
     @Override
     public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type,
+            ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity) {
+        long dcId = plan.getDataCenterId();
+        Long podId = plan.getPodId();
+        Long clusterId = plan.getClusterId();
+        ServiceOffering offering = vmProfile.getServiceOffering();
+        List<Host> suitableHosts = new ArrayList<Host>();
+
+        if (type == Host.Type.Storage) {
+            return suitableHosts;
+        }
+
+        String hostTag = offering.getHostTag();
+        if(hostTag != null){
+            s_logger.debug("Looking for hosts in dc: " + dcId + "  pod:" + podId + "  cluster:" + clusterId +
+                    " having host tag:" + hostTag);
+        }else{
+            s_logger.debug("Looking for hosts in dc: " + dcId + "  pod:" + podId + "  cluster:" + clusterId);
+        }
+
+        // list all computing hosts, regardless of whether they support routing...it's random after all
+        if(hostTag != null){
+            hosts.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, hostTag));
+        }else{
+            hosts.retainAll(_resourceMgr.listAllUpAndEnabledHosts(type, clusterId, podId, dcId));
+        }
+
+        s_logger.debug("Random Allocator found " + hosts.size() + "  hosts");
+        if (hosts.size() == 0) {
+            return suitableHosts;
+        }
+
+        Collections.shuffle(hosts);
+        for (Host host : hosts) {
+            if(suitableHosts.size() == returnUpTo){
+                break;
+            }
+
+            if (!avoid.shouldAvoid(host)) {
+                suitableHosts.add(host);
+            } else {
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("Host name: " + host.getName() + ", hostId: "+ host.getId() +" is in avoid set, " +
+                            "skipping this and trying other available hosts");
+                }
+            }
+        }
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Random Host Allocator returning "+suitableHosts.size() +" suitable hosts");
+        }
+
+        return suitableHosts;
+    }
+
+    @Override
+    public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type,
             ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) {
 
         long dcId = plan.getDataCenterId();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/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 4ef583a..46ae35a 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
@@ -3358,7 +3358,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         vm.setMemoryLimits(conn, maxMemsize, maxMemsize, minMemsize, maxMemsize);
     }
 
-    private void waitForTask(Connection c, Task task, long pollInterval, long timeout) throws XenAPIException, XmlRpcException {
+    protected void waitForTask(Connection c, Task task, long pollInterval, long timeout) throws XenAPIException, XmlRpcException {
         long beginTime = System.currentTimeMillis();
         while (task.getStatus(c) == Types.TaskStatusType.PENDING) {
             try {
@@ -3374,7 +3374,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         }
     }
 
-    private void checkForSuccess(Connection c, Task task) throws XenAPIException, XmlRpcException {
+    protected void checkForSuccess(Connection c, Task task) throws XenAPIException, XmlRpcException {
         if (task.getStatus(c) == Types.TaskStatusType.SUCCESS) {
             return;
         } else {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
index d64e173..96a90a6 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
@@ -132,6 +132,7 @@ public class XenServer56FP1Resource extends XenServer56Resource {
         record.affinity = host;
         record.otherConfig.remove("disks");
         record.otherConfig.remove("default_template");
+        record.otherConfig.remove("mac_seed");
         record.isATemplate = false;
         record.nameLabel = vmSpec.getName();
         record.actionsAfterCrash = Types.OnCrashBehaviour.DESTROY;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java
index 8d267b1..bb31136 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java
@@ -20,6 +20,9 @@ package com.cloud.hypervisor.xen.resource;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 import javax.ejb.Local;
 
@@ -28,7 +31,34 @@ import org.apache.log4j.Logger;
 import com.cloud.resource.ServerResource;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.script.Script;
-
+import com.cloud.vm.VirtualMachine.State;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.storage.MigrateVolumeAnswer;
+import com.cloud.agent.api.storage.MigrateVolumeCommand;
+import com.cloud.agent.api.MigrateWithStorageAnswer;
+import com.cloud.agent.api.MigrateWithStorageCommand;
+import com.cloud.agent.api.MigrateWithStorageReceiveAnswer;
+import com.cloud.agent.api.MigrateWithStorageReceiveCommand;
+import com.cloud.agent.api.MigrateWithStorageSendAnswer;
+import com.cloud.agent.api.MigrateWithStorageSendCommand;
+import com.cloud.agent.api.MigrateWithStorageCompleteAnswer;
+import com.cloud.agent.api.MigrateWithStorageCompleteCommand;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.network.Networks.TrafficType;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.agent.api.to.NicTO;
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.Host;
+import com.xensource.xenapi.Network;
+import com.xensource.xenapi.SR;
+import com.xensource.xenapi.Task;
+import com.xensource.xenapi.Types;
+import com.xensource.xenapi.VBD;
+import com.xensource.xenapi.VDI;
+import com.xensource.xenapi.VIF;
+import com.xensource.xenapi.VM;
 
 @Local(value=ServerResource.class)
 public class XenServer610Resource extends XenServer56FP1Resource {
@@ -55,4 +85,331 @@ public class XenServer610Resource extends XenServer56FP1Resource {
         files.add(file);
         return files;
     }
+
+    @Override
+    public Answer executeRequest(Command cmd) {
+        if (cmd instanceof MigrateWithStorageCommand) {
+            return execute((MigrateWithStorageCommand) cmd);
+        } else if (cmd instanceof MigrateWithStorageReceiveCommand) {
+            return execute((MigrateWithStorageReceiveCommand) cmd);
+        } else if (cmd instanceof MigrateWithStorageSendCommand) {
+            return execute((MigrateWithStorageSendCommand) cmd);
+        } else if (cmd instanceof MigrateWithStorageCompleteCommand) {
+            return execute((MigrateWithStorageCompleteCommand) cmd);
+        } else if (cmd instanceof MigrateVolumeCommand) {
+            return execute((MigrateVolumeCommand) cmd);
+        } else {
+            return super.executeRequest(cmd);
+        }
+    }
+
+    private List<VolumeTO> getUpdatedVolumePathsOfMigratedVm(Connection connection, VM migratedVm,
+            VolumeTO[] volumes) throws CloudRuntimeException {
+        List<VolumeTO> volumeToList = new ArrayList<VolumeTO>();
+
+        try {
+            // Volume paths would have changed. Return that information.
+            Set<VBD> vbds = migratedVm.getVBDs(connection);
+            Map<String, VDI> deviceIdToVdiMap = new HashMap<String, VDI>();
+            // get vdi:vbdr to a map
+            for (VBD vbd : vbds) {
+                VBD.Record vbdr = vbd.getRecord(connection);
+                if (vbdr.type == Types.VbdType.DISK) {
+                    VDI vdi = vbdr.VDI;
+                    deviceIdToVdiMap.put(vbdr.userdevice, vdi);
+                }
+            }
+
+            for (VolumeTO volumeTo : volumes) {
+                Long deviceId = volumeTo.getDeviceId();
+                VDI vdi = deviceIdToVdiMap.get(deviceId.toString());
+                volumeTo.setPath(vdi.getUuid(connection));
+                volumeToList.add(volumeTo);
+            }
+        } catch (Exception e) {
+            s_logger.error("Unable to get the updated VDI paths of the migrated vm " + e.toString(), e);
+            throw new CloudRuntimeException("Unable to get the updated VDI paths of the migrated vm " + e.toString(), e);
+        }
+
+        return volumeToList;
+    }
+
+    protected MigrateWithStorageAnswer execute(MigrateWithStorageCommand cmd) {
+        Connection connection = getConnection();
+        VirtualMachineTO vmSpec = cmd.getVirtualMachine();
+        Map<VolumeTO, StorageFilerTO> volumeToFiler = cmd.getVolumeToFiler();
+        final String vmName = vmSpec.getName();
+        State state = s_vms.getState(_cluster, vmName);
+        Task task = null;
+
+        synchronized (_cluster.intern()) {
+            s_vms.put(_cluster, _name, vmName, State.Stopping);
+        }
+
+        try {
+            prepareISO(connection, vmSpec.getName());
+            Map<String, String> other = new HashMap<String, String>();
+            other.put("live", "true");
+            Network networkForSm = getNativeNetworkForTraffic(connection, TrafficType.Storage, null).getNetwork();
+            Host host = Host.getByUuid(connection, _host.uuid);
+            Map<String,String> token = host.migrateReceive(connection, networkForSm, other);
+
+            // Get the vm to migrate.
+            Set<VM> vms = VM.getByNameLabel(connection, vmSpec.getName());
+            VM vmToMigrate = vms.iterator().next();
+
+            // Create the vif map. The vm stays in the same cluster so we have to pass an empty vif map.
+            Map<VIF, Network> vifMap = new HashMap<VIF, Network>();
+            Map<VDI, SR> vdiMap = new HashMap<VDI, SR>();
+            for (Map.Entry<VolumeTO, StorageFilerTO> entry : volumeToFiler.entrySet()) {
+                vdiMap.put(getVDIbyUuid(connection, entry.getKey().getPath()),
+                        getStorageRepository(connection, entry.getValue().getUuid()));
+            }
+
+            // Check migration with storage is possible.
+            task = vmToMigrate.assertCanMigrateAsync(connection, token, true, vdiMap, vifMap, other);
+            try {
+                // poll every 1 seconds
+                long timeout = (_migratewait) * 1000L;
+                waitForTask(connection, task, 1000, timeout);
+                checkForSuccess(connection, task);
+            } catch (Types.HandleInvalid e) {
+                s_logger.error("Error while checking if vm " + vmName + " can be migrated to the destination host " +
+                        host, e);
+                throw new CloudRuntimeException("Error while checking if vm " + vmName + " can be migrated to the " +
+                        "destination host " + host, e);
+            }
+
+            // Migrate now.
+            task = vmToMigrate.migrateSendAsync(connection, token, true, vdiMap, vifMap, other);
+            try {
+                // poll every 1 seconds.
+                long timeout = (_migratewait) * 1000L;
+                waitForTask(connection, task, 1000, timeout);
+                checkForSuccess(connection, task);
+            } catch (Types.HandleInvalid e) {
+                s_logger.error("Error while migrating vm " + vmName + " to the destination host " + host, e);
+                throw new CloudRuntimeException("Error while migrating vm " + vmName + " to the destination host " +
+                        host, e);
+            }
+
+            // Volume paths would have changed. Return that information.
+            List<VolumeTO> volumeToList = getUpdatedVolumePathsOfMigratedVm(connection, vmToMigrate, vmSpec.getDisks());
+            vmToMigrate.setAffinity(connection, host);
+            state = State.Stopping;
+
+            return new MigrateWithStorageAnswer(cmd, volumeToList);
+        } catch (Exception e) {
+            s_logger.warn("Catch Exception " + e.getClass().getName() + ". Storage motion failed due to " +
+                    e.toString(), e);
+            return new MigrateWithStorageAnswer(cmd, e);
+        } finally {
+            if (task != null) {
+                try {
+                    task.destroy(connection);
+                } catch (Exception e) {
+                    s_logger.debug("Unable to destroy task " + task.toString() + " on host " + _host.uuid +" due to " +
+                            e.toString());
+                }
+            }
+
+            synchronized (_cluster.intern()) {
+                s_vms.put(_cluster, _name, vmName, state);
+            }
+        }
+    }
+
+    protected MigrateWithStorageReceiveAnswer execute(MigrateWithStorageReceiveCommand cmd) {
+        Connection connection = getConnection();
+        VirtualMachineTO vmSpec = cmd.getVirtualMachine();
+        Map<VolumeTO, StorageFilerTO> volumeToFiler = cmd.getVolumeToFiler();
+
+        try {
+            // Get a map of all the SRs to which the vdis will be migrated.
+            Map<VolumeTO, Object> volumeToSr = new HashMap<VolumeTO, Object>();
+            for (Map.Entry<VolumeTO, StorageFilerTO> entry : volumeToFiler.entrySet()) {
+                SR sr = getStorageRepository(connection, entry.getValue().getUuid());
+                volumeToSr.put(entry.getKey(), sr);
+            }
+
+            // Get the list of networks to which the vifs will attach.
+            Map<NicTO, Object> nicToNetwork = new HashMap<NicTO, Object>();
+            for (NicTO nicTo : vmSpec.getNics()) {
+                Network network = getNetwork(connection, nicTo);
+                nicToNetwork.put(nicTo, network);
+            }
+
+            Map<String, String> other = new HashMap<String, String>();
+            other.put("live", "true");
+            Network network = getNativeNetworkForTraffic(connection, TrafficType.Storage, null).getNetwork();
+            Host host = Host.getByUuid(connection, _host.uuid);
+            Map<String,String> token = host.migrateReceive(connection, network, other);
+
+            return new MigrateWithStorageReceiveAnswer(cmd, volumeToSr, nicToNetwork, token);
+        } catch (CloudRuntimeException e) {
+            s_logger.error("Migration of vm " + vmSpec.getName() + " with storage failed due to " + e.toString(), e);
+            return new MigrateWithStorageReceiveAnswer(cmd, e);
+        } catch (Exception e) {
+            s_logger.error("Migration of vm " + vmSpec.getName() + " with storage failed due to " + e.toString(), e);
+            return new MigrateWithStorageReceiveAnswer(cmd, e);
+        }
+    }
+
+    protected MigrateWithStorageSendAnswer execute(MigrateWithStorageSendCommand cmd) {
+        Connection connection = getConnection();
+        VirtualMachineTO vmSpec = cmd.getVirtualMachine();
+        Map<VolumeTO, Object> volumeToSr = cmd.getVolumeToSr();
+        Map<NicTO, Object> nicToNetwork = cmd.getNicToNetwork();
+        Map<String, String> token = cmd.getToken();
+        final String vmName = vmSpec.getName();
+        State state = s_vms.getState(_cluster, vmName);
+        Set<VolumeTO> volumeToSet = null;
+        boolean migrated = false;
+        Task task = null;
+
+        synchronized (_cluster.intern()) {
+            s_vms.put(_cluster, _name, vmName, State.Stopping);
+        }
+
+        try {
+            Set<VM> vms = VM.getByNameLabel(connection, vmSpec.getName());
+            VM vmToMigrate = vms.iterator().next();
+            Map<String, String> other = new HashMap<String, String>();
+            other.put("live", "true");
+
+            // Create the vdi map which tells what volumes of the vm need to go on which sr on the destination.
+            Map<VDI, SR> vdiMap = new HashMap<VDI, SR>();
+            for (Map.Entry<VolumeTO, Object> entry : volumeToSr.entrySet()) {
+                if  (entry.getValue() instanceof SR) {
+                    SR sr = (SR)entry.getValue();
+                    VDI vdi = getVDIbyUuid(connection, entry.getKey().getPath());
+                    vdiMap.put(vdi, sr);
+                } else {
+                    throw new CloudRuntimeException("The object " + entry.getValue() + " passed is not of type SR.");
+                }
+            }
+
+            // Create the vif map.
+            Map<VIF, Network> vifMap = new HashMap<VIF, Network>();
+            for (Map.Entry<NicTO, Object> entry : nicToNetwork.entrySet()) {
+                if (entry.getValue() instanceof Network) {
+                    Network network = (Network)entry.getValue();
+                    VIF vif = getVifByMac(connection, vmToMigrate, entry.getKey().getMac());
+                    vifMap.put(vif, network);
+                } else {
+                    throw new CloudRuntimeException("The object " + entry.getValue() + " passed is not of type Network.");
+                }
+            }
+
+            // Check migration with storage is possible.
+            task = vmToMigrate.assertCanMigrateAsync(connection, token, true, vdiMap, vifMap, other);
+            try {
+                // poll every 1 seconds.
+                long timeout = (_migratewait) * 1000L;
+                waitForTask(connection, task, 1000, timeout);
+                checkForSuccess(connection, task);
+            } catch (Types.HandleInvalid e) {
+                s_logger.error("Error while checking if vm " + vmName + " can be migrated.", e);
+                throw new CloudRuntimeException("Error while checking if vm " + vmName + " can be migrated.", e);
+            }
+
+            // Migrate now.
+            task = vmToMigrate.migrateSendAsync(connection, token, true, vdiMap, vifMap, other);
+            try {
+                // poll every 1 seconds.
+                long timeout = (_migratewait) * 1000L;
+                waitForTask(connection, task, 1000, timeout);
+                checkForSuccess(connection, task);
+            } catch (Types.HandleInvalid e) {
+                s_logger.error("Error while migrating vm " + vmName, e);
+                throw new CloudRuntimeException("Error while migrating vm " + vmName, e);
+            }
+
+            migrated = true;
+            return new MigrateWithStorageSendAnswer(cmd, volumeToSet);
+        } catch (CloudRuntimeException e) {
+            s_logger.error("Migration of vm " + vmName + " with storage failed due to " + e.toString(), e);
+            return new MigrateWithStorageSendAnswer(cmd, e);
+        } catch (Exception e) {
+            s_logger.error("Migration of vm " + vmName + " with storage failed due to " + e.toString(), e);
+            return new MigrateWithStorageSendAnswer(cmd, e);
+        } finally {
+            if (task != null) {
+                try {
+                    task.destroy(connection);
+                } catch (Exception e) {
+                    s_logger.debug("Unable to destroy task " + task.toString() + " on host " + _host.uuid +" due to " +
+                            e.toString());
+                }
+            }
+
+            // Keep cluster/vm sync happy.
+            synchronized (_cluster.intern()) {
+                if (migrated) {
+                    s_vms.remove(_cluster, _name, vmName);
+                } else {
+                    s_vms.put(_cluster, _name, vmName, state);
+                }
+            }
+        }
+    }
+
+    protected MigrateWithStorageCompleteAnswer execute(MigrateWithStorageCompleteCommand cmd) {
+        Connection connection = getConnection();
+        VirtualMachineTO vmSpec = cmd.getVirtualMachine();
+
+        try {
+            Host host = Host.getByUuid(connection, _host.uuid);
+            Set<VM> vms = VM.getByNameLabel(connection, vmSpec.getName());
+            VM migratedVm = vms.iterator().next();
+
+            // Check the vm is present on the new host.
+            if (migratedVm == null) {
+                throw new CloudRuntimeException("Couldn't find the migrated vm " + vmSpec.getName() +
+                        " on the destination host.");
+            }
+
+            // Volume paths would have changed. Return that information.
+            List<VolumeTO > volumeToSet = getUpdatedVolumePathsOfMigratedVm(connection, migratedVm, vmSpec.getDisks());
+            migratedVm.setAffinity(connection, host);
+
+            synchronized (_cluster.intern()) {
+                s_vms.put(_cluster, _name, vmSpec.getName(), State.Running);
+            }
+
+            return new MigrateWithStorageCompleteAnswer(cmd, volumeToSet);
+        } catch (CloudRuntimeException e) {
+            s_logger.error("Migration of vm " + vmSpec.getName() + " with storage failed due to " + e.toString(), e);
+            return new MigrateWithStorageCompleteAnswer(cmd, e);
+        } catch (Exception e) {
+            s_logger.error("Migration of vm " + vmSpec.getName() + " with storage failed due to " + e.toString(), e);
+            return new MigrateWithStorageCompleteAnswer(cmd, e);
+        }
+    }
+
+    protected MigrateVolumeAnswer execute(MigrateVolumeCommand cmd) {
+        Connection connection = getConnection();
+        String volumeUUID = cmd.getVolumePath();
+        StorageFilerTO poolTO = cmd.getPool();
+
+        try {
+            SR destinationPool = getStorageRepository(connection, poolTO.getUuid());
+            VDI srcVolume = getVDIbyUuid(connection, volumeUUID);
+            Map<String, String> other = new HashMap<String, String>();
+            other.put("live", "true");
+
+            // Live migrate the vdi across pool.
+            Task task = srcVolume.poolMigrateAsync(connection, destinationPool, other);
+            long timeout = (_migratewait) * 1000L;
+            waitForTask(connection, task, 1000, timeout);
+            checkForSuccess(connection, task);
+            VDI dvdi = Types.toVDI(task, connection);
+
+            return new MigrateVolumeAnswer(cmd, true, null, dvdi.getUuid(connection));
+        } catch (Exception e) {
+            String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString();
+            s_logger.error(msg, e);
+            return new MigrateVolumeAnswer(cmd, false, msg, null);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/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
new file mode 100644
index 0000000..353f2b5
--- /dev/null
+++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java
@@ -0,0 +1,239 @@
+/*
+ * 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.storage.motion;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+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.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.MigrateWithStorageAnswer;
+import com.cloud.agent.api.MigrateWithStorageCommand;
+import com.cloud.agent.api.MigrateWithStorageCompleteAnswer;
+import com.cloud.agent.api.MigrateWithStorageCompleteCommand;
+import com.cloud.agent.api.MigrateWithStorageReceiveAnswer;
+import com.cloud.agent.api.MigrateWithStorageReceiveCommand;
+import com.cloud.agent.api.MigrateWithStorageSendAnswer;
+import com.cloud.agent.api.MigrateWithStorageSendCommand;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.Host;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+
+@Component
+public class XenServerStorageMotionStrategy implements DataMotionStrategy {
+    private static final Logger s_logger = Logger.getLogger(XenServerStorageMotionStrategy.class);
+    @Inject AgentManager agentMgr;
+    @Inject VolumeDao volDao;
+    @Inject VolumeDataFactory volFactory;
+    @Inject PrimaryDataStoreDao storagePoolDao;
+    @Inject VMInstanceDao instanceDao;
+
+    @Override
+    public boolean canHandle(DataObject srcData, DataObject destData) {
+        return false;
+    }
+
+    @Override
+    public boolean canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
+        return true;
+    }
+
+    @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.");
+        callback.complete(result);
+
+        return null;
+    }
+
+    @Override
+    public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost,
+            AsyncCompletionCallback<CopyCommandResult> callback) {
+        Answer answer = null;
+        String errMsg = null;
+        try {
+            VMInstanceVO instance = instanceDao.findById(vmTo.getId());
+            if (instance != null) {
+                if (srcHost.getClusterId() == destHost.getClusterId()) {
+                    answer = migrateVmWithVolumesWithinCluster(instance, vmTo, srcHost, destHost, volumeMap);
+                } else {
+                    answer = migrateVmWithVolumesAcrossCluster(instance, vmTo, srcHost, destHost, volumeMap);
+                }
+            } else {
+                throw new CloudRuntimeException("Unsupported operation requested for moving data.");
+            }
+        } catch (Exception e) {
+            s_logger.error("copy failed", e);
+            errMsg = e.toString();
+        }
+
+        CopyCommandResult result = new CopyCommandResult(null, answer);
+        result.setResult(errMsg);
+        callback.complete(result);
+        return null;
+    }
+
+    private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost,
+            Host destHost, Map<VolumeInfo, DataStore> volumeToPool) throws AgentUnavailableException {
+
+        // Initiate migration of a virtual machine with it's volumes.
+        try {
+            Map<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
+            for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+                VolumeInfo volume = entry.getKey();
+                VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
+                StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
+                volumeToFilerto.put(volumeTo, filerTo);
+            }
+
+            // Migration across cluster needs to be done in three phases.
+            // 1. Send a migrate receive command to the destination host so that it is ready to receive a vm.
+            // 2. Send a migrate send command to the source host. This actually migrates the vm to the destination.
+            // 3. Complete the process. Update the volume details.
+            MigrateWithStorageReceiveCommand receiveCmd = new MigrateWithStorageReceiveCommand(to, volumeToFilerto);
+            MigrateWithStorageReceiveAnswer receiveAnswer = (MigrateWithStorageReceiveAnswer) agentMgr.send(
+                    destHost.getId(), receiveCmd);
+            if (receiveAnswer == null) {
+                s_logger.error("Migration with storage of vm " + vm+ " to host " + destHost + " failed.");
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+            } else if (!receiveAnswer.getResult()) {
+                s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + receiveAnswer.getDetails());
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost +
+                        ". " + receiveAnswer.getDetails());
+            }
+
+            MigrateWithStorageSendCommand sendCmd = new MigrateWithStorageSendCommand(to, receiveAnswer.getVolumeToSr(),
+                    receiveAnswer.getNicToNetwork(), receiveAnswer.getToken());
+            MigrateWithStorageSendAnswer sendAnswer = (MigrateWithStorageSendAnswer) agentMgr.send(
+                    srcHost.getId(), sendCmd);
+            if (sendAnswer == null) {
+                s_logger.error("Migration with storage of vm " + vm+ " to host " + destHost + " failed.");
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+            } else if (!sendAnswer.getResult()) {
+                s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + sendAnswer.getDetails());
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost +
+                        ". " + sendAnswer.getDetails());
+            }
+
+            MigrateWithStorageCompleteCommand command = new MigrateWithStorageCompleteCommand(to);
+            MigrateWithStorageCompleteAnswer answer = (MigrateWithStorageCompleteAnswer) agentMgr.send(
+                    destHost.getId(), command);
+            if (answer == null) {
+                s_logger.error("Migration with storage of vm " + vm + " failed.");
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+            } else if (!answer.getResult()) {
+                s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + answer.getDetails());
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost +
+                        ". " + answer.getDetails());
+            } else {
+                // Update the volume details after migration.
+                updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
+            }
+
+            return answer;
+        } catch (OperationTimedoutException e) {
+            s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
+            throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId());
+        }
+    }
+
+    private Answer migrateVmWithVolumesWithinCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost,
+            Host destHost, Map<VolumeInfo, DataStore> volumeToPool) throws AgentUnavailableException {
+
+        // Initiate migration of a virtual machine with it's volumes.
+        try {
+            Map<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
+            for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+                VolumeInfo volume = entry.getKey();
+                VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
+                StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
+                volumeToFilerto.put(volumeTo, filerTo);
+            }
+
+            MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto);
+            MigrateWithStorageAnswer answer = (MigrateWithStorageAnswer) agentMgr.send(destHost.getId(), command);
+            if (answer == null) {
+                s_logger.error("Migration with storage of vm " + vm + " failed.");
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
+            } else if (!answer.getResult()) {
+                s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + answer.getDetails());
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost +
+                        ". " + answer.getDetails());
+            } else {
+                // Update the volume details after migration.
+                updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
+            }
+
+            return answer;
+        } catch (OperationTimedoutException e) {
+            s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
+            throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId());
+        }
+    }
+
+    private void updateVolumePathsAfterMigration(Map<VolumeInfo, DataStore> volumeToPool, List<VolumeTO> volumeTos) {
+        for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+            boolean updated = false;
+            VolumeInfo volume = entry.getKey();
+            StoragePool pool = (StoragePool)entry.getValue();
+            for (VolumeTO volumeTo : volumeTos) {
+                if (volume.getId() == volumeTo.getId()) {
+                    VolumeVO volumeVO = volDao.findById(volume.getId());
+                    Long oldPoolId = volumeVO.getPoolId();
+                    volumeVO.setPath(volumeTo.getPath());
+                    volumeVO.setFolder(pool.getPath());
+                    volumeVO.setPodId(pool.getPodId());
+                    volumeVO.setPoolId(pool.getId());
+                    volumeVO.setLastPoolId(oldPoolId);
+                    volDao.update(volume.getId(), volumeVO);
+                    updated = true;
+                    break;
+                }
+            }
+
+            if (!updated) {
+                s_logger.error("Volume path wasn't updated for volume " + volume + " after it was migrated.");
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/agent/manager/allocator/HostAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/agent/manager/allocator/HostAllocator.java b/server/src/com/cloud/agent/manager/allocator/HostAllocator.java
index 60027e7..6700f22 100755
--- a/server/src/com/cloud/agent/manager/allocator/HostAllocator.java
+++ b/server/src/com/cloud/agent/manager/allocator/HostAllocator.java
@@ -21,6 +21,7 @@ import java.util.List;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.deploy.DeploymentPlanner.ExcludeList;
 import com.cloud.host.Host;
+import com.cloud.host.HostVO;
 import com.cloud.host.Host.Type;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.utils.component.Adapter;
@@ -63,8 +64,22 @@ public interface HostAllocator extends Adapter {
     **/ 
     
     public List<Host> allocateTo(VirtualMachineProfile<?extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity);
-	
-	
-	public static int RETURN_UPTO_ALL = -1;
-		
+
+    /**
+     * Determines which physical hosts are suitable to
+     * allocate the guest virtual machines on
+     *
+     * @param VirtualMachineProfile vmProfile
+     * @param DeploymentPlan plan
+     * @param GuestType type
+     * @param ExcludeList avoid
+     * @param List<HostVO> hosts
+     * @param int returnUpTo (use -1 to return all possible hosts)
+     * @param boolean considerReservedCapacity (default should be true, set to false if host capacity calculation should not look at reserved capacity)
+     * @return List<Host> List of hosts that are suitable for VM allocation
+     **/
+     public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity);
+
+     public static int RETURN_UPTO_ALL = -1;
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
index 0091e43..b54b1c1 100755
--- a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
+++ b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
@@ -172,6 +172,53 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
         return allocateTo(plan, offering, template, avoid, clusterHosts, returnUpTo, considerReservedCapacity, account);
     }
 
+    @Override
+    public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan,
+            Type type, ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity) {
+        long dcId = plan.getDataCenterId();
+        Long podId = plan.getPodId();
+        Long clusterId = plan.getClusterId();
+        ServiceOffering offering = vmProfile.getServiceOffering();
+        VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate();
+        Account account = vmProfile.getOwner();
+        List<Host> suitableHosts = new ArrayList<Host>();
+
+        if (type == Host.Type.Storage) {
+            // FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of
+            // routing or not.
+            return suitableHosts;
+        }
+
+        String hostTagOnOffering = offering.getHostTag();
+        String hostTagOnTemplate = template.getTemplateTag();
+        boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false;
+        boolean hasTemplateTag = hostTagOnTemplate != null ? true : false;
+
+        String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
+        if (haVmTag != null) {
+            hosts.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, haVmTag));
+        } else {
+            if (hostTagOnOffering == null && hostTagOnTemplate == null){
+                hosts.retainAll(_resourceMgr.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId));
+            } else {
+                if (hasSvcOfferingTag) {
+                    hosts.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering));
+                }
+
+                if (hasTemplateTag) {
+                    hosts.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate));
+                }
+            }
+        }
+
+        if (!hosts.isEmpty()) {
+            suitableHosts = allocateTo(plan, offering, template, avoid, hosts, returnUpTo, considerReservedCapacity,
+                    account);
+        }
+
+        return suitableHosts;
+    }
+
     protected List<Host> allocateTo(DeploymentPlan plan, ServiceOffering offering, VMTemplateVO template, ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity, Account account) {
         if (_allocationAlgorithm.equals("random") || _allocationAlgorithm.equals("userconcentratedpod_random")) {
         	// Shuffle this so that we don't check the hosts in the same order.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java
index 90bd956..890c047 100755
--- a/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java
+++ b/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java
@@ -29,6 +29,7 @@ import com.cloud.agent.manager.allocator.HostAllocator;
 import com.cloud.deploy.DeploymentPlan;
 import com.cloud.deploy.DeploymentPlanner.ExcludeList;
 import com.cloud.host.Host;
+import com.cloud.host.HostVO;
 import com.cloud.host.Host.Type;
 import com.cloud.host.dao.HostDao;
 import com.cloud.offering.ServiceOffering;
@@ -52,6 +53,12 @@ public class TestingAllocator extends AdapterBase implements HostAllocator {
 
     @Override
     public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type,
+            ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity) {
+        return allocateTo(vmProfile, plan, type, avoid, returnUpTo, considerReservedCapacity);
+    }
+
+    @Override
+    public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, Type type,
             ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) {
         List<Host> availableHosts = new ArrayList<Host>();
         Host host = null;    	

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/ApiDBUtils.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java
index 303f328..c60af27 100755
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -36,6 +36,7 @@ import org.apache.cloudstack.api.response.DiskOfferingResponse;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
 import org.apache.cloudstack.api.response.EventResponse;
 import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import org.apache.cloudstack.api.response.InstanceGroupResponse;
 import org.apache.cloudstack.api.response.ProjectAccountResponse;
 import org.apache.cloudstack.api.response.ProjectInvitationResponse;
@@ -43,6 +44,7 @@ import org.apache.cloudstack.api.response.ProjectResponse;
 import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
+import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse;
 import org.apache.cloudstack.api.response.StoragePoolResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
@@ -1518,6 +1520,14 @@ public class ApiDBUtils {
         return _hostJoinDao.setHostResponse(vrData, vr);
     }
 
+    public static HostForMigrationResponse newHostForMigrationResponse(HostJoinVO vr, EnumSet<HostDetails> details) {
+        return _hostJoinDao.newHostForMigrationResponse(vr, details);
+    }
+
+    public static HostForMigrationResponse fillHostForMigrationDetails(HostForMigrationResponse vrData, HostJoinVO vr) {
+        return _hostJoinDao.setHostForMigrationResponse(vrData, vr);
+    }
+
     public static List<HostJoinVO> newHostView(Host vr){
         return _hostJoinDao.newHostView(vr);
     }
@@ -1543,6 +1553,15 @@ public class ApiDBUtils {
         return _poolJoinDao.setStoragePoolResponse(vrData, vr);
     }
 
+    public static StoragePoolForMigrationResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO vr) {
+        return _poolJoinDao.newStoragePoolForMigrationResponse(vr);
+    }
+
+    public static StoragePoolForMigrationResponse fillStoragePoolForMigrationDetails(StoragePoolForMigrationResponse
+            vrData, StoragePoolJoinVO vr){
+        return _poolJoinDao.setStoragePoolForMigrationResponse(vrData, vr);
+    }
+
     public static List<StoragePoolJoinVO> newStoragePoolView(StoragePool vr){
         return _poolJoinDao.newStoragePoolView(vr);
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 7629e5e..a7d6165 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -65,6 +65,7 @@ import org.apache.cloudstack.api.response.FirewallRuleResponse;
 import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
 import org.apache.cloudstack.api.response.GuestOSResponse;
 import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse;
 import org.apache.cloudstack.api.response.IPAddressResponse;
 import org.apache.cloudstack.api.response.InstanceGroupResponse;
@@ -105,6 +106,7 @@ import org.apache.cloudstack.api.response.SnapshotResponse;
 import org.apache.cloudstack.api.response.SnapshotScheduleResponse;
 import org.apache.cloudstack.api.response.StaticRouteResponse;
 import org.apache.cloudstack.api.response.StorageNetworkIpRangeResponse;
+import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse;
 import org.apache.cloudstack.api.response.StoragePoolResponse;
 import org.apache.cloudstack.api.response.SwiftResponse;
 import org.apache.cloudstack.api.response.SystemVmInstanceResponse;
@@ -511,6 +513,20 @@ public class ApiResponseHelper implements ResponseGenerator {
     }
 
     @Override
+    public HostForMigrationResponse createHostForMigrationResponse(Host host) {
+        return createHostForMigrationResponse(host, EnumSet.of(HostDetails.all));
+    }
+
+    @Override
+    public HostForMigrationResponse createHostForMigrationResponse(Host host, EnumSet<HostDetails> details) {
+        List<HostJoinVO> viewHosts = ApiDBUtils.newHostView(host);
+        List<HostForMigrationResponse> listHosts = ViewResponseHelper.createHostForMigrationResponse(details,
+                viewHosts.toArray(new HostJoinVO[viewHosts.size()]));
+        assert listHosts != null && listHosts.size() == 1 : "There should be one host returned";
+        return listHosts.get(0);
+    }
+
+    @Override
     public SwiftResponse createSwiftResponse(Swift swift) {
         SwiftResponse swiftResponse = new SwiftResponse();
         swiftResponse.setId(swift.getUuid());
@@ -908,16 +924,21 @@ public class ApiResponseHelper implements ResponseGenerator {
 
     }
 
-
-
     @Override
     public StoragePoolResponse createStoragePoolResponse(StoragePool pool) {
         List<StoragePoolJoinVO> viewPools = ApiDBUtils.newStoragePoolView(pool);
         List<StoragePoolResponse> listPools = ViewResponseHelper.createStoragePoolResponse(viewPools.toArray(new StoragePoolJoinVO[viewPools.size()]));
         assert listPools != null && listPools.size() == 1 : "There should be one storage pool returned";
         return listPools.get(0);
+    }
 
-
+    @Override
+    public StoragePoolForMigrationResponse createStoragePoolForMigrationResponse(StoragePool pool) {
+        List<StoragePoolJoinVO> viewPools = ApiDBUtils.newStoragePoolView(pool);
+        List<StoragePoolForMigrationResponse> listPools = ViewResponseHelper.createStoragePoolForMigrationResponse(
+                viewPools.toArray(new StoragePoolJoinVO[viewPools.size()]));
+        assert listPools != null && listPools.size() == 1 : "There should be one storage pool returned";
+        return listPools.get(0);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/query/ViewResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/ViewResponseHelper.java b/server/src/com/cloud/api/query/ViewResponseHelper.java
index dc2727e..827ae7b 100644
--- a/server/src/com/cloud/api/query/ViewResponseHelper.java
+++ b/server/src/com/cloud/api/query/ViewResponseHelper.java
@@ -30,6 +30,7 @@ import org.apache.cloudstack.api.response.DiskOfferingResponse;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
 import org.apache.cloudstack.api.response.EventResponse;
 import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import org.apache.cloudstack.api.response.InstanceGroupResponse;
 import org.apache.cloudstack.api.response.ProjectAccountResponse;
 import org.apache.cloudstack.api.response.ProjectInvitationResponse;
@@ -38,6 +39,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
 import org.apache.cloudstack.api.response.StoragePoolResponse;
+import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.api.response.VolumeResponse;
@@ -230,6 +232,24 @@ public class ViewResponseHelper {
         return new ArrayList<HostResponse>(vrDataList.values());
     }
 
+    public static List<HostForMigrationResponse> createHostForMigrationResponse(EnumSet<HostDetails> details,
+            HostJoinVO... hosts) {
+        Hashtable<Long, HostForMigrationResponse> vrDataList = new Hashtable<Long, HostForMigrationResponse>();
+        // Initialise the vrdatalist with the input data
+        for (HostJoinVO vr : hosts) {
+            HostForMigrationResponse vrData = vrDataList.get(vr.getId());
+            if ( vrData == null ) {
+                // first time encountering this vm
+                vrData = ApiDBUtils.newHostForMigrationResponse(vr, details);
+            } else {
+                // update tags
+                vrData = ApiDBUtils.fillHostForMigrationDetails(vrData, vr);
+            }
+            vrDataList.put(vr.getId(), vrData);
+        }
+        return new ArrayList<HostForMigrationResponse>(vrDataList.values());
+    }
+
     public static List<VolumeResponse> createVolumeResponse(VolumeJoinVO... volumes) {
         Hashtable<Long, VolumeResponse> vrDataList = new Hashtable<Long, VolumeResponse>();
         for (VolumeJoinVO vr : volumes) {
@@ -265,6 +285,23 @@ public class ViewResponseHelper {
         return new ArrayList<StoragePoolResponse>(vrDataList.values());
     }
 
+    public static List<StoragePoolForMigrationResponse> createStoragePoolForMigrationResponse(StoragePoolJoinVO... pools) {
+        Hashtable<Long, StoragePoolForMigrationResponse> vrDataList = new Hashtable<Long, StoragePoolForMigrationResponse>();
+        // Initialise the vrdatalist with the input data
+        for (StoragePoolJoinVO vr : pools) {
+            StoragePoolForMigrationResponse vrData = vrDataList.get(vr.getId());
+            if ( vrData == null ) {
+                // first time encountering this vm
+                vrData = ApiDBUtils.newStoragePoolForMigrationResponse(vr);
+            } else {
+                // update tags
+                vrData = ApiDBUtils.fillStoragePoolForMigrationDetails(vrData, vr);
+            }
+            vrDataList.put(vr.getId(), vrData);
+        }
+        return new ArrayList<StoragePoolForMigrationResponse>(vrDataList.values());
+    }
+
 
     public static List<AccountResponse> createAccountResponse(AccountJoinVO... accounts) {
         List<AccountResponse> respList = new ArrayList<AccountResponse>();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/query/dao/HostJoinDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/HostJoinDao.java b/server/src/com/cloud/api/query/dao/HostJoinDao.java
index 1a21299..f526ca3 100644
--- a/server/src/com/cloud/api/query/dao/HostJoinDao.java
+++ b/server/src/com/cloud/api/query/dao/HostJoinDao.java
@@ -21,6 +21,7 @@ import java.util.List;
 
 import org.apache.cloudstack.api.ApiConstants.HostDetails;
 import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import com.cloud.api.query.vo.HostJoinVO;
 import com.cloud.host.Host;
 import com.cloud.utils.db.GenericDao;
@@ -31,6 +32,10 @@ public interface HostJoinDao extends GenericDao<HostJoinVO, Long> {
 
     HostResponse setHostResponse(HostResponse response, HostJoinVO host);
 
+    HostForMigrationResponse newHostForMigrationResponse(HostJoinVO host, EnumSet<HostDetails> details);
+
+    HostForMigrationResponse setHostForMigrationResponse(HostForMigrationResponse response, HostJoinVO host);
+
     List<HostJoinVO> newHostView(Host group);
 
     List<HostJoinVO> searchByIds(Long... ids);