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

[26/35] 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 c

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java
index 1adff40..7796529 100644
--- a/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java
@@ -29,6 +29,7 @@ import javax.inject.Inject;
 
 import org.apache.cloudstack.api.ApiConstants.HostDetails;
 import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.api.response.HostForMigrationResponse;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
@@ -190,10 +191,6 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
     }
 
 
-
-
-
-
     @Override
     public HostResponse setHostResponse(HostResponse response, HostJoinVO host) {
         String tag = host.getTag();
@@ -208,7 +205,137 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
         return response;
     }
 
+    @Override
+    public HostForMigrationResponse newHostForMigrationResponse(HostJoinVO host, EnumSet<HostDetails> details) {
+        HostForMigrationResponse hostResponse = new HostForMigrationResponse();
+        hostResponse.setId(host.getUuid());
+        hostResponse.setCapabilities(host.getCapabilities());
+        hostResponse.setClusterId(host.getClusterUuid());
+        hostResponse.setCpuNumber(host.getCpus());
+        hostResponse.setZoneId(host.getZoneUuid());
+        hostResponse.setDisconnectedOn(host.getDisconnectedOn());
+        hostResponse.setHypervisor(host.getHypervisorType());
+        hostResponse.setHostType(host.getType());
+        hostResponse.setLastPinged(new Date(host.getLastPinged()));
+        hostResponse.setManagementServerId(host.getManagementServerId());
+        hostResponse.setName(host.getName());
+        hostResponse.setPodId(host.getPodUuid());
+        hostResponse.setRemoved(host.getRemoved());
+        hostResponse.setCpuSpeed(host.getSpeed());
+        hostResponse.setState(host.getStatus());
+        hostResponse.setIpAddress(host.getPrivateIpAddress());
+        hostResponse.setVersion(host.getVersion());
+        hostResponse.setCreated(host.getCreated());
+
+        if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity)
+                || details.contains(HostDetails.stats) || details.contains(HostDetails.events)) {
 
+            hostResponse.setOsCategoryId(host.getOsCategoryUuid());
+            hostResponse.setOsCategoryName(host.getOsCategoryName());
+            hostResponse.setZoneName(host.getZoneName());
+            hostResponse.setPodName(host.getPodName());
+            if ( host.getClusterId() > 0) {
+                hostResponse.setClusterName(host.getClusterName());
+                hostResponse.setClusterType(host.getClusterType().toString());
+            }
+        }
+
+        DecimalFormat decimalFormat = new DecimalFormat("#.##");
+        if (host.getType() == Host.Type.Routing) {
+            if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity)) {
+                // set allocated capacities
+                Long mem = host.getMemReservedCapacity() + host.getMemUsedCapacity();
+                Long cpu = host.getCpuReservedCapacity() + host.getCpuReservedCapacity();
+
+                hostResponse.setMemoryAllocated(mem);
+                hostResponse.setMemoryTotal(host.getTotalMemory());
+
+                String hostTags = host.getTag();
+                hostResponse.setHostTags(host.getTag());
+
+                String haTag = ApiDBUtils.getHaTag();
+                if (haTag != null && !haTag.isEmpty() && hostTags != null && !hostTags.isEmpty()) {
+                    if (haTag.equalsIgnoreCase(hostTags)) {
+                        hostResponse.setHaHost(true);
+                    } else {
+                        hostResponse.setHaHost(false);
+                    }
+                } else {
+                    hostResponse.setHaHost(false);
+                }
+
+                hostResponse.setHypervisorVersion(host.getHypervisorVersion());
+
+                String cpuAlloc = decimalFormat.format(((float) cpu / (float) (host.getCpus() * host.getSpeed())) * 100f) + "%";
+                hostResponse.setCpuAllocated(cpuAlloc);
+                String cpuWithOverprovisioning = new Float(host.getCpus() * host.getSpeed() * ApiDBUtils.getCpuOverprovisioningFactor()).toString();
+                hostResponse.setCpuWithOverprovisioning(cpuWithOverprovisioning);
+            }
+
+            if (details.contains(HostDetails.all) || details.contains(HostDetails.stats)) {
+                // set CPU/RAM/Network stats
+                String cpuUsed = null;
+                HostStats hostStats = ApiDBUtils.getHostStatistics(host.getId());
+                if (hostStats != null) {
+                    float cpuUtil = (float) hostStats.getCpuUtilization();
+                    cpuUsed = decimalFormat.format(cpuUtil) + "%";
+                    hostResponse.setCpuUsed(cpuUsed);
+                    hostResponse.setMemoryUsed((new Double(hostStats.getUsedMemory())).longValue());
+                    hostResponse.setNetworkKbsRead((new Double(hostStats.getNetworkReadKBs())).longValue());
+                    hostResponse.setNetworkKbsWrite((new Double(hostStats.getNetworkWriteKBs())).longValue());
+
+                }
+            }
+
+        } else if (host.getType() == Host.Type.SecondaryStorage) {
+            StorageStats secStorageStats = ApiDBUtils.getSecondaryStorageStatistics(host.getId());
+            if (secStorageStats != null) {
+                hostResponse.setDiskSizeTotal(secStorageStats.getCapacityBytes());
+                hostResponse.setDiskSizeAllocated(secStorageStats.getByteUsed());
+            }
+        }
+
+        hostResponse.setLocalStorageActive(ApiDBUtils.isLocalStorageActiveOnHost(host.getId()));
+
+        if (details.contains(HostDetails.all) || details.contains(HostDetails.events)) {
+            Set<com.cloud.host.Status.Event> possibleEvents = host.getStatus().getPossibleEvents();
+            if ((possibleEvents != null) && !possibleEvents.isEmpty()) {
+                String events = "";
+                Iterator<com.cloud.host.Status.Event> iter = possibleEvents.iterator();
+                while (iter.hasNext()) {
+                    com.cloud.host.Status.Event event = iter.next();
+                    events += event.toString();
+                    if (iter.hasNext()) {
+                        events += "; ";
+                    }
+                }
+                hostResponse.setEvents(events);
+            }
+        }
+
+        hostResponse.setResourceState(host.getResourceState().toString());
+
+        // set async job
+        hostResponse.setJobId(host.getJobUuid());
+        hostResponse.setJobStatus(host.getJobStatus());
+
+        hostResponse.setObjectName("host");
+
+        return hostResponse;
+    }
+
+    @Override
+    public HostForMigrationResponse setHostForMigrationResponse(HostForMigrationResponse response, HostJoinVO host) {
+        String tag = host.getTag();
+        if (tag != null) {
+            if (response.getHostTags() != null && response.getHostTags().length() > 0) {
+                response.setHostTags(response.getHostTags() + "," + tag);
+            } else {
+                response.setHostTags(tag);
+            }
+        }
+        return response;
+    }
 
     @Override
     public List<HostJoinVO> newHostView(Host host) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java
index bbb0242..b7e467f 100644
--- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java
+++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDao.java
@@ -18,6 +18,7 @@ package com.cloud.api.query.dao;
 
 import java.util.List;
 
+import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse;
 import org.apache.cloudstack.api.response.StoragePoolResponse;
 
 import com.cloud.api.query.vo.StoragePoolJoinVO;
@@ -30,6 +31,11 @@ public interface StoragePoolJoinDao extends GenericDao<StoragePoolJoinVO, Long>
 
     StoragePoolResponse setStoragePoolResponse(StoragePoolResponse response, StoragePoolJoinVO host);
 
+    StoragePoolForMigrationResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO host);
+
+    StoragePoolForMigrationResponse setStoragePoolForMigrationResponse(StoragePoolForMigrationResponse response,
+            StoragePoolJoinVO host);
+
     List<StoragePoolJoinVO> newStoragePoolView(StoragePool group);
 
     List<StoragePoolJoinVO> searchByIds(Long... spIds);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
index 58968df..34b88ba 100644
--- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java
@@ -22,6 +22,7 @@ import java.util.List;
 import javax.ejb.Local;
 import javax.inject.Inject;
 
+import org.apache.cloudstack.api.response.StoragePoolForMigrationResponse;
 import org.apache.cloudstack.api.response.StoragePoolResponse;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
@@ -108,10 +109,6 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
         return poolResponse;
     }
 
-
-
-
-
     @Override
     public StoragePoolResponse setStoragePoolResponse(StoragePoolResponse response, StoragePoolJoinVO sp) {
         String tag = sp.getTag();
@@ -126,7 +123,61 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
         return response;
     }
 
+    @Override
+    public StoragePoolForMigrationResponse newStoragePoolForMigrationResponse(StoragePoolJoinVO pool) {
+        StoragePoolForMigrationResponse poolResponse = new StoragePoolForMigrationResponse();
+        poolResponse.setId(pool.getUuid());
+        poolResponse.setName(pool.getName());
+        poolResponse.setState(pool.getStatus());
+        poolResponse.setPath(pool.getPath());
+        poolResponse.setIpAddress(pool.getHostAddress());
+        poolResponse.setZoneId(pool.getZoneUuid());
+        poolResponse.setZoneName(pool.getZoneName());
+        if (pool.getPoolType() != null) {
+            poolResponse.setType(pool.getPoolType().toString());
+        }
+        poolResponse.setPodId(pool.getPodUuid());
+        poolResponse.setPodName(pool.getPodName());
+        poolResponse.setCreated(pool.getCreated());
+        poolResponse.setScope(pool.getScope().toString());
+
 
+        long allocatedSize = pool.getUsedCapacity() +  pool.getReservedCapacity();
+        poolResponse.setDiskSizeTotal(pool.getCapacityBytes());
+        poolResponse.setDiskSizeAllocated(allocatedSize);
+
+        //TODO: StatsCollector does not persist data
+        StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());
+        if (stats != null) {
+            Long used = stats.getByteUsed();
+            poolResponse.setDiskSizeUsed(used);
+        }
+
+        poolResponse.setClusterId(pool.getClusterUuid());
+        poolResponse.setClusterName(pool.getClusterName());
+        poolResponse.setTags(pool.getTag());
+
+        // set async job
+        poolResponse.setJobId(pool.getJobUuid());
+        poolResponse.setJobStatus(pool.getJobStatus());
+
+        poolResponse.setObjectName("storagepool");
+        return poolResponse;
+    }
+
+    @Override
+    public StoragePoolForMigrationResponse setStoragePoolForMigrationResponse(StoragePoolForMigrationResponse response,
+            StoragePoolJoinVO sp) {
+        String tag = sp.getTag();
+        if (tag != null) {
+            if ( response.getTags() != null && response.getTags().length() > 0){
+                response.setTags(response.getTags() + "," + tag);
+            } else {
+                response.setTags(tag);
+            }
+        }
+        return response;
+    }
 
     @Override
     public List<StoragePoolJoinVO> newStoragePoolView(StoragePool host) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/server/ManagementServerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index db8db8a..16127a2 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -214,6 +214,7 @@ import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd;
 import org.apache.cloudstack.api.command.admin.vlan.ListVlanIpRangesCmd;
 import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
 import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
+import org.apache.cloudstack.api.command.admin.vm.MigrateVirtualMachineWithVolumeCmd;
 import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
 import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd;
 import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd;
@@ -256,6 +257,7 @@ import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd;
 import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToSnapshotCmd;
 import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 
@@ -329,6 +331,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     @Inject
     private StorageManager _storageMgr;
     @Inject
+    private VolumeManager _volumeMgr;
+    @Inject
     private VirtualMachineManager _itMgr;
     @Inject
     private HostPodDao _hostPodDao;
@@ -352,10 +356,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
     private LoadBalancerDao _loadbalancerDao;
     @Inject
     private HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
-
     private List<HostAllocator> _hostAllocators;
-
-	@Inject
+    @Inject
+    private List<StoragePoolAllocator> _storagePoolAllocators;
+    @Inject
     private ConfigurationManager _configMgr;
     @Inject
     private ResourceTagDao _resourceTagDao;
@@ -679,12 +683,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         Object resourceState = cmd.getResourceState();
         Object haHosts = cmd.getHaHost();
 
-        Pair<List<HostVO>, Integer> result = searchForServers(cmd.getStartIndex(), cmd.getPageSizeVal(), name, type, state, zoneId, pod, cluster, id, keyword, resourceState, haHosts);
+        Pair<List<HostVO>, Integer> result = searchForServers(cmd.getStartIndex(), cmd.getPageSizeVal(), name, type,
+                state, zoneId, pod, cluster, id, keyword, resourceState, haHosts, null, null);
         return new Pair<List<? extends Host>, Integer>(result.first(), result.second());
     }
 
     @Override
-    public Pair<Pair<List<? extends Host>, Integer>, List<? extends Host>> listHostsForMigrationOfVM(Long vmId, Long startIndex, Long pageSize) {
+    public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>
+            listHostsForMigrationOfVM(Long vmId, Long startIndex, Long pageSize) {
         // access check - only root admin can migrate VM
         Account caller = UserContext.current().getCaller();
         if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
@@ -700,12 +706,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
             ex.addProxyObject(vm, vmId, "vmId");
             throw ex;
         }
-        // business logic
+
         if (vm.getState() != State.Running) {
             if (s_logger.isDebugEnabled()) {
                 s_logger.debug("VM is not Running, unable to migrate the vm" + vm);
             }
-            InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Running, unable to migrate the vm with specified id");
+            InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Running, unable to" +
+                    " migrate the vm with specified id");
             ex.addProxyObject(vm, vmId, "vmId");
             throw ex;
         }
@@ -715,17 +722,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
             if (s_logger.isDebugEnabled()) {
                 s_logger.debug(vm + " is not XenServer/VMware/KVM/OVM, cannot migrate this VM.");
             }
-            throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM only");
-        }
-        ServiceOfferingVO svcOffering = _offeringsDao.findById(vm.getServiceOfferingId());
-        if (svcOffering.getUseLocalStorage()) {
-            if (s_logger.isDebugEnabled()) {
-                s_logger.debug(vm + " is using Local Storage, cannot migrate this VM.");
-            }
-            throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate");
+            throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support " +
+                    "XenServer/VMware/KVM/Ovm only");
         }
+
         long srcHostId = vm.getHostId();
-        // why is this not HostVO?
         Host srcHost = _hostDao.findById(srcHostId);
         if (srcHost == null) {
             if (s_logger.isDebugEnabled()) {
@@ -737,32 +738,73 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
             ex.addProxyObject(vm, vmId, "vmId");
             throw ex;
         }
-        Long cluster = srcHost.getClusterId();
-        Type hostType = srcHost.getType();
-        if (s_logger.isDebugEnabled()) {
-            s_logger.debug("Searching for all hosts in cluster: " + cluster + " for migrating VM " + vm);
-        }
-
-        Pair<List<HostVO>, Integer> allHostsInClusterPair = searchForServers(startIndex, pageSize, null, hostType, null, null, null, cluster, null, null, null, null);
 
-        // filter out the current host
-        List<HostVO> allHostsInCluster = allHostsInClusterPair.first();
-        allHostsInCluster.remove(srcHost);
-        Pair<List<? extends Host>, Integer> otherHostsInCluster = new Pair<List <? extends Host>, Integer>(allHostsInCluster, new Integer(allHostsInClusterPair.second().intValue()-1));
+        // Check if the vm can be migrated with storage.
+        boolean canMigrateWithStorage = false;
+        HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(
+                srcHost.getHypervisorType(), srcHost.getHypervisorVersion());
+        if (capabilities != null) {
+            canMigrateWithStorage = capabilities.isStorageMotionSupported();
+        }
 
-        if (s_logger.isDebugEnabled()) {
-            s_logger.debug("Other Hosts in this cluster: " + allHostsInCluster);
+        // Check if the vm is using any disks on local storage.
+        VirtualMachineProfile<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+        List<VolumeVO> volumes = _volumeDao.findCreatedByInstance(vmProfile.getId());
+        boolean usesLocal = false;
+        for (VolumeVO volume : volumes) {
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+            DiskProfile diskProfile = new DiskProfile(volume, diskOffering, vmProfile.getHypervisorType());
+            if (diskProfile.useLocalStorage()) {
+                usesLocal = true;
+                break;
+            }
         }
 
-        if (s_logger.isDebugEnabled()) {
-            s_logger.debug("Calling HostAllocators to search for hosts in cluster: " + cluster + " having enough capacity and suitable for migration");
+        if (!canMigrateWithStorage && usesLocal) {
+            throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate");
         }
 
-        List<Host> suitableHosts = new ArrayList<Host>();
+        Type hostType = srcHost.getType();
+        Pair<List<HostVO>, Integer> allHostsPair = null;
+        List<HostVO> allHosts = null;
+        Map<Host, Boolean> requiresStorageMotion = new HashMap<Host, Boolean>();
+        DataCenterDeployment plan = null;
+        if (canMigrateWithStorage) {
+            allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, srcHost.getDataCenterId(), null,
+                    null, null, null, null, null, srcHost.getHypervisorType(), srcHost.getHypervisorVersion());
+            allHosts = allHostsPair.first();
+            allHosts.remove(srcHost);
+
+            // Check if the host has storage pools for all the volumes of the vm to be migrated.
+            for (Host host : allHosts) {
+                Map<Volume, List<StoragePool>> volumePools = findSuitablePoolsForVolumes(vmProfile, host);
+                if (volumePools.isEmpty()) {
+                    allHosts.remove(host);
+                } else {
+                    if (host.getClusterId() != srcHost.getClusterId() || usesLocal) {
+                        requiresStorageMotion.put(host, true);
+                    }
+                }
+            }
 
-        VirtualMachineProfile<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+            plan = new DataCenterDeployment(srcHost.getDataCenterId(), null, null, null, null, null);
+        } else {
+            Long cluster = srcHost.getClusterId();
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("Searching for all hosts in cluster " + cluster + " for migrating VM " + vm);
+            }
+            allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, null, null, cluster, null, null,
+                    null, null, null, null);
+            // Filter out the current host.
+            allHosts = allHostsPair.first();
+            allHosts.remove(srcHost);
+            plan = new DataCenterDeployment(srcHost.getDataCenterId(), srcHost.getPodId(), srcHost.getClusterId(),
+                    null, null, null);
+        }
 
-        DataCenterDeployment plan = new DataCenterDeployment(srcHost.getDataCenterId(), srcHost.getPodId(), srcHost.getClusterId(), null, null, null);
+        Pair<List<? extends Host>, Integer> otherHosts = new Pair<List <? extends Host>, Integer>(allHosts,
+                new Integer(allHosts.size()));
+        List<Host> suitableHosts = new ArrayList<Host>();
         ExcludeList excludes = new ExcludeList();
         excludes.addHost(srcHostId);
 
@@ -776,25 +818,174 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         }
 
         for (HostAllocator allocator : _hostAllocators) {
-            suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, HostAllocator.RETURN_UPTO_ALL, false);
+            if  (canMigrateWithStorage) {
+                suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, allHosts,
+                        HostAllocator.RETURN_UPTO_ALL, false);
+            } else {
+                suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes,
+                        HostAllocator.RETURN_UPTO_ALL, false);
+            }
+
             if (suitableHosts != null && !suitableHosts.isEmpty()) {
                 break;
             }
         }
 
-        if (suitableHosts.isEmpty()) {
-            s_logger.debug("No suitable hosts found");
-        } else {
-            if (s_logger.isDebugEnabled()) {
+        if (s_logger.isDebugEnabled()) {
+            if (suitableHosts.isEmpty()) {
+                s_logger.debug("No suitable hosts found");
+            } else {
                 s_logger.debug("Hosts having capacity and suitable for migration: " + suitableHosts);
             }
         }
 
-        return new Pair<Pair<List<? extends Host>, Integer>, List<? extends Host>>(otherHostsInCluster, suitableHosts);
+        return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> (otherHosts,
+                suitableHosts, requiresStorageMotion);
+    }
+
+    private Map<Volume, List<StoragePool>> findSuitablePoolsForVolumes(VirtualMachineProfile<VMInstanceVO> vmProfile,
+            Host host) {
+        List<VolumeVO> volumes = _volumeDao.findCreatedByInstance(vmProfile.getId());
+        Map<Volume, List<StoragePool>> suitableVolumeStoragePools = new HashMap<Volume, List<StoragePool>>();
+
+        // For each volume find list of suitable storage pools by calling the allocators
+        for (VolumeVO volume : volumes) {
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+            DiskProfile diskProfile = new DiskProfile(volume, diskOffering, vmProfile.getHypervisorType());
+            DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(),
+                    host.getClusterId(), host.getId(), null, null);
+            ExcludeList avoid = new ExcludeList();
+
+            boolean foundPools = false;
+            for (StoragePoolAllocator allocator : _storagePoolAllocators) {
+                List<StoragePool> poolList = allocator.allocateToPool(diskProfile, vmProfile, plan, avoid,
+                        StoragePoolAllocator.RETURN_UPTO_ALL);
+                if (poolList != null && !poolList.isEmpty()) {
+                    suitableVolumeStoragePools.put(volume, poolList);
+                    foundPools = true;
+                    break;
+                }
+            }
+
+            if (!foundPools) {
+                suitableVolumeStoragePools.clear();
+                break;
+            }
+        }
+
+        return suitableVolumeStoragePools;
+    }
+
+    @Override
+    public Pair<List<? extends StoragePool>, List<? extends StoragePool>> listStoragePoolsForMigrationOfVolume(Long volumeId) {
+        // Access check - only root administrator can migrate volumes.
+        Account caller = UserContext.current().getCaller();
+        if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("Caller is not a root admin, permission denied to migrate the volume");
+            }
+            throw new PermissionDeniedException("No permission to migrate volume, only root admin can migrate a volume");
+        }
+
+        VolumeVO volume = _volumeDao.findById(volumeId);
+        if (volume == null) {
+            InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find volume with" +
+                    " specified id.");
+            ex.addProxyObject(volume, volumeId, "volumeId");
+            throw ex;
+        }
+
+        // Volume must be attached to an instance for live migration.
+        List<StoragePool> allPools = new ArrayList<StoragePool>();
+        List<StoragePool> suitablePools = new ArrayList<StoragePool>();
+        Long instanceId = volume.getInstanceId();
+        VMInstanceVO vm = null;
+        if (instanceId != null) {
+            vm = _vmInstanceDao.findById(instanceId);
+        }
+
+        // Check that the VM is in correct state.
+        if (vm == null || vm.getState() != State.Running) {
+            s_logger.info("Volume " + volume + " isn't attached to any running vm. Only volumes attached to a running" +
+                    " VM can be migrated.");
+            return new Pair<List<? extends StoragePool>, List<? extends StoragePool>>(allPools, suitablePools);
+        }
+
+        // Volume must be in Ready state to be migrated.
+        if (!Volume.State.Ready.equals(volume.getState())) {
+            s_logger.info("Volume " + volume + " must be in ready state for migration.");
+            return new Pair<List<? extends StoragePool>, List<? extends StoragePool>>(allPools, suitablePools);
+        }
+
+        if (!_volumeMgr.volumeOnSharedStoragePool(volume)) {
+            s_logger.info("Volume " + volume + " is on local storage. It cannot be migrated to another pool.");
+            return new Pair<List<? extends StoragePool>, List<? extends StoragePool>>(allPools, suitablePools);
+        }
+
+        // Check if the underlying hypervisor supports storage motion.
+        boolean storageMotionSupported = false;
+        Long hostId = vm.getHostId();
+        if (hostId != null) {
+            HostVO host = _hostDao.findById(hostId);
+            HypervisorCapabilitiesVO capabilities = null;
+            if (host != null) {
+                capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(host.getHypervisorType(),
+                        host.getHypervisorVersion());
+            } else {
+                s_logger.error("Details of the host on which the vm " + vm + ", to which volume "+ volume + " is "
+                        + "attached, couldn't be retrieved.");
+            }
+
+            if (capabilities != null) {
+                storageMotionSupported = capabilities.isStorageMotionSupported();
+            } else {
+                s_logger.error("Capabilities for host " + host + " couldn't be retrieved.");
+            }
+        }
+
+        if (storageMotionSupported) {
+            // Source pool of the volume.
+            StoragePoolVO srcVolumePool = _poolDao.findById(volume.getPoolId());
+
+            // Get all the pools available. Only shared pools are considered because only a volume on a shared pools
+            // can be live migrated while the virtual machine stays on the same host.
+            List<StoragePoolVO> storagePools = _poolDao.findPoolsByTags(volume.getDataCenterId(),
+                    volume.getPodId(), srcVolumePool.getClusterId(), null);
+            storagePools.remove(srcVolumePool);
+            for (StoragePoolVO pool : storagePools) {
+                if (pool.isShared()) {
+                    allPools.add((StoragePool)this.dataStoreMgr.getPrimaryDataStore(pool.getId()));
+                }
+            }
+
+            // Get all the suitable pools.
+            // Exclude the current pool from the list of pools to which the volume can be migrated.
+            ExcludeList avoid = new ExcludeList();
+            avoid.addPool(srcVolumePool.getId());
+
+            // Volume stays in the same cluster after migration.
+            DataCenterDeployment plan = new DataCenterDeployment(volume.getDataCenterId(), volume.getPodId(),
+                    srcVolumePool.getClusterId(), null, null, null);
+            VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+            DiskProfile diskProfile = new DiskProfile(volume, diskOffering, profile.getHypervisorType());
+
+            // Call the storage pool allocator to find the list of storage pools.
+            for (StoragePoolAllocator allocator : _storagePoolAllocators) {
+                List<StoragePool> pools = allocator.allocateToPool(diskProfile, profile, plan, avoid,
+                        StoragePoolAllocator.RETURN_UPTO_ALL);
+                if (pools != null && !pools.isEmpty()) {
+                    suitablePools.addAll(pools);
+                    break;
+                }
+            }
+        }
+        return new Pair<List<? extends StoragePool>, List<? extends StoragePool>>(allPools, suitablePools);
     }
 
     private Pair<List<HostVO>, Integer> searchForServers(Long startIndex, Long pageSize, Object name, Object type, Object state, Object zone, Object pod, Object cluster, Object id, Object keyword,
-            Object resourceState, Object haHosts) {
+            Object resourceState, Object haHosts, Object hypervisorType, Object hypervisorVersion) {
         Filter searchFilter = new Filter(HostVO.class, "id", Boolean.TRUE, startIndex, pageSize);
 
         SearchBuilder<HostVO> sb = _hostDao.createSearchBuilder();
@@ -806,6 +997,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ);
         sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ);
         sb.and("resourceState", sb.entity().getResourceState(), SearchCriteria.Op.EQ);
+        sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
+        sb.and("hypervisorVersion", sb.entity().getHypervisorVersion(), SearchCriteria.Op.EQ);
 
         String haTag = _haMgr.getHaTag();
         SearchBuilder<HostTagVO> hostTagSearch = null;
@@ -855,6 +1048,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         if (cluster != null) {
             sc.setParameters("clusterId", cluster);
         }
+        if (hypervisorType != null) {
+            sc.setParameters("hypervisorType", hypervisorType);
+        }
+        if (hypervisorVersion != null) {
+            sc.setParameters("hypervisorVersion", hypervisorVersion);
+        }
 
         if (resourceState != null) {
             sc.setParameters("resourceState", resourceState);
@@ -1969,6 +2168,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         cmdList.add(CancelMaintenanceCmd.class);
         cmdList.add(DeleteHostCmd.class);
         cmdList.add(ListHostsCmd.class);
+        cmdList.add(FindHostsForMigrationCmd.class);
         cmdList.add(PrepareForMaintenanceCmd.class);
         cmdList.add(ReconnectHostCmd.class);
         cmdList.add(UpdateHostCmd.class);
@@ -2025,6 +2225,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         cmdList.add(DeletePoolCmd.class);
         cmdList.add(ListS3sCmd.class);
         cmdList.add(ListStoragePoolsCmd.class);
+        cmdList.add(FindStoragePoolsForMigrationCmd.class);
         cmdList.add(PreparePrimaryStorageForMaintenanceCmd.class);
         cmdList.add(UpdateStoragePoolCmd.class);
         cmdList.add(AddSwiftCmd.class);
@@ -2064,6 +2265,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
         cmdList.add(ReleasePublicIpRangeCmd.class);
         cmdList.add(AssignVMCmd.class);
         cmdList.add(MigrateVMCmd.class);
+        cmdList.add(MigrateVirtualMachineWithVolumeCmd.class);
         cmdList.add(RecoverVMCmd.class);
         cmdList.add(CreatePrivateGatewayCmd.class);
         cmdList.add(CreateVPCOfferingCmd.class);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/storage/VolumeManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeManager.java b/server/src/com/cloud/storage/VolumeManager.java
index 2101038..d198e5d 100644
--- a/server/src/com/cloud/storage/VolumeManager.java
+++ b/server/src/com/cloud/storage/VolumeManager.java
@@ -18,6 +18,8 @@
  */
 package com.cloud.storage;
 
+import java.util.Map;
+
 import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
@@ -25,12 +27,15 @@ import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 
+import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.exception.ConcurrentOperationException;
 import com.cloud.exception.InsufficientStorageCapacityException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.StorageUnavailableException;
+import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.Volume.Type;
 import com.cloud.user.Account;
@@ -80,6 +85,9 @@ public interface VolumeManager extends VolumeApiService {
 
     Volume migrateVolume(MigrateVolumeCmd cmd);
 
+    <T extends VMInstanceVO> void migrateVolumes(T vm, VirtualMachineTO vmTo, Host srcHost, Host destHost,
+            Map<VolumeVO, StoragePoolVO> volumeToPool);
+
     boolean storageMigration(
             VirtualMachineProfile<? extends VirtualMachine> vm,
             StoragePool destPool);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/storage/VolumeManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java
index 1e8edaf..e57d393 100644
--- a/server/src/com/cloud/storage/VolumeManagerImpl.java
+++ b/server/src/com/cloud/storage/VolumeManagerImpl.java
@@ -28,6 +28,7 @@ import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.HashMap;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
@@ -42,6 +43,7 @@ import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
+import org.apache.cloudstack.engine.subsystem.api.storage.CommandResult;
 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.DataStoreProviderManager;
@@ -68,6 +70,7 @@ import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.AttachVolumeAnswer;
 import com.cloud.agent.api.AttachVolumeCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.alert.AlertManager;
 import com.cloud.api.ApiDBUtils;
@@ -102,11 +105,13 @@ import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.exception.StorageUnavailableException;
+import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorGuruManager;
 import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
+import com.cloud.hypervisor.HypervisorCapabilitiesVO;
 import com.cloud.network.NetworkModel;
 import com.cloud.org.Grouping;
 import com.cloud.resource.ResourceManager;
@@ -2003,7 +2008,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
     public Volume migrateVolume(MigrateVolumeCmd cmd) {
         Long volumeId = cmd.getVolumeId();
         Long storagePoolId = cmd.getStoragePoolId();
-        
+
         VolumeVO vol = _volsDao.findById(volumeId);
         if (vol == null) {
             throw new InvalidParameterValueException(
@@ -2015,9 +2020,39 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                     "Volume must be in ready state");
         }
 
-        if (vol.getInstanceId() != null) {
-            throw new InvalidParameterValueException(
-                    "Volume needs to be dettached from VM");
+        boolean liveMigrateVolume = false;
+        Long instanceId = vol.getInstanceId();
+        VMInstanceVO vm = null;
+        if (instanceId != null) {
+            vm = _vmInstanceDao.findById(instanceId);
+        }
+
+        if (vm != null && vm.getState() == State.Running) {
+            // Check if the underlying hypervisor supports storage motion.
+            Long hostId = vm.getHostId();
+            if (hostId != null) {
+                HostVO host = _hostDao.findById(hostId);
+                HypervisorCapabilitiesVO capabilities = null;
+                if (host != null) {
+                    capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(host.getHypervisorType(),
+                            host.getHypervisorVersion());
+                }
+
+                if (capabilities != null) {
+                    liveMigrateVolume = capabilities.isStorageMotionSupported();
+                }
+            }
+        }
+
+        // If the disk is not attached to any VM then it can be moved. Otherwise, it needs to be attached to a vm
+        // running on a hypervisor that supports storage motion so that it be be migrated.
+        if (instanceId != null && !liveMigrateVolume) {
+            throw new InvalidParameterValueException("Volume needs to be detached from VM");
+        }
+
+        if (liveMigrateVolume && !cmd.isLiveMigrate()) {
+            throw new InvalidParameterValueException("The volume " + vol + "is attached to a vm and for migrating it " +
+                    "the parameter livemigrate should be specified");
         }
 
         StoragePool destPool = (StoragePool)this.dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
@@ -2032,12 +2067,15 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                     "Migration of volume from local storage pool is not supported");
         }
 
-        Volume newVol = migrateVolume(vol, destPool);
+        Volume newVol = null;
+        if (liveMigrateVolume) {
+            newVol = liveMigrateVolume(vol, destPool);
+        } else {
+            newVol = migrateVolume(vol, destPool);
+        }
         return newVol;
     }
 
-    
-    
     @DB
     protected Volume migrateVolume(Volume volume, StoragePool destPool) {
         VolumeInfo vol = this.volFactory.getVolume(volume.getId());
@@ -2058,6 +2096,66 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         }
     }
 
+    @DB
+    protected Volume liveMigrateVolume(Volume volume, StoragePool destPool) {
+        VolumeInfo vol = this.volFactory.getVolume(volume.getId());
+        AsyncCallFuture<VolumeApiResult> future = this.volService.migrateVolume(vol, (DataStore)destPool);
+        try {
+            VolumeApiResult result = future.get();
+            if (result.isFailed()) {
+                s_logger.debug("migrate volume failed:" + result.getResult());
+                return null;
+            }
+            return result.getVolume();
+        } catch (InterruptedException e) {
+            s_logger.debug("migrate volume failed", e);
+            return null;
+        } catch (ExecutionException e) {
+            s_logger.debug("migrate volume failed", e);
+            return null;
+        }
+    }
+
+    @Override
+    public <T extends VMInstanceVO> void migrateVolumes(T vm, VirtualMachineTO vmTo, Host srcHost, Host destHost,
+            Map<VolumeVO, StoragePoolVO> volumeToPool) {
+        // Check if all the vms being migrated belong to the vm.
+        // Check if the storage pool is of the right type.
+        // Create a VolumeInfo to DataStore map too.
+        Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
+        for (Map.Entry<VolumeVO, StoragePoolVO> entry : volumeToPool.entrySet()) {
+            VolumeVO volume = entry.getKey();
+            StoragePoolVO storagePool = entry.getValue();
+            StoragePool destPool = (StoragePool)this.dataStoreMgr.getDataStore(storagePool.getId(),
+                    DataStoreRole.Primary);
+
+            if (volume.getInstanceId() != vm.getId()) {
+                throw new CloudRuntimeException("Volume " + volume + " that has to be migrated doesn't belong to the" +
+                        " instance " + vm);
+            }
+
+            if (destPool == null) {
+                throw new CloudRuntimeException("Failed to find the destination storage pool " + storagePool.getId());
+            }
+
+            volumeMap.put(this.volFactory.getVolume(volume.getId()), (DataStore)destPool);
+        }
+
+        AsyncCallFuture<CommandResult> future = this.volService.migrateVolumes(volumeMap, vmTo, srcHost, destHost);
+        try {
+            CommandResult result = future.get();
+            if (result.isFailed()) {
+                s_logger.debug("Failed to migrated vm " + vm + " along with its volumes. " + result.getResult());
+                throw new CloudRuntimeException("Failed to migrated vm " + vm + " along with its volumes. " +
+                        result.getResult());
+            }
+        } catch (InterruptedException e) {
+            s_logger.debug("Failed to migrated vm " + vm + " along with its volumes.", e);
+        } catch (ExecutionException e) {
+            s_logger.debug("Failed to migrated vm " + vm + " along with its volumes.", e);
+        }
+    }
+
     @Override
     public boolean storageMigration(
             VirtualMachineProfile<? extends VirtualMachine> vm,

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 3ecdf42..bc6237f 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -46,6 +46,7 @@ import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd;
 import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
 import org.apache.cloudstack.engine.service.api.OrchestrationService;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.log4j.Logger;
 
@@ -108,6 +109,7 @@ import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.hypervisor.HypervisorCapabilitiesVO;
 import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
 import com.cloud.network.Network;
 import com.cloud.network.Network.IpAddresses;
@@ -3502,6 +3504,127 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         return migratedVm;
     }
 
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true)
+    public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost,
+            Map<String, String> volumeToPool) throws ResourceUnavailableException, ConcurrentOperationException,
+            ManagementServerException, VirtualMachineMigrationException {
+        // Access check - only root administrator can migrate VM.
+        Account caller = UserContext.current().getCaller();
+        if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("Caller is not a root admin, permission denied to migrate the VM");
+            }
+            throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!");
+        }
+
+        VMInstanceVO vm = _vmInstanceDao.findById(vmId);
+        if (vm == null) {
+            throw new InvalidParameterValueException("Unable to find the vm by id " + vmId);
+        }
+
+        if (vm.getState() != State.Running) {
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
+            }
+            CloudRuntimeException ex = new CloudRuntimeException("VM is not Running, unable to migrate the vm with" +
+                    " specified id");
+            ex.addProxyObject(vm, vmId, "vmId");
+            throw ex;
+        }
+
+        if (!vm.getHypervisorType().equals(HypervisorType.XenServer) &&
+                !vm.getHypervisorType().equals(HypervisorType.VMware) &&
+                !vm.getHypervisorType().equals(HypervisorType.KVM) &&
+                !vm.getHypervisorType().equals(HypervisorType.Ovm)) {
+            throw new InvalidParameterValueException("Unsupported hypervisor type for vm migration, we support" +
+                    " XenServer/VMware/KVM only");
+        }
+
+        long srcHostId = vm.getHostId();
+        Host srcHost = _resourceMgr.getHost(srcHostId);
+        // Check if src and destination hosts are valid and migrating to same host
+        if (destinationHost.getId() == srcHostId) {
+            throw new InvalidParameterValueException("Cannot migrate VM, VM is already present on this host, please" +
+                    " specify valid destination host to migrate the VM");
+        }
+
+        // Check if the source and destination hosts are of the same type and support storage motion.
+        if (!(srcHost.getHypervisorType().equals(destinationHost.getHypervisorType()) &&
+            srcHost.getHypervisorVersion().equals(destinationHost.getHypervisorVersion()))) {
+            throw new CloudRuntimeException("The source and destination hosts are not of the same type and version. " +
+                "Source hypervisor type and version: " + srcHost.getHypervisorType().toString() + " " +
+                srcHost.getHypervisorVersion() + ", Destination hypervisor type and version: " +
+                destinationHost.getHypervisorType().toString() + " " + destinationHost.getHypervisorVersion());
+        }
+
+        HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(
+                srcHost.getHypervisorType(), srcHost.getHypervisorVersion());
+        if (!capabilities.isStorageMotionSupported()) {
+            throw new CloudRuntimeException("Migration with storage isn't supported on hypervisor " +
+                    srcHost.getHypervisorType() + " of version " + srcHost.getHypervisorVersion());
+        }
+
+        // Check if destination host is up.
+        if (destinationHost.getStatus() != com.cloud.host.Status.Up ||
+                destinationHost.getResourceState() != ResourceState.Enabled){
+            throw new CloudRuntimeException("Cannot migrate VM, destination host is not in correct state, has " +
+                    "status: " + destinationHost.getStatus() + ", state: " + destinationHost.getResourceState());
+        }
+
+        List<VolumeVO> vmVolumes = _volsDao.findUsableVolumesForInstance(vm.getId());
+        Map<VolumeVO, StoragePoolVO> volToPoolObjectMap = new HashMap<VolumeVO, StoragePoolVO>();
+        if (!isVMUsingLocalStorage(vm) && destinationHost.getClusterId() == srcHost.getClusterId()) {
+            if (volumeToPool.isEmpty()) {
+                // If the destination host is in the same cluster and volumes do not have to be migrated across pools
+                // then fail the call. migrateVirtualMachine api should have been used.
+                throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost +
+                        " to destination host " + destinationHost + " doesn't involve migrating the volumes.");
+            }
+        }
+
+        if (!volumeToPool.isEmpty()) {
+            // Check if all the volumes and pools passed as parameters are valid.
+            for (Map.Entry<String, String> entry : volumeToPool.entrySet()) {
+                VolumeVO volume = _volsDao.findByUuid(entry.getKey());
+                StoragePoolVO pool = _storagePoolDao.findByUuid(entry.getValue());
+                if (volume == null) {
+                    throw new InvalidParameterValueException("There is no volume present with the given id " +
+                            entry.getKey());
+                } else if (pool == null) {
+                    throw new InvalidParameterValueException("There is no storage pool present with the given id " +
+                            entry.getValue());
+                } else {
+                    // Verify the volume given belongs to the vm.
+                    if (!vmVolumes.contains(volume)) {
+                        throw new InvalidParameterValueException("There volume " + volume + " doesn't belong to " +
+                                "the virtual machine "+ vm + " that has to be migrated");
+                    }
+                    volToPoolObjectMap.put(volume, pool);
+                }
+            }
+        }
+
+        // Check if all the volumes are in the correct state.
+        for (VolumeVO volume : vmVolumes) {
+            if (volume.getState() != Volume.State.Ready) {
+                throw new CloudRuntimeException("Volume " + volume + " of the VM is not in Ready state. Cannot " +
+                        "migrate the vm with its volumes.");
+            }
+        }
+
+        // Check max guest vm limit for the destinationHost.
+        HostVO destinationHostVO = _hostDao.findById(destinationHost.getId());
+        if(_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)){
+            throw new VirtualMachineMigrationException("Host name: " + destinationHost.getName() + ", hostId: " +
+                    destinationHost.getId() + " already has max running vms (count includes system VMs). Cannot" +
+                    " migrate to this host");
+        }
+
+        VMInstanceVO migratedVm = _itMgr.migrateWithStorage(vm, srcHostId, destinationHost.getId(), volToPoolObjectMap);
+        return migratedVm;
+    }
+
     @DB
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/vm/VirtualMachineManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/VirtualMachineManager.java b/server/src/com/cloud/vm/VirtualMachineManager.java
index 4a30d97..ea9f7bb 100644
--- a/server/src/com/cloud/vm/VirtualMachineManager.java
+++ b/server/src/com/cloud/vm/VirtualMachineManager.java
@@ -20,6 +20,7 @@ import java.net.URI;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
@@ -41,6 +42,7 @@ import com.cloud.service.ServiceOfferingVO;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
 import com.cloud.user.Account;
 import com.cloud.user.User;
 import com.cloud.utils.Pair;
@@ -109,6 +111,8 @@ public interface VirtualMachineManager extends Manager {
 
     <T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException;
 
+    <T extends VMInstanceVO> T migrateWithStorage(T vm, long srcId, long destId, Map<VolumeVO, StoragePoolVO> volumeToPool) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException;
+
     <T extends VMInstanceVO> T reboot(T vm, Map<VirtualMachineProfile.Param, Object> params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException;
 
     <T extends VMInstanceVO> T advanceReboot(T vm, Map<VirtualMachineProfile.Param, Object> params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
index 19f4005..2ecece2 100755
--- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -38,7 +38,12 @@ import javax.naming.ConfigurationException;
 
 import com.cloud.capacity.CapacityManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
+import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 
 import com.cloud.dc.*;
 import com.cloud.agent.api.*;
@@ -50,6 +55,8 @@ import com.cloud.agent.Listener;
 import com.cloud.agent.api.StartupRoutingCommand.VmState;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.agent.manager.Commands;
 import com.cloud.agent.manager.allocator.HostAllocator;
 import com.cloud.alert.AlertManager;
@@ -57,6 +64,7 @@ import com.cloud.cluster.ClusterManager;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.ConfigurationManager;
 import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.dc.dao.ClusterDao;
 import com.cloud.dc.DataCenter;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.HostPodVO;
@@ -111,10 +119,13 @@ import com.cloud.storage.Volume;
 import com.cloud.storage.Volume.Type;
 import com.cloud.storage.VolumeManager;
 import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSCategoryDao;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.StoragePoolHostDao;
+import com.cloud.storage.snapshot.SnapshotManager;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.User;
@@ -164,6 +175,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
     @Inject
     protected ServiceOfferingDao _offeringDao;
     @Inject
+    protected DiskOfferingDao _diskOfferingDao;
+    @Inject
     protected VMTemplateDao _templateDao;
     @Inject
     protected UserDao _userDao;
@@ -202,13 +215,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
     @Inject
     protected DataCenterDao _dcDao;
     @Inject
+    protected ClusterDao _clusterDao;
+    @Inject
     protected PrimaryDataStoreDao _storagePoolDao;
     @Inject
     protected HypervisorGuruManager _hvGuruMgr;
     @Inject
     protected NetworkDao _networkDao;
     @Inject
+    protected StoragePoolHostDao _poolHostDao;
+    @Inject
     protected VMSnapshotDao _vmSnapshotDao;
+    @Inject
+    protected VolumeDataFactory volFactory;
 
     protected List<DeploymentPlanner> _planners;
     public List<DeploymentPlanner> getPlanners() {
@@ -226,10 +245,16 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
 		this._hostAllocators = _hostAllocators;
 	}
 
-	@Inject
+    @Inject
+    protected List<StoragePoolAllocator> _storagePoolAllocators;
+
+    @Inject
     protected ResourceManager _resourceMgr;
 
     @Inject
+    protected SnapshotManager _snapshotMgr;
+
+    @Inject
     protected VMSnapshotManager _vmSnapshotMgr = null;
     @Inject
     protected ClusterDetailsDao  _clusterDetailsDao;
@@ -1427,6 +1452,189 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
         }
     }
 
+    private Map<VolumeVO, StoragePoolVO> getPoolListForVolumesForMigration(VirtualMachineProfile<VMInstanceVO> profile,
+            Host host, Map<VolumeVO, StoragePoolVO> volumeToPool) {
+        List<VolumeVO> allVolumes = _volsDao.findUsableVolumesForInstance(profile.getId());
+        for (VolumeVO volume : allVolumes) {
+            StoragePoolVO pool = volumeToPool.get(volume);
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+            StoragePoolVO currentPool = _storagePoolDao.findById(volume.getPoolId());
+            if (pool != null) {
+                // Check if pool is accessible from the destination host and disk offering with which the volume was
+                // created is compliant with the pool type.
+                if (_poolHostDao.findByPoolHost(pool.getId(), host.getId()) == null ||
+                        pool.isLocal() != diskOffering.getUseLocalStorage()) {
+                    // Cannot find a pool for the volume. Throw an exception.
+                    throw new CloudRuntimeException("Cannot migrate volume " + volume + " to storage pool " + pool +
+                            " while migrating vm to host " + host + ". Either the pool is not accessible from the " +
+                            "host or because of the offering with which the volume is created it cannot be placed on " +
+                            "the given pool.");
+                } else if (pool.getId() == currentPool.getId()){
+                    // If the pool to migrate too is the same as current pool, remove the volume from the list of
+                    // volumes to be migrated.
+                    volumeToPool.remove(volume);
+                }
+            } else {
+                // Find a suitable pool for the volume. Call the storage pool allocator to find the list of pools.
+                DiskProfile diskProfile = new DiskProfile(volume, diskOffering, profile.getHypervisorType());
+                DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(),
+                        host.getClusterId(), host.getId(), null, null);
+                ExcludeList avoid = new ExcludeList();
+                boolean currentPoolAvailable = false;
+
+                for (StoragePoolAllocator allocator : _storagePoolAllocators) {
+                    List<StoragePool> poolList = allocator.allocateToPool(diskProfile, profile, plan, avoid,
+                            StoragePoolAllocator.RETURN_UPTO_ALL);
+                    if (poolList != null && !poolList.isEmpty()) {
+                        // Volume needs to be migrated. Pick the first pool from the list. Add a mapping to migrate the
+                        // volume to a pool only if it is required; that is the current pool on which the volume resides
+                        // is not available on the destination host.
+                        if (poolList.contains(currentPool)) {
+                            currentPoolAvailable = true;
+                        } else {
+                            volumeToPool.put(volume, _storagePoolDao.findByUuid(poolList.get(0).getUuid()));
+                        }
+
+                        break;
+                    }
+                }
+
+                if (!currentPoolAvailable && !volumeToPool.containsKey(volume)) {
+                    // Cannot find a pool for the volume. Throw an exception.
+                    throw new CloudRuntimeException("Cannot find a storage pool which is available for volume " +
+                            volume + " while migrating virtual machine " + profile.getVirtualMachine() + " to host " +
+                            host);
+                }
+            }
+        }
+
+        return volumeToPool;
+    }
+
+    private <T extends VMInstanceVO> void moveVmToMigratingState(T vm, Long hostId, ItWorkVO work)
+            throws ConcurrentOperationException {
+        // Put the vm in migrating state.
+        try {
+            if (!changeState(vm, Event.MigrationRequested, hostId, work, Step.Migrating)) {
+                s_logger.info("Migration cancelled because state has changed: " + vm);
+                throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
+            }
+        } catch (NoTransitionException e) {
+            s_logger.info("Migration cancelled because " + e.getMessage());
+            throw new ConcurrentOperationException("Migration cancelled because " + e.getMessage());
+        }
+    }
+
+    private <T extends VMInstanceVO> void moveVmOutofMigratingStateOnSuccess(T vm, Long hostId, ItWorkVO work)
+            throws ConcurrentOperationException {
+        // Put the vm in running state.
+        try {
+            if (!changeState(vm, Event.OperationSucceeded, hostId, work, Step.Started)) {
+                s_logger.error("Unable to change the state for " + vm);
+                throw new ConcurrentOperationException("Unable to change the state for " + vm);
+            }
+        } catch (NoTransitionException e) {
+            s_logger.error("Unable to change state due to " + e.getMessage());
+            throw new ConcurrentOperationException("Unable to change state due to " + e.getMessage());
+        }
+    }
+
+    @Override
+    public <T extends VMInstanceVO> T migrateWithStorage(T vm, long srcHostId, long destHostId,
+            Map<VolumeVO, StoragePoolVO> volumeToPool) throws ResourceUnavailableException, ConcurrentOperationException,
+            ManagementServerException, VirtualMachineMigrationException {
+
+        HostVO srcHost = _hostDao.findById(srcHostId);
+        HostVO destHost = _hostDao.findById(destHostId);
+        VirtualMachineGuru<T> vmGuru = getVmGuru(vm);
+
+        DataCenterVO dc = _dcDao.findById(destHost.getDataCenterId());
+        HostPodVO pod = _podDao.findById(destHost.getPodId());
+        Cluster cluster = _clusterDao.findById(destHost.getClusterId());
+        DeployDestination destination = new DeployDestination(dc, pod, cluster, destHost);
+
+        // Create a map of which volume should go in which storage pool.
+        long vmId = vm.getId();
+        vm = vmGuru.findById(vmId);
+        VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+        volumeToPool = getPoolListForVolumesForMigration(profile, destHost, volumeToPool);
+
+        // If none of the volumes have to be migrated, fail the call. Administrator needs to make a call for migrating
+        // a vm and not migrating a vm with storage.
+        if (volumeToPool.isEmpty()) {
+            throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost +
+                    " to destination host " + destHost + " doesn't involve migrating the volumes.");
+        }
+
+        short alertType = AlertManager.ALERT_TYPE_USERVM_MIGRATE;
+        if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
+            alertType = AlertManager.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE;
+        } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
+            alertType = AlertManager.ALERT_TYPE_CONSOLE_PROXY_MIGRATE;
+        }
+
+        _networkMgr.prepareNicForMigration(profile, destination);
+        this.volumeMgr.prepareForMigration(profile, destination);
+        HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
+        VirtualMachineTO to = hvGuru.implement(profile);
+
+        ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
+        work.setStep(Step.Prepare);
+        work.setResourceType(ItWorkVO.ResourceType.Host);
+        work.setResourceId(destHostId);
+        work = _workDao.persist(work);
+
+        // Put the vm in migrating state.
+        vm.setLastHostId(srcHostId);
+        moveVmToMigratingState(vm, destHostId, work);
+
+        boolean migrated = false;
+        try {
+            // Migrate the vm and its volume.
+            this.volumeMgr.migrateVolumes(vm, to, srcHost, destHost, volumeToPool);
+
+            // Put the vm back to running state.
+            moveVmOutofMigratingStateOnSuccess(vm, destHost.getId(), work);
+
+            try {
+                if (!checkVmOnHost(vm, destHostId)) {
+                    s_logger.error("Vm not found on destination host. Unable to complete migration for " + vm);
+                    try {
+                        _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null);
+                    } catch (AgentUnavailableException e) {
+                        s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId);
+                    }
+                    cleanup(vmGuru, new VirtualMachineProfileImpl<T>(vm), work, Event.AgentReportStopped, true,
+                            _accountMgr.getSystemUser(), _accountMgr.getSystemAccount());
+                    return null;
+                }
+            } catch (OperationTimedoutException e) {
+                s_logger.warn("Error while checking the vm " + vm + " is on host " + destHost, e);
+            }
+
+            migrated = true;
+            return vm;
+        } finally {
+            if (!migrated) {
+                s_logger.info("Migration was unsuccessful.  Cleaning up: " + vm);
+                _alertMgr.sendAlert(alertType, srcHost.getDataCenterId(), srcHost.getPodId(), "Unable to migrate vm " +
+                        vm.getInstanceName() + " from host " + srcHost.getName() + " in zone " + dc.getName() +
+                        " and pod " + dc.getName(), "Migrate Command failed.  Please check logs.");
+                try {
+                    _agentMgr.send(destHostId, new Commands(cleanup(vm.getInstanceName())), null);
+                    stateTransitTo(vm, Event.OperationFailed, srcHostId);
+                } catch (AgentUnavailableException e) {
+                    s_logger.warn("Looks like the destination Host is unavailable for cleanup.", e);
+                } catch (NoTransitionException e) {
+                    s_logger.error("Error while transitioning vm from migrating to running state.", e);
+                }
+            }
+
+            work.setStep(Step.Done);
+            _workDao.update(work.getId(), work);
+        }
+    }
+
     @Override
     public VirtualMachineTO toVmTO(VirtualMachineProfile<? extends VMInstanceVO> profile) {
         HypervisorGuru hvGuru = _hvGuruMgr.getGuru(profile.getVirtualMachine().getHypervisorType());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/test/com/cloud/vm/MockUserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
index fd826d9..0d0a8f4 100644
--- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java
+++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
@@ -367,6 +367,14 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager,
     }
 
     @Override
+    public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map<String, String> volumeToPool)
+            throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException,
+            VirtualMachineMigrationException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public UserVm moveVMToUser(AssignVMCmd moveUserVMCmd)
             throws ResourceAllocationException, ConcurrentOperationException,
             ResourceUnavailableException, InsufficientCapacityException {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
index 4917e77..94ddea6 100755
--- a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
+++ b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import javax.ejb.Local;
 import javax.naming.ConfigurationException;
 
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.springframework.stereotype.Component;
 
 import com.cloud.agent.api.to.NicTO;
@@ -45,6 +46,7 @@ import com.cloud.service.ServiceOfferingVO;
 import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
 import com.cloud.user.Account;
 import com.cloud.user.User;
 import com.cloud.utils.Pair;
@@ -143,6 +145,14 @@ public class MockVirtualMachineManagerImpl extends ManagerBase implements Virtua
     }
 
     @Override
+    public <T extends VMInstanceVO> T migrateWithStorage(T vm, long srcHostId, long destHostId,
+            Map<VolumeVO, StoragePoolVO> volumeToPool) throws ResourceUnavailableException,
+            ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public VMInstanceVO findByIdAndType(Type type, long vmId) {
         // TODO Auto-generated method stub
         return null;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/21ce3bef/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java b/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
index 322f051..dd51e74 100644
--- a/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
+++ b/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
@@ -31,6 +31,41 @@ import com.cloud.service.ServiceOfferingVO;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.VolumeManager;
 import com.cloud.storage.VolumeVO;
+import com.cloud.agent.api.PrepareForMigrationAnswer;
+import com.cloud.agent.api.PrepareForMigrationCommand;
+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.CheckVirtualMachineAnswer;
+import com.cloud.agent.api.CheckVirtualMachineCommand;
+import com.cloud.capacity.CapacityManager;
+import com.cloud.configuration.ConfigurationManager;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.dc.dao.ClusterDao;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.HostPodDao;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.ManagementServerException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.exception.VirtualMachineMigrationException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.HypervisorGuru;
+import com.cloud.hypervisor.HypervisorGuruManager;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.NetworkManager;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.StoragePoolHostVO;
+import com.cloud.storage.VolumeManager;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.user.*;
@@ -41,20 +76,33 @@ import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
 import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
 import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.VMSnapshotManager;
+import com.cloud.vm.VirtualMachine.Event;
+import com.cloud.vm.VirtualMachine.State;
+
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+
 import org.junit.Test;
 import org.junit.Before;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.*;
 
-
 import java.lang.reflect.Field;
 import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
 
 public class VirtualMachineManagerImplTest {
 
-
         @Spy VirtualMachineManagerImpl _vmMgr = new VirtualMachineManagerImpl();
         @Mock
         VolumeManager _storageMgr;
@@ -106,6 +154,25 @@ public class VirtualMachineManagerImplTest {
         List<VolumeVO> _rootVols;
         @Mock
         ItWorkVO _work;
+
+        @Mock ClusterDao _clusterDao;
+        @Mock HostPodDao _podDao;
+        @Mock DataCenterDao _dcDao;
+        @Mock DiskOfferingDao _diskOfferingDao;
+        @Mock PrimaryDataStoreDao _storagePoolDao;
+        @Mock StoragePoolHostDao _poolHostDao;
+        @Mock NetworkManager _networkMgr;
+        @Mock HypervisorGuruManager _hvGuruMgr;
+        @Mock VMSnapshotManager _vmSnapshotMgr;
+
+        // Mock objects for vm migration with storage test.
+        @Mock DiskOfferingVO _diskOfferingMock;
+        @Mock StoragePoolVO _srcStoragePoolMock;
+        @Mock StoragePoolVO _destStoragePoolMock;
+        @Mock HostVO _srcHostMock;
+        @Mock HostVO _destHostMock;
+        @Mock Map<VolumeVO, StoragePoolVO> _volumeToPoolMock;
+
         @Before
         public void setup(){
             MockitoAnnotations.initMocks(this);
@@ -122,6 +189,16 @@ public class VirtualMachineManagerImplTest {
             _vmMgr._nodeId = 1L;
             _vmMgr._workDao = _workDao;
             _vmMgr._agentMgr = _agentMgr;
+            _vmMgr._podDao = _podDao;
+            _vmMgr._clusterDao = _clusterDao;
+            _vmMgr._dcDao = _dcDao;
+            _vmMgr._diskOfferingDao = _diskOfferingDao;
+            _vmMgr._storagePoolDao = _storagePoolDao;
+            _vmMgr._poolHostDao= _poolHostDao;
+            _vmMgr._networkMgr = _networkMgr;
+            _vmMgr._hvGuruMgr = _hvGuruMgr;
+            _vmMgr._vmSnapshotMgr = _vmSnapshotMgr;
+            _vmMgr._vmDao = _vmInstanceDao;
 
             when(_vmMock.getId()).thenReturn(314l);
             when(_vmInstance.getId()).thenReturn(1L);
@@ -204,5 +281,155 @@ public class VirtualMachineManagerImplTest {
         return serviceOffering;
     }
 
+    private void initializeMockConfigForMigratingVmWithVolumes() throws OperationTimedoutException,
+        ResourceUnavailableException {
+
+        // Mock the source and destination hosts.
+        when(_srcHostMock.getId()).thenReturn(5L);
+        when(_destHostMock.getId()).thenReturn(6L);
+        when(_hostDao.findById(5L)).thenReturn(_srcHostMock);
+        when(_hostDao.findById(6L)).thenReturn(_destHostMock);
+
+        // Mock the vm being migrated.
+        when(_vmMock.getId()).thenReturn(1L);
+        when(_vmMock.getHypervisorType()).thenReturn(HypervisorType.XenServer);
+        when(_vmMock.getState()).thenReturn(State.Running).thenReturn(State.Running).thenReturn(State.Migrating)
+            .thenReturn(State.Migrating);
+        when(_vmMock.getHostId()).thenReturn(5L);
+        when(_vmInstance.getId()).thenReturn(1L);
+        when(_vmInstance.getServiceOfferingId()).thenReturn(2L);
+        when(_vmInstance.getInstanceName()).thenReturn("myVm");
+        when(_vmInstance.getHostId()).thenReturn(5L);
+        when(_vmInstance.getType()).thenReturn(VirtualMachine.Type.User);
+        when(_vmInstance.getState()).thenReturn(State.Running).thenReturn(State.Running).thenReturn(State.Migrating)
+            .thenReturn(State.Migrating);
+
+        // Mock the work item.
+        when(_workDao.persist(any(ItWorkVO.class))).thenReturn(_work);
+        when(_workDao.update("1", _work)).thenReturn(true);
+        when(_work.getId()).thenReturn("1");
+        doNothing().when(_work).setStep(ItWorkVO.Step.Done);
+
+        // Mock the vm guru and the user vm object that gets returned.
+        _vmMgr._vmGurus = new HashMap<VirtualMachine.Type, VirtualMachineGuru<? extends VMInstanceVO>>();
+        UserVmManagerImpl userVmManager = mock(UserVmManagerImpl.class);
+        _vmMgr.registerGuru(VirtualMachine.Type.User, userVmManager);
+        when(userVmManager.findById(anyLong())).thenReturn(_vmMock);
+
+        // Mock the iteration over all the volumes of an instance.
+        Iterator<VolumeVO> volumeIterator = mock(Iterator.class);
+        when(_volsDao.findUsableVolumesForInstance(anyLong())).thenReturn(_rootVols);
+        when(_rootVols.iterator()).thenReturn(volumeIterator);
+        when(volumeIterator.hasNext()).thenReturn(true, false);
+        when(volumeIterator.next()).thenReturn(_volumeMock);
+
+        // Mock the disk offering and pool objects for a volume.
+        when(_volumeMock.getDiskOfferingId()).thenReturn(5L);
+        when(_volumeMock.getPoolId()).thenReturn(200L);
+        when(_diskOfferingDao.findById(anyLong())).thenReturn(_diskOfferingMock);
+        when(_storagePoolDao.findById(anyLong())).thenReturn(_srcStoragePoolMock);
+
+        // Mock the volume to pool mapping.
+        when(_volumeToPoolMock.get(_volumeMock)).thenReturn(_destStoragePoolMock);
+        when(_destStoragePoolMock.getId()).thenReturn(201L);
+        when(_srcStoragePoolMock.getId()).thenReturn(200L);
+        when(_destStoragePoolMock.isLocal()).thenReturn(false);
+        when(_diskOfferingMock.getUseLocalStorage()).thenReturn(false);
+        when(_poolHostDao.findByPoolHost(anyLong(), anyLong())).thenReturn(mock(StoragePoolHostVO.class));
+
+        // Mock hypervisor guru.
+        HypervisorGuru guruMock = mock(HypervisorGuru.class);
+        when(_hvGuruMgr.getGuru(HypervisorType.XenServer)).thenReturn(guruMock);
+
+        when(_srcHostMock.getClusterId()).thenReturn(3L);
+        when(_destHostMock.getClusterId()).thenReturn(3L);
+
+        // Mock the commands and answers to the agent.
+        PrepareForMigrationAnswer prepAnswerMock = mock(PrepareForMigrationAnswer.class);
+        when(prepAnswerMock.getResult()).thenReturn(true);
+        when(_agentMgr.send(anyLong(), isA(PrepareForMigrationCommand.class))).thenReturn(prepAnswerMock);
+
+        MigrateWithStorageAnswer migAnswerMock = mock(MigrateWithStorageAnswer.class);
+        when(migAnswerMock.getResult()).thenReturn(true);
+        when(_agentMgr.send(anyLong(), isA(MigrateWithStorageCommand.class))).thenReturn(migAnswerMock);
+
+        MigrateWithStorageReceiveAnswer migRecAnswerMock = mock(MigrateWithStorageReceiveAnswer.class);
+        when(migRecAnswerMock.getResult()).thenReturn(true);
+        when(_agentMgr.send(anyLong(), isA(MigrateWithStorageReceiveCommand.class))).thenReturn(migRecAnswerMock);
+
+        MigrateWithStorageSendAnswer migSendAnswerMock = mock(MigrateWithStorageSendAnswer.class);
+        when(migSendAnswerMock.getResult()).thenReturn(true);
+        when(_agentMgr.send(anyLong(), isA(MigrateWithStorageSendCommand.class))).thenReturn(migSendAnswerMock);
+
+        MigrateWithStorageCompleteAnswer migCompleteAnswerMock = mock(MigrateWithStorageCompleteAnswer.class);
+        when(migCompleteAnswerMock.getResult()).thenReturn(true);
+        when(_agentMgr.send(anyLong(), isA(MigrateWithStorageCompleteCommand.class))).thenReturn(migCompleteAnswerMock);
+
+        CheckVirtualMachineAnswer checkVmAnswerMock = mock(CheckVirtualMachineAnswer.class);
+        when(checkVmAnswerMock.getResult()).thenReturn(true);
+        when(checkVmAnswerMock.getState()).thenReturn(State.Running);
+        when(_agentMgr.send(anyLong(), isA(CheckVirtualMachineCommand.class))).thenReturn(checkVmAnswerMock);
+
+        // Mock the state transitions of vm.
+        Pair<Long, Long> opaqueMock = new Pair<Long, Long> (_vmMock.getHostId(), _destHostMock.getId());
+        when(_vmSnapshotMgr.hasActiveVMSnapshotTasks(anyLong())).thenReturn(false);
+        when(_vmInstanceDao.updateState(State.Running, Event.MigrationRequested, State.Migrating, _vmMock, opaqueMock))
+            .thenReturn(true);
+        when(_vmInstanceDao.updateState(State.Migrating, Event.OperationSucceeded, State.Running, _vmMock, opaqueMock))
+            .thenReturn(true);
+    }
+
+    // Check migration of a vm with its volumes within a cluster.
+    @Test
+    public void testMigrateWithVolumeWithinCluster() throws ResourceUnavailableException, ConcurrentOperationException,
+        ManagementServerException, VirtualMachineMigrationException, OperationTimedoutException {
 
+        initializeMockConfigForMigratingVmWithVolumes();
+        when(_srcHostMock.getClusterId()).thenReturn(3L);
+        when(_destHostMock.getClusterId()).thenReturn(3L);
+
+        _vmMgr.migrateWithStorage(_vmInstance, _srcHostMock.getId(), _destHostMock.getId(), _volumeToPoolMock);
+    }
+
+    // Check migration of a vm with its volumes across a cluster.
+    @Test
+    public void testMigrateWithVolumeAcrossCluster() throws ResourceUnavailableException, ConcurrentOperationException,
+        ManagementServerException, VirtualMachineMigrationException, OperationTimedoutException {
+
+        initializeMockConfigForMigratingVmWithVolumes();
+        when(_srcHostMock.getClusterId()).thenReturn(3L);
+        when(_destHostMock.getClusterId()).thenReturn(4L);
+
+        _vmMgr.migrateWithStorage(_vmInstance, _srcHostMock.getId(), _destHostMock.getId(), _volumeToPoolMock);
+    }
+
+    // Check migration of a vm fails when src and destination pool are not of same type; that is, one is shared and
+    // other is local.
+    @Test(expected=CloudRuntimeException.class)
+    public void testMigrateWithVolumeFail1() throws ResourceUnavailableException, ConcurrentOperationException,
+        ManagementServerException, VirtualMachineMigrationException, OperationTimedoutException {
+
+        initializeMockConfigForMigratingVmWithVolumes();
+        when(_srcHostMock.getClusterId()).thenReturn(3L);
+        when(_destHostMock.getClusterId()).thenReturn(3L);
+
+        when(_destStoragePoolMock.isLocal()).thenReturn(true);
+        when(_diskOfferingMock.getUseLocalStorage()).thenReturn(false);
+
+        _vmMgr.migrateWithStorage(_vmInstance, _srcHostMock.getId(), _destHostMock.getId(), _volumeToPoolMock);
+    }
+
+    // Check migration of a vm fails when vm is not in Running state.
+    @Test(expected=ConcurrentOperationException.class)
+    public void testMigrateWithVolumeFail2() throws ResourceUnavailableException, ConcurrentOperationException,
+        ManagementServerException, VirtualMachineMigrationException, OperationTimedoutException {
+
+        initializeMockConfigForMigratingVmWithVolumes();
+        when(_srcHostMock.getClusterId()).thenReturn(3L);
+        when(_destHostMock.getClusterId()).thenReturn(3L);
+
+        when(_vmMock.getState()).thenReturn(State.Stopped);
+
+        _vmMgr.migrateWithStorage(_vmInstance, _srcHostMock.getId(), _destHostMock.getId(), _volumeToPoolMock);
+    }
 }