You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ra...@apache.org on 2018/12/14 11:01:36 UTC

[cloudstack] branch master updated: Allow KVM VM live migration with ROOT volume on file storage type (#2997)

This is an automated email from the ASF dual-hosted git repository.

rafael pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/master by this push:
     new bf20940  Allow KVM VM live migration with ROOT volume on file storage type (#2997)
bf20940 is described below

commit bf209405e7d60b6a5abf87677d368c429359d98a
Author: Gabriel Beims Bräscher <ga...@gmail.com>
AuthorDate: Fri Dec 14 09:01:28 2018 -0200

    Allow KVM VM live migration with ROOT volume on file storage type (#2997)
    
    * Allow KVM VM live migration with ROOT volume on file
    
    * Allow KVM VM live migration with ROOT volume on file
    - Add JUnit tests
    
    * Address reviewers and change some variable names to ease future
    implementation (developers can easily guess the name and use
    autocomplete)
---
 .../com/cloud/agent/api/to/VirtualMachineTO.java   |   9 +
 api/src/main/java/com/cloud/vm/DiskProfile.java    |   1 +
 .../java/com/cloud/agent/api/MigrateCommand.java   |  20 ++
 .../KvmNonManagedStorageDataMotionStrategy.java    | 143 ++++++++++++
 .../motion/StorageSystemDataMotionStrategy.java    | 122 +++++++---
 ...g-engine-storage-datamotion-storage-context.xml |   2 +
 .../KvmNonManagedStorageSystemDataMotionTest.java  | 256 +++++++++++++++++++++
 .../StorageSystemDataMotionStrategyTest.java       | 181 ++++++++++++---
 .../hypervisor/kvm/resource/MigrateKVMAsync.java   |  41 +++-
 .../wrapper/LibvirtCreateCommandWrapper.java       |   2 +-
 .../wrapper/LibvirtMigrateCommandWrapper.java      |  56 ++++-
 .../kvm/storage/IscsiAdmStorageAdaptor.java        |   3 +-
 .../kvm/storage/KVMStoragePoolManager.java         |  27 ++-
 .../kvm/resource/LibvirtComputingResourceTest.java |   3 +-
 .../wrapper/LibvirtMigrateCommandWrapperTest.java  | 132 ++++++++++-
 .../CloudStackPrimaryDataStoreDriverImpl.java      |   4 +-
 .../com/cloud/hypervisor/HypervisorGuruBase.java   |   1 +
 17 files changed, 899 insertions(+), 104 deletions(-)

diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
index f977a1c..e5623ac 100644
--- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
@@ -28,6 +28,7 @@ public class VirtualMachineTO {
     private long id;
     private String name;
     private BootloaderType bootloader;
+    private VirtualMachine.State state;
     Type type;
     int cpus;
 
@@ -147,6 +148,14 @@ public class VirtualMachineTO {
         this.bootloader = bootloader;
     }
 
+    public VirtualMachine.State getState() {
+        return state;
+    }
+
+    public void setState(VirtualMachine.State state) {
+        this.state = state;
+    }
+
     public int getCpus() {
         return cpus;
     }
diff --git a/api/src/main/java/com/cloud/vm/DiskProfile.java b/api/src/main/java/com/cloud/vm/DiskProfile.java
index a1e5faa..2b76c68 100644
--- a/api/src/main/java/com/cloud/vm/DiskProfile.java
+++ b/api/src/main/java/com/cloud/vm/DiskProfile.java
@@ -72,6 +72,7 @@ public class DiskProfile {
             offering.isCustomized(),
             null);
         this.hyperType = hyperType;
+        this.provisioningType = offering.getProvisioningType();
     }
 
     public DiskProfile(DiskProfile dp) {
diff --git a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
index 3e7dfc1..e31287c 100644
--- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
@@ -19,7 +19,9 @@
 
 package com.cloud.agent.api;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import com.cloud.agent.api.to.VirtualMachineTO;
@@ -33,6 +35,7 @@ public class MigrateCommand extends Command {
     private boolean isWindows;
     private VirtualMachineTO vmTO;
     private boolean executeInSequence = false;
+    private List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
 
     protected MigrateCommand() {
     }
@@ -90,6 +93,14 @@ public class MigrateCommand extends Command {
         return executeInSequence;
     }
 
+    public List<MigrateDiskInfo> getMigrateDiskInfoList() {
+        return migrateDiskInfoList;
+    }
+
+    public void setMigrateDiskInfoList(List<MigrateDiskInfo> migrateDiskInfoList) {
+        this.migrateDiskInfoList = migrateDiskInfoList;
+    }
+
     public static class MigrateDiskInfo {
         public enum DiskType {
             FILE, BLOCK;
@@ -123,6 +134,7 @@ public class MigrateCommand extends Command {
         private final DriverType driverType;
         private final Source source;
         private final String sourceText;
+        private boolean isSourceDiskOnStorageFileSystem;
 
         public MigrateDiskInfo(final String serialNumber, final DiskType diskType, final DriverType driverType, final Source source, final String sourceText) {
             this.serialNumber = serialNumber;
@@ -151,5 +163,13 @@ public class MigrateCommand extends Command {
         public String getSourceText() {
             return sourceText;
         }
+
+        public boolean isSourceDiskOnStorageFileSystem() {
+            return isSourceDiskOnStorageFileSystem;
+        }
+
+        public void setSourceDiskOnStorageFileSystem(boolean isDiskOnFileSystemStorage) {
+            this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage;
+        }
     }
 }
diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageDataMotionStrategy.java
new file mode 100644
index 0000000..bb75a66
--- /dev/null
+++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageDataMotionStrategy.java
@@ -0,0 +1,143 @@
+/*
+ * 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.io.File;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.MigrateCommand;
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
+import com.cloud.agent.api.storage.CreateAnswer;
+import com.cloud.agent.api.storage.CreateCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.Host;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.VolumeVO;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.DiskProfile;
+
+/**
+ * Extends {@link StorageSystemDataMotionStrategy}, allowing KVM hosts to migrate VMs with the ROOT volume on a non managed local storage pool.
+ * As {@link StorageSystemDataMotionStrategy} is considering KVM, this implementation also migrates only from/to KVM hosts.
+ */
+public class KvmNonManagedStorageDataMotionStrategy extends StorageSystemDataMotionStrategy {
+
+    @Inject
+    private TemplateDataFactory templateDataFactory;
+
+    /**
+     * Uses the canHandle from the Super class {@link StorageSystemDataMotionStrategy}. If the storage pool is of file and the internalCanHandle from {@link StorageSystemDataMotionStrategy} CANT_HANDLE, returns the StrategyPriority.HYPERVISOR strategy priority. otherwise returns CANT_HANDLE.
+     * Note that the super implementation (override) is called by {@link #canHandle(Map, Host, Host)} which ensures that {@link #internalCanHandle(Map)} will be executed only if the source host is KVM.
+     */
+    @Override
+    protected StrategyPriority internalCanHandle(Map<VolumeInfo, DataStore> volumeMap) {
+        if (super.internalCanHandle(volumeMap) == StrategyPriority.CANT_HANDLE) {
+            Set<VolumeInfo> volumeInfoSet = volumeMap.keySet();
+
+            for (VolumeInfo volumeInfo : volumeInfoSet) {
+                StoragePoolVO storagePoolVO = _storagePoolDao.findById(volumeInfo.getPoolId());
+                if (storagePoolVO.getPoolType() != StoragePoolType.Filesystem && storagePoolVO.getPoolType() != StoragePoolType.NetworkFilesystem) {
+                    return StrategyPriority.CANT_HANDLE;
+                }
+            }
+            return StrategyPriority.HYPERVISOR;
+        }
+        return StrategyPriority.CANT_HANDLE;
+    }
+
+    /**
+     * Configures a {@link MigrateDiskInfo} object configured for migrating a File System volume and calls rootImageProvisioning.
+     */
+    @Override
+    protected MigrateCommand.MigrateDiskInfo configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath) {
+        return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(), MigrateCommand.MigrateDiskInfo.DiskType.FILE, MigrateCommand.MigrateDiskInfo.DriverType.QCOW2,
+                MigrateCommand.MigrateDiskInfo.Source.FILE, destPath);
+    }
+
+    /**
+     * Generates the volume path by appending the Volume UUID to the Libvirt destiny images path.</br>
+     * Example: /var/lib/libvirt/images/f3d49ecc-870c-475a-89fa-fd0124420a9b
+     */
+    @Override
+    protected String generateDestPath(VirtualMachineTO vmTO, VolumeVO srcVolume, Host destHost, StoragePoolVO destStoragePool, VolumeInfo destVolumeInfo) {
+        DiskOfferingVO diskOffering = _diskOfferingDao.findById(srcVolume.getDiskOfferingId());
+        DiskProfile diskProfile = new DiskProfile(destVolumeInfo, diskOffering, HypervisorType.KVM);
+        String templateUuid = getTemplateUuid(destVolumeInfo.getTemplateId());
+        CreateCommand rootImageProvisioningCommand = new CreateCommand(diskProfile, templateUuid, destStoragePool, true);
+
+        Answer rootImageProvisioningAnswer = _agentMgr.easySend(destHost.getId(), rootImageProvisioningCommand);
+
+        if (rootImageProvisioningAnswer == null) {
+            throw new CloudRuntimeException(String.format("Migration with storage of vm [%s] failed while provisioning root image", vmTO.getName()));
+        }
+
+        if (!rootImageProvisioningAnswer.getResult()) {
+            throw new CloudRuntimeException(String.format("Unable to modify target volume on the host [host id:%s, name:%s]", destHost.getId(), destHost.getName()));
+        }
+
+        String libvirtDestImgsPath = null;
+        if (rootImageProvisioningAnswer instanceof CreateAnswer) {
+            libvirtDestImgsPath = ((CreateAnswer)rootImageProvisioningAnswer).getVolume().getName();
+        }
+        // File.getAbsolutePath is used to keep the file separator as it should be and eliminate a verification to check if exists a file separator in the last character of libvirtDestImgsPath.
+        return new File(libvirtDestImgsPath, destVolumeInfo.getUuid()).getAbsolutePath();
+    }
+
+    /**
+     * Returns the template UUID with the given id. If the template ID is null, it returns null.
+     */
+    protected String getTemplateUuid(Long templateId) {
+        if (templateId == null) {
+            return null;
+        }
+        TemplateInfo templateImage = templateDataFactory.getTemplate(templateId, DataStoreRole.Image);
+        return templateImage.getUuid();
+    }
+
+    /**
+     * Sets the volume path as the volume UUID.
+     */
+    @Override
+    protected void setVolumePath(VolumeVO volume) {
+        volume.setPath(volume.getUuid());
+    }
+
+    /**
+     * Return true if the volume should be migrated. Currently only supports migrating volumes on storage pool of the type StoragePoolType.Filesystem.
+     * This ensures that volumes on shared storage are not migrated and those on local storage pools are migrated.
+     */
+    @Override
+    protected boolean shouldMigrateVolume(StoragePoolVO sourceStoragePool, Host destHost, StoragePoolVO destStoragePool) {
+        return sourceStoragePool.getPoolType() == StoragePoolType.Filesystem;
+    }
+}
diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
index 74ad8cb..adeb0d1 100644
--- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
@@ -24,6 +24,7 @@ import com.cloud.agent.api.storage.CopyVolumeAnswer;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
 import com.cloud.agent.api.MigrateAnswer;
 import com.cloud.agent.api.MigrateCommand;
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
 import com.cloud.agent.api.ModifyTargetsAnswer;
 import com.cloud.agent.api.ModifyTargetsCommand;
 import com.cloud.agent.api.PrepareForMigrationCommand;
@@ -41,7 +42,6 @@ import com.cloud.exception.OperationTimedoutException;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
-import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.resource.ResourceState;
 import com.cloud.storage.DataStoreRole;
@@ -49,6 +49,7 @@ import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.Snapshot;
 import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateVO;
@@ -136,16 +137,18 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
     private static final int LOCK_TIME_IN_SECONDS = 300;
     private static final String OPERATION_NOT_SUPPORTED = "This operation is not supported.";
 
-    @Inject private AgentManager _agentMgr;
+    @Inject
+    protected AgentManager _agentMgr;
     @Inject private ConfigurationDao _configDao;
     @Inject private DataStoreManager dataStoreMgr;
-    @Inject private DiskOfferingDao _diskOfferingDao;
+    @Inject
+    protected DiskOfferingDao _diskOfferingDao;
     @Inject private GuestOSCategoryDao _guestOsCategoryDao;
     @Inject private GuestOSDao _guestOsDao;
     @Inject private ClusterDao clusterDao;
     @Inject private HostDao _hostDao;
-    @Inject private HostDetailsDao hostDetailsDao;
-    @Inject private PrimaryDataStoreDao _storagePoolDao;
+    @Inject
+    protected PrimaryDataStoreDao _storagePoolDao;
     @Inject private SnapshotDao _snapshotDao;
     @Inject private SnapshotDataStoreDao _snapshotDataStoreDao;
     @Inject private SnapshotDetailsDao _snapshotDetailsDao;
@@ -251,29 +254,36 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
     }
 
     @Override
-    public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
+    public final StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
         if (HypervisorType.KVM.equals(srcHost.getHypervisorType())) {
-            Set<VolumeInfo> volumeInfoSet = volumeMap.keySet();
+            return internalCanHandle(volumeMap);
+        }
+        return StrategyPriority.CANT_HANDLE;
+    }
+
+    /**
+     * Handles migrating volumes on managed Storage.
+     */
+    protected StrategyPriority internalCanHandle(Map<VolumeInfo, DataStore> volumeMap) {
+        Set<VolumeInfo> volumeInfoSet = volumeMap.keySet();
 
-            for (VolumeInfo volumeInfo : volumeInfoSet) {
-                StoragePoolVO storagePoolVO = _storagePoolDao.findById(volumeInfo.getPoolId());
+        for (VolumeInfo volumeInfo : volumeInfoSet) {
+            StoragePoolVO storagePoolVO = _storagePoolDao.findById(volumeInfo.getPoolId());
 
-                if (storagePoolVO.isManaged()) {
-                    return StrategyPriority.HIGHEST;
-                }
+            if (storagePoolVO.isManaged()) {
+                return StrategyPriority.HIGHEST;
             }
+        }
 
-            Collection<DataStore> dataStores = volumeMap.values();
+        Collection<DataStore> dataStores = volumeMap.values();
 
-            for (DataStore dataStore : dataStores) {
-                StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStore.getId());
+        for (DataStore dataStore : dataStores) {
+            StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStore.getId());
 
-                if (storagePoolVO.isManaged()) {
-                    return StrategyPriority.HIGHEST;
-                }
+            if (storagePoolVO.isManaged()) {
+                return StrategyPriority.HIGHEST;
             }
         }
-
         return StrategyPriority.CANT_HANDLE;
     }
 
@@ -1677,10 +1687,12 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
     /**
      * For each disk to migrate:
-     *   Create a volume on the target storage system.
-     *   Make the newly created volume accessible to the target KVM host.
-     *   Send a command to the target KVM host to connect to the newly created volume.
-     * Send a command to the source KVM host to migrate the VM and its storage.
+     * <ul>
+     *  <li>Create a volume on the target storage system.</li>
+     *  <li>Make the newly created volume accessible to the target KVM host.</li>
+     *  <li>Send a command to the target KVM host to connect to the newly created volume.</li>
+     *  <li>Send a command to the source KVM host to migrate the VM and its storage.</li>
+     * </ul>
      */
     @Override
     public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMachineTO vmTO, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
@@ -1693,6 +1705,10 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
             verifyLiveMigrationMapForKVM(volumeDataStoreMap);
 
+            VMInstanceVO vmInstance = _vmDao.findById(vmTO.getId());
+            vmTO.setState(vmInstance.getState());
+            List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<MigrateDiskInfo>();
+
             Map<String, MigrateCommand.MigrateDiskInfo> migrateStorage = new HashMap<>();
             Map<VolumeInfo, VolumeInfo> srcVolumeInfoToDestVolumeInfo = new HashMap<>();
 
@@ -1702,6 +1718,11 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
                 VolumeVO srcVolume = _volumeDao.findById(srcVolumeInfo.getId());
                 StoragePoolVO destStoragePool = _storagePoolDao.findById(destDataStore.getId());
+                StoragePoolVO sourceStoragePool = _storagePoolDao.findById(srcVolumeInfo.getPoolId());
+
+                if (!shouldMigrateVolume(sourceStoragePool, destHost, destStoragePool)) {
+                    continue;
+                }
 
                 VolumeVO destVolume = duplicateVolumeOnAnotherStorage(srcVolume, destStoragePool);
                 VolumeInfo destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore);
@@ -1718,7 +1739,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
                 destVolume = _volumeDao.findById(destVolume.getId());
 
-                destVolume.setPath(destVolume.get_iScsiName());
+                setVolumePath(destVolume);
 
                 _volumeDao.update(destVolume.getId(), destVolume);
 
@@ -1728,13 +1749,11 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
 
                 _volumeService.grantAccess(destVolumeInfo, destHost, destDataStore);
 
-                String connectedPath = connectHostToVolume(destHost, destVolumeInfo.getPoolId(), destVolumeInfo.get_iScsiName());
+                String destPath = generateDestPath(vmTO, srcVolume, destHost, destStoragePool, destVolumeInfo);
 
-                MigrateCommand.MigrateDiskInfo migrateDiskInfo = new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(),
-                        MigrateCommand.MigrateDiskInfo.DiskType.BLOCK,
-                        MigrateCommand.MigrateDiskInfo.DriverType.RAW,
-                        MigrateCommand.MigrateDiskInfo.Source.DEV,
-                        connectedPath);
+                MigrateCommand.MigrateDiskInfo migrateDiskInfo = configureMigrateDiskInfo(srcVolumeInfo, destPath);
+                migrateDiskInfo.setSourceDiskOnStorageFileSystem(isStoragePoolTypeOfFile(sourceStoragePool));
+                migrateDiskInfoList.add(migrateDiskInfo);
 
                 migrateStorage.put(srcVolumeInfo.getPath(), migrateDiskInfo);
 
@@ -1765,6 +1784,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
             migrateCommand.setWait(StorageManager.KvmStorageOnlineMigrationWait.value());
 
             migrateCommand.setMigrateStorage(migrateStorage);
+            migrateCommand.setMigrateDiskInfoList(migrateDiskInfoList);
 
             String autoConvergence = _configDao.getValue(Config.KvmAutoConvergence.toString());
             boolean kvmAutoConvergence = Boolean.parseBoolean(autoConvergence);
@@ -1803,6 +1823,42 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
         }
     }
 
+    /**
+     * Returns true. This method was implemented considering the classes that extend this {@link StorageSystemDataMotionStrategy} and cannot migrate volumes from certain types of source storage pools and/or to a different kind of destiny storage pool.
+     */
+    protected boolean shouldMigrateVolume(StoragePoolVO sourceStoragePool, Host destHost, StoragePoolVO destStoragePool) {
+        return true;
+    }
+
+    /**
+     * Returns true if the storage pool type is {@link StoragePoolType.Filesystem}.
+     */
+    protected boolean isStoragePoolTypeOfFile(StoragePoolVO sourceStoragePool) {
+        return sourceStoragePool.getPoolType() == StoragePoolType.Filesystem;
+    }
+
+    /**
+     * Returns the iScsi connection path.
+     */
+    protected String generateDestPath(VirtualMachineTO vmTO, VolumeVO srcVolume, Host destHost, StoragePoolVO destStoragePool, VolumeInfo destVolumeInfo) {
+        return connectHostToVolume(destHost, destVolumeInfo.getPoolId(), destVolumeInfo.get_iScsiName());
+    }
+
+    /**
+     * Configures a {@link MigrateDiskInfo} object with disk type of BLOCK, Driver type RAW and Source DEV
+     */
+    protected MigrateCommand.MigrateDiskInfo configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath) {
+        return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(), MigrateCommand.MigrateDiskInfo.DiskType.BLOCK, MigrateCommand.MigrateDiskInfo.DriverType.RAW,
+                MigrateCommand.MigrateDiskInfo.Source.DEV, destPath);
+    }
+
+    /**
+     * Sets the volume path as the iScsi name in case of a configured iScsi.
+     */
+    protected void setVolumePath(VolumeVO volume) {
+        volume.setPath(volume.get_iScsiName());
+    }
+
     private void handlePostMigration(boolean success, Map<VolumeInfo, VolumeInfo> srcVolumeInfoToDestVolumeInfo, VirtualMachineTO vmTO, Host destHost) {
         if (!success) {
             try {
@@ -1913,7 +1969,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
         return _volumeDao.persist(newVol);
     }
 
-    private String connectHostToVolume(Host host, long storagePoolId, String iqn) {
+    protected String connectHostToVolume(Host host, long storagePoolId, String iqn) {
         ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, true);
 
         return sendModifyTargetsCommand(modifyTargetsCommand, host.getId()).get(0);
@@ -1990,10 +2046,6 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
             if (destStoragePoolVO == null) {
                 throw new CloudRuntimeException("Destination storage pool with ID " + dataStore.getId() + " was not located.");
             }
-
-            if (!destStoragePoolVO.isManaged()) {
-                throw new CloudRuntimeException("Migrating a volume online with KVM can currently only be done when moving to managed storage.");
-            }
         }
     }
 
diff --git a/engine/storage/datamotion/src/main/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml b/engine/storage/datamotion/src/main/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
index 1cefc51..5292419 100644
--- a/engine/storage/datamotion/src/main/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
+++ b/engine/storage/datamotion/src/main/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
@@ -34,4 +34,6 @@
         class="org.apache.cloudstack.storage.motion.HypervStorageMotionStrategy" />
     <bean id="storageSystemDataMotionStrategy"
         class="org.apache.cloudstack.storage.motion.StorageSystemDataMotionStrategy" />
+    <bean id="kvmNonManagedStorageSystemDataMotionStrategy"
+        class="org.apache.cloudstack.storage.motion.KvmNonManagedStorageDataMotionStrategy" />
 </beans>
diff --git a/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageSystemDataMotionTest.java b/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageSystemDataMotionTest.java
new file mode 100644
index 0000000..b344f83
--- /dev/null
+++ b/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageSystemDataMotionTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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 org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.storage.datastore.PrimaryDataStoreImpl;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.storage.volume.VolumeObject;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.MigrateCommand;
+import com.cloud.agent.api.storage.CreateAnswer;
+import com.cloud.agent.api.storage.CreateCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.host.HostVO;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.Storage;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.DiskProfile;
+
+@RunWith(MockitoJUnitRunner.class)
+public class KvmNonManagedStorageSystemDataMotionTest {
+
+    @Mock
+    private PrimaryDataStoreDao primaryDataStoreDao;
+
+    @Mock
+    private TemplateDataFactory templateDataFactory;
+
+    @Mock
+    private AgentManager agentManager;
+
+    @Mock
+    private DiskOfferingDao diskOfferingDao;
+
+    @Spy
+    @InjectMocks
+    private KvmNonManagedStorageDataMotionStrategy kvmNonManagedStorageDataMotionStrategy;
+
+    @Test
+    public void canHandleTestExpectHypervisorStrategyForKvm() {
+        canHandleExpectCannotHandle(HypervisorType.KVM, 1, StrategyPriority.HYPERVISOR);
+    }
+
+    @Test
+    public void canHandleTestExpectCannotHandle() {
+        HypervisorType[] hypervisorTypeArray = HypervisorType.values();
+        for (int i = 0; i < hypervisorTypeArray.length; i++) {
+            HypervisorType ht = hypervisorTypeArray[i];
+            if (ht.equals(HypervisorType.KVM)) {
+                continue;
+            }
+            canHandleExpectCannotHandle(ht, 0, StrategyPriority.CANT_HANDLE);
+        }
+    }
+
+    private void canHandleExpectCannotHandle(HypervisorType hypervisorType, int times, StrategyPriority expectedStrategyPriority) {
+        HostVO srcHost = new HostVO("sourceHostUuid");
+        srcHost.setHypervisorType(hypervisorType);
+        Mockito.doReturn(StrategyPriority.HYPERVISOR).when(kvmNonManagedStorageDataMotionStrategy).internalCanHandle(new HashMap<>());
+
+        StrategyPriority strategyPriority = kvmNonManagedStorageDataMotionStrategy.canHandle(new HashMap<>(), srcHost, new HostVO("destHostUuid"));
+
+        Mockito.verify(kvmNonManagedStorageDataMotionStrategy, Mockito.times(times)).internalCanHandle(new HashMap<>());
+        Assert.assertEquals(expectedStrategyPriority, strategyPriority);
+    }
+
+    @Test
+    public void internalCanHandleTestNonManaged() {
+        StoragePoolType[] storagePoolTypeArray = StoragePoolType.values();
+        for (int i = 0; i < storagePoolTypeArray.length; i++) {
+            Map<VolumeInfo, DataStore> volumeMap = configureTestInternalCanHandle(false, storagePoolTypeArray[i]);
+            StrategyPriority strategyPriority = kvmNonManagedStorageDataMotionStrategy.internalCanHandle(volumeMap);
+            if (storagePoolTypeArray[i] == StoragePoolType.Filesystem || storagePoolTypeArray[i] == StoragePoolType.NetworkFilesystem) {
+                Assert.assertEquals(StrategyPriority.HYPERVISOR, strategyPriority);
+            } else {
+                Assert.assertEquals(StrategyPriority.CANT_HANDLE, strategyPriority);
+            }
+        }
+    }
+
+    @Test
+    public void internalCanHandleTestIsManaged() {
+        StoragePoolType[] storagePoolTypeArray = StoragePoolType.values();
+        for (int i = 0; i < storagePoolTypeArray.length; i++) {
+            Map<VolumeInfo, DataStore> volumeMap = configureTestInternalCanHandle(true, storagePoolTypeArray[i]);
+            StrategyPriority strategyPriority = kvmNonManagedStorageDataMotionStrategy.internalCanHandle(volumeMap);
+            Assert.assertEquals(StrategyPriority.CANT_HANDLE, strategyPriority);
+        }
+    }
+
+    private Map<VolumeInfo, DataStore> configureTestInternalCanHandle(boolean isManagedStorage, StoragePoolType storagePoolType) {
+        VolumeObject volumeInfo = Mockito.spy(new VolumeObject());
+        Mockito.doReturn(0l).when(volumeInfo).getPoolId();
+        DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
+        Mockito.doReturn(0l).when(ds).getId();
+
+        Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
+        volumeMap.put(volumeInfo, ds);
+
+        StoragePoolVO storagePool = Mockito.spy(new StoragePoolVO());
+        Mockito.doReturn(storagePoolType).when(storagePool).getPoolType();
+
+        Mockito.doReturn(storagePool).when(primaryDataStoreDao).findById(0l);
+        Mockito.doReturn(isManagedStorage).when(storagePool).isManaged();
+        return volumeMap;
+    }
+
+    @Test
+    public void getTemplateUuidTestTemplateIdNotNull() {
+        String expectedTemplateUuid = prepareTestGetTemplateUuid();
+        String templateUuid = kvmNonManagedStorageDataMotionStrategy.getTemplateUuid(0l);
+        Assert.assertEquals(expectedTemplateUuid, templateUuid);
+    }
+
+    @Test
+    public void getTemplateUuidTestTemplateIdNull() {
+        prepareTestGetTemplateUuid();
+        String templateUuid = kvmNonManagedStorageDataMotionStrategy.getTemplateUuid(null);
+        Assert.assertEquals(null, templateUuid);
+    }
+
+    private String prepareTestGetTemplateUuid() {
+        TemplateInfo templateImage = Mockito.mock(TemplateInfo.class);
+        String expectedTemplateUuid = "template uuid";
+        Mockito.when(templateImage.getUuid()).thenReturn(expectedTemplateUuid);
+        Mockito.doReturn(templateImage).when(templateDataFactory).getTemplate(0l, DataStoreRole.Image);
+        return expectedTemplateUuid;
+    }
+
+    @Test
+    public void configureMigrateDiskInfoTest() {
+        VolumeObject srcVolumeInfo = Mockito.spy(new VolumeObject());
+        Mockito.doReturn("volume path").when(srcVolumeInfo).getPath();
+        MigrateCommand.MigrateDiskInfo migrateDiskInfo = kvmNonManagedStorageDataMotionStrategy.configureMigrateDiskInfo(srcVolumeInfo, "destPath");
+        Assert.assertEquals(MigrateCommand.MigrateDiskInfo.DiskType.FILE, migrateDiskInfo.getDiskType());
+        Assert.assertEquals(MigrateCommand.MigrateDiskInfo.DriverType.QCOW2, migrateDiskInfo.getDriverType());
+        Assert.assertEquals(MigrateCommand.MigrateDiskInfo.Source.FILE, migrateDiskInfo.getSource());
+        Assert.assertEquals("destPath", migrateDiskInfo.getSourceText());
+        Assert.assertEquals("volume path", migrateDiskInfo.getSerialNumber());
+    }
+
+    @Test
+    public void generateDestPathTest() {
+        configureAndVerifygenerateDestPathTest(true, false);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void generateDestPathTestExpectCloudRuntimeException() {
+        configureAndVerifygenerateDestPathTest(false, false);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void generateDestPathTestExpectCloudRuntimeException2() {
+        configureAndVerifygenerateDestPathTest(false, true);
+    }
+
+    private void configureAndVerifygenerateDestPathTest(boolean answerResult, boolean answerIsNull) {
+        String uuid = "f3d49ecc-870c-475a-89fa-fd0124420a9b";
+        String destPath = "/var/lib/libvirt/images/";
+
+        VirtualMachineTO vmTO = Mockito.mock(VirtualMachineTO.class);
+        Mockito.when(vmTO.getName()).thenReturn("vmName");
+
+        VolumeVO srcVolume = Mockito.spy(new VolumeVO("name", 0l, 0l, 0l, 0l, 0l, "folder", "path", Storage.ProvisioningType.THIN, 0l, Volume.Type.ROOT));
+        StoragePoolVO destStoragePool = Mockito.spy(new StoragePoolVO());
+
+        VolumeInfo destVolumeInfo = Mockito.spy(new VolumeObject());
+        Mockito.doReturn(0l).when(destVolumeInfo).getTemplateId();
+        Mockito.doReturn(0l).when(destVolumeInfo).getId();
+        Mockito.doReturn(Volume.Type.ROOT).when(destVolumeInfo).getVolumeType();
+        Mockito.doReturn("name").when(destVolumeInfo).getName();
+        Mockito.doReturn(0l).when(destVolumeInfo).getSize();
+        Mockito.doReturn(uuid).when(destVolumeInfo).getUuid();
+
+        DiskOfferingVO diskOffering = Mockito.spy(new DiskOfferingVO());
+        Mockito.doReturn(0l).when(diskOffering).getId();
+        Mockito.doReturn(diskOffering).when(diskOfferingDao).findById(0l);
+        DiskProfile diskProfile = Mockito.spy(new DiskProfile(destVolumeInfo, diskOffering, HypervisorType.KVM));
+
+        String templateUuid = Mockito.doReturn("templateUuid").when(kvmNonManagedStorageDataMotionStrategy).getTemplateUuid(0l);
+        CreateCommand rootImageProvisioningCommand = new CreateCommand(diskProfile, templateUuid, destStoragePool, true);
+        CreateAnswer createAnswer = Mockito.spy(new CreateAnswer(rootImageProvisioningCommand, "details"));
+        Mockito.doReturn(answerResult).when(createAnswer).getResult();
+
+        VolumeTO volumeTo = Mockito.mock(VolumeTO.class);
+        Mockito.doReturn(destPath).when(volumeTo).getName();
+        Mockito.doReturn(volumeTo).when(createAnswer).getVolume();
+
+        if (answerIsNull) {
+            Mockito.doReturn(null).when(agentManager).easySend(0l, rootImageProvisioningCommand);
+        } else {
+            Mockito.doReturn(createAnswer).when(agentManager).easySend(0l, rootImageProvisioningCommand);
+        }
+
+        String generatedDestPath = kvmNonManagedStorageDataMotionStrategy.generateDestPath(vmTO, srcVolume, new HostVO("sourceHostUuid"), destStoragePool, destVolumeInfo);
+
+        Assert.assertEquals(destPath + uuid, generatedDestPath);
+    }
+
+    @Test
+    public void shouldMigrateVolumeTest() {
+        StoragePoolVO sourceStoragePool = Mockito.spy(new StoragePoolVO());
+        HostVO destHost = new HostVO("guid");
+        StoragePoolVO destStoragePool = new StoragePoolVO();
+        StoragePoolType[] storagePoolTypes = StoragePoolType.values();
+        for (int i = 0; i < storagePoolTypes.length; i++) {
+            Mockito.doReturn(storagePoolTypes[i]).when(sourceStoragePool).getPoolType();
+            boolean result = kvmNonManagedStorageDataMotionStrategy.shouldMigrateVolume(sourceStoragePool, destHost, destStoragePool);
+            if (storagePoolTypes[i] == StoragePoolType.Filesystem) {
+                Assert.assertTrue(result);
+            } else {
+                Assert.assertFalse(result);
+            }
+        }
+    }
+}
diff --git a/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategyTest.java b/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategyTest.java
index ec85f7d..d76ff27 100644
--- a/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategyTest.java
+++ b/engine/storage/datamotion/src/test/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategyTest.java
@@ -18,67 +18,192 @@
  */
 package org.apache.cloudstack.storage.motion;
 
-import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.ImageStore;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.HashMap;
+import java.util.Map;
+
 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.PrimaryDataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.storage.datastore.PrimaryDataStoreImpl;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.image.store.ImageStoreImpl;
 import org.apache.cloudstack.storage.volume.VolumeObject;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
 import org.mockito.runners.MockitoJUnitRunner;
 
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.MockitoAnnotations.initMocks;
+import com.cloud.agent.api.MigrateCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.HostVO;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.ImageStore;
+import com.cloud.storage.Storage;
+import com.cloud.storage.Volume;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.VolumeVO;
 
 @RunWith(MockitoJUnitRunner.class)
 public class StorageSystemDataMotionStrategyTest {
 
+    @Spy
+    @InjectMocks
+    private StorageSystemDataMotionStrategy storageSystemDataMotionStrategy;
+
     @Mock
-    VolumeObject source;
+    private VolumeObject volumeObjectSource;
     @Mock
-    DataObject destination;
+    private DataObject dataObjectDestination;
     @Mock
-    PrimaryDataStore sourceStore;
+    private PrimaryDataStore primaryDataStoreSourceStore;
     @Mock
-    ImageStore destinationStore;
-
-    @InjectMocks
-    DataMotionStrategy strategy = new StorageSystemDataMotionStrategy();
+    private ImageStore destinationStore;
     @Mock
-    PrimaryDataStoreDao _storagePoolDao;
+    private PrimaryDataStoreDao primaryDataStoreDao;
 
-    @Before public void setUp() throws Exception {
-        sourceStore = mock(PrimaryDataStoreImpl.class);
+    @Before
+    public void setUp() throws Exception {
+        primaryDataStoreSourceStore = mock(PrimaryDataStoreImpl.class);
         destinationStore = mock(ImageStoreImpl.class);
-        source = mock(VolumeObject.class);
-        destination = mock(VolumeObject.class);
+        volumeObjectSource = mock(VolumeObject.class);
+        dataObjectDestination = mock(VolumeObject.class);
 
-                initMocks(strategy);
+        initMocks(storageSystemDataMotionStrategy);
     }
 
     @Test
     public void cantHandleSecondary() {
-        doReturn(sourceStore).when(source).getDataStore();
-        doReturn(DataStoreRole.Primary).when(sourceStore).getRole();
-        doReturn(destinationStore).when(destination).getDataStore();
+        doReturn(primaryDataStoreSourceStore).when(volumeObjectSource).getDataStore();
+        doReturn(DataStoreRole.Primary).when(primaryDataStoreSourceStore).getRole();
+        doReturn(destinationStore).when(dataObjectDestination).getDataStore();
         doReturn(DataStoreRole.Image).when((DataStore)destinationStore).getRole();
-        doReturn(sourceStore).when(source).getDataStore();
-        doReturn(destinationStore).when(destination).getDataStore();
+        doReturn(primaryDataStoreSourceStore).when(volumeObjectSource).getDataStore();
+        doReturn(destinationStore).when(dataObjectDestination).getDataStore();
         StoragePoolVO storeVO = new StoragePoolVO();
-        doReturn(storeVO).when(_storagePoolDao).findById(0l);
+        doReturn(storeVO).when(primaryDataStoreDao).findById(0l);
+
+        assertTrue(storageSystemDataMotionStrategy.canHandle(volumeObjectSource, dataObjectDestination) == StrategyPriority.CANT_HANDLE);
+    }
+
+    @Test
+    public void internalCanHandleTestAllStoragePoolsAreManaged() {
+        configureAndTestInternalCanHandle(true, true, StrategyPriority.HIGHEST);
+    }
+
+    @Test
+    public void internalCanHandleTestFirstStoragePoolsIsManaged() {
+        configureAndTestInternalCanHandle(false, true, StrategyPriority.HIGHEST);
+    }
+
+    @Test
+    public void internalCanHandleTestSecondStoragePoolsIsManaged() {
+        configureAndTestInternalCanHandle(true, false, StrategyPriority.HIGHEST);
+    }
 
-        assertTrue(strategy.canHandle(source,destination) == StrategyPriority.CANT_HANDLE);
+    @Test
+    public void internalCanHandleTestNoStoragePoolsIsManaged() {
+        configureAndTestInternalCanHandle(false, false, StrategyPriority.CANT_HANDLE);
+    }
+
+    private void configureAndTestInternalCanHandle(boolean sPool0IsManaged, boolean sPool1IsManaged, StrategyPriority expectedStrategyPriority) {
+        VolumeObject volumeInfo = Mockito.spy(new VolumeObject());
+        Mockito.doReturn(0l).when(volumeInfo).getPoolId();
+
+        DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
+        Mockito.doReturn(1l).when(ds).getId();
+
+        Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
+        volumeMap.put(volumeInfo, ds);
+
+        StoragePoolVO storagePool0 = Mockito.spy(new StoragePoolVO());
+        Mockito.doReturn(sPool0IsManaged).when(storagePool0).isManaged();
+        StoragePoolVO storagePool1 = Mockito.spy(new StoragePoolVO());
+        Mockito.doReturn(sPool1IsManaged).when(storagePool1).isManaged();
+
+        Mockito.doReturn(storagePool0).when(primaryDataStoreDao).findById(0l);
+        Mockito.doReturn(storagePool1).when(primaryDataStoreDao).findById(1l);
+
+        StrategyPriority strategyPriority = storageSystemDataMotionStrategy.internalCanHandle(volumeMap);
+
+        Assert.assertEquals(expectedStrategyPriority, strategyPriority);
+    }
+
+    @Test
+    public void isStoragePoolTypeOfFileTest() {
+        StoragePoolVO sourceStoragePool = Mockito.spy(new StoragePoolVO());
+        StoragePoolType[] storagePoolTypeArray = StoragePoolType.values();
+        for (int i = 0; i < storagePoolTypeArray.length; i++) {
+            Mockito.doReturn(storagePoolTypeArray[i]).when(sourceStoragePool).getPoolType();
+            boolean result = storageSystemDataMotionStrategy.isStoragePoolTypeOfFile(sourceStoragePool);
+            if (sourceStoragePool.getPoolType() == StoragePoolType.Filesystem) {
+                Assert.assertTrue(result);
+            } else {
+                Assert.assertFalse(result);
+            }
+        }
+    }
+
+    @Test
+    public void generateDestPathTest() {
+        VolumeObject destVolumeInfo = Mockito.spy(new VolumeObject());
+        HostVO destHost = new HostVO("guid");
+        Mockito.doReturn("iScsiName").when(destVolumeInfo).get_iScsiName();
+        Mockito.doReturn(0l).when(destVolumeInfo).getPoolId();
+        Mockito.doReturn("expected").when(storageSystemDataMotionStrategy).connectHostToVolume(destHost, 0l, "iScsiName");
+
+        String expected = storageSystemDataMotionStrategy.generateDestPath(Mockito.mock(VirtualMachineTO.class), Mockito.mock(VolumeVO.class), destHost,
+                Mockito.mock(StoragePoolVO.class), destVolumeInfo);
+
+        Assert.assertEquals(expected, "expected");
+        Mockito.verify(storageSystemDataMotionStrategy).connectHostToVolume(destHost, 0l, "iScsiName");
+    }
+
+    @Test
+    public void configureMigrateDiskInfoTest() {
+        VolumeObject srcVolumeInfo = Mockito.spy(new VolumeObject());
+        Mockito.doReturn("volume path").when(srcVolumeInfo).getPath();
+        MigrateCommand.MigrateDiskInfo migrateDiskInfo = storageSystemDataMotionStrategy.configureMigrateDiskInfo(srcVolumeInfo, "destPath");
+        Assert.assertEquals(MigrateCommand.MigrateDiskInfo.DiskType.BLOCK, migrateDiskInfo.getDiskType());
+        Assert.assertEquals(MigrateCommand.MigrateDiskInfo.DriverType.RAW, migrateDiskInfo.getDriverType());
+        Assert.assertEquals(MigrateCommand.MigrateDiskInfo.Source.DEV, migrateDiskInfo.getSource());
+        Assert.assertEquals("destPath", migrateDiskInfo.getSourceText());
+        Assert.assertEquals("volume path", migrateDiskInfo.getSerialNumber());
+    }
+
+    @Test
+    public void setVolumePathTest() {
+        VolumeVO volume = new VolumeVO("name", 0l, 0l, 0l, 0l, 0l, "folder", "path", Storage.ProvisioningType.THIN, 0l, Volume.Type.ROOT);
+        String volumePath = "iScsiName";
+        volume.set_iScsiName(volumePath);
+
+        storageSystemDataMotionStrategy.setVolumePath(volume);
+
+        Assert.assertEquals(volumePath, volume.getPath());
+    }
+
+    @Test
+    public void shouldMigrateVolumeTest() {
+        StoragePoolVO sourceStoragePool = Mockito.spy(new StoragePoolVO());
+        HostVO destHost = new HostVO("guid");
+        StoragePoolVO destStoragePool = new StoragePoolVO();
+        StoragePoolType[] storagePoolTypes = StoragePoolType.values();
+        for (int i = 0; i < storagePoolTypes.length; i++) {
+            Mockito.doReturn(storagePoolTypes[i]).when(sourceStoragePool).getPoolType();
+            boolean result = storageSystemDataMotionStrategy.shouldMigrateVolume(sourceStoragePool, destHost, destStoragePool);
+            Assert.assertTrue(result);
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
index 4b2afa6..51dbd92 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
@@ -36,6 +36,36 @@ public class MigrateKVMAsync implements Callable<Domain> {
     private boolean migrateStorage;
     private boolean autoConvergence;
 
+    /**
+     * Do not pause the domain during migration. The domain's memory will be transferred to the destination host while the domain is running. The migration may never converge if the domain is changing its memory faster then it can be transferred. The domain can be manually paused anytime during migration using virDomainSuspend.
+     * @value 1
+     * @see Libvirt <a href="https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainMigrateFlags">virDomainMigrateFlags</a> documentation
+     */
+    private static final long VIR_MIGRATE_LIVE = 1L;
+    /**
+     * Migrate full disk images in addition to domain's memory. By default only non-shared non-readonly disk images are transferred. The VIR_MIGRATE_PARAM_MIGRATE_DISKS parameter can be used to specify which disks should be migrated. This flag and VIR_MIGRATE_NON_SHARED_INC are mutually exclusive.
+     * @value 64
+     * @see Libvirt <a href="https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainMigrateFlags">virDomainMigrateFlags</a> documentation
+     */
+    private static final long VIR_MIGRATE_NON_SHARED_DISK = 64L;
+    /**
+     * Compress migration data. The compression methods can be specified using VIR_MIGRATE_PARAM_COMPRESSION. A hypervisor default method will be used if this parameter is omitted. Individual compression methods can be tuned via their specific VIR_MIGRATE_PARAM_COMPRESSION_* parameters.
+     * @value 2048
+     * @see Libvirt <a href="https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainMigrateFlags">virDomainMigrateFlags</a> documentation
+     */
+    private static final long VIR_MIGRATE_COMPRESSED = 2048L;
+    /**
+     * Enable algorithms that ensure a live migration will eventually converge. This usually means the domain will be slowed down to make sure it does not change its memory faster than a hypervisor can transfer the changed memory to the destination host. VIR_MIGRATE_PARAM_AUTO_CONVERGE_* parameters can be used to tune the algorithm.
+     * @value 8192
+     * @see Libvirt <a href="https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainMigrateFlags">virDomainMigrateFlags</a> documentation
+     */
+    private static final long VIR_MIGRATE_AUTO_CONVERGE = 8192L;
+
+    /**
+     *  Libvirt 1.0.3 supports compression flag for migration.
+     */
+    private static final int LIBVIRT_VERSION_SUPPORTS_MIGRATE_COMPRESSED = 1000003;
+
     public MigrateKVMAsync(final LibvirtComputingResource libvirtComputingResource, final Domain dm, final Connect dconn, final String dxml,
                            final boolean migrateStorage, final boolean autoConvergence, final String vmName, final String destIp) {
         this.libvirtComputingResource = libvirtComputingResource;
@@ -51,19 +81,18 @@ public class MigrateKVMAsync implements Callable<Domain> {
 
     @Override
     public Domain call() throws LibvirtException {
-        long flags = 1 << 0;
+        long flags = VIR_MIGRATE_LIVE;
 
-        // set compression flag for migration, if libvirt version supports it
-        if (dconn.getLibVirVersion() >= 1000003) {
-            flags |= 1 << 11;
+        if (dconn.getLibVirVersion() >= LIBVIRT_VERSION_SUPPORTS_MIGRATE_COMPRESSED) {
+            flags += VIR_MIGRATE_COMPRESSED;
         }
 
         if (migrateStorage) {
-            flags |= 1 << 6;
+            flags += VIR_MIGRATE_NON_SHARED_DISK;
         }
 
         if (autoConvergence && dconn.getLibVirVersion() >= 1002003) {
-            flags |= 1 << 13;
+            flags += VIR_MIGRATE_AUTO_CONVERGE;
         }
 
         return dm.migrate(dconn, flags, dxml, vmName, "tcp:" + destIp, libvirtComputingResource.getMigrateSpeed());
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java
index 1796dc5..bfa5573 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java
@@ -59,7 +59,7 @@ public final class LibvirtCreateCommandWrapper extends CommandWrapper<CreateComm
                     vol = libvirtComputingResource.templateToPrimaryDownload(command.getTemplateUrl(), primaryPool, dskch.getPath());
                 } else {
                     baseVol = primaryPool.getPhysicalDisk(command.getTemplateUrl());
-                    vol = storagePoolMgr.createDiskFromTemplate(baseVol, dskch.getPath(), dskch.getProvisioningType(), primaryPool, 0);
+                    vol = storagePoolMgr.createDiskFromTemplate(baseVol, dskch.getPath(), dskch.getProvisioningType(), primaryPool, baseVol.getSize(), 0);
                 }
                 if (vol == null) {
                     return new Answer(command, false, " Can't create storage volume on storage pool");
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
index 067e77d..2e3bd20 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
@@ -51,6 +51,7 @@ import org.libvirt.Connect;
 import org.libvirt.Domain;
 import org.libvirt.DomainInfo.DomainState;
 import org.libvirt.LibvirtException;
+import org.libvirt.StorageVol;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
@@ -61,7 +62,9 @@ import org.xml.sax.SAXException;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.MigrateAnswer;
 import com.cloud.agent.api.MigrateCommand;
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
 import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
 import com.cloud.hypervisor.kvm.resource.MigrateKVMAsync;
@@ -91,6 +94,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
     public Answer execute(final MigrateCommand command, final LibvirtComputingResource libvirtComputingResource) {
         final String vmName = command.getVmName();
         final String destinationUri = createMigrationURI(command.getDestinationIp(), libvirtComputingResource);
+        final List<MigrateDiskInfo> migrateDiskInfoList = command.getMigrateDiskInfoList();
 
         String result = null;
 
@@ -203,9 +207,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
             destDomain = migrateThread.get(10, TimeUnit.SECONDS);
 
             if (destDomain != null) {
-                for (final DiskDef disk : disks) {
-                    libvirtComputingResource.cleanupDisk(disk);
-                }
+                deleteOrDisconnectDisksOnSourcePool(libvirtComputingResource, migrateDiskInfoList, disks);
             }
 
         } catch (final LibvirtException e) {
@@ -280,6 +282,48 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
     }
 
     /**
+     * In case of a local file, it deletes the file on the source host/storage pool. Otherwise (for instance iScsi) it disconnects the disk on the source storage pool. </br>
+     * This method must be executed after a successful migration to a target storage pool, cleaning up the source storage.
+     */
+    protected void deleteOrDisconnectDisksOnSourcePool(final LibvirtComputingResource libvirtComputingResource, final List<MigrateDiskInfo> migrateDiskInfoList,
+            List<DiskDef> disks) {
+        for (DiskDef disk : disks) {
+            MigrateDiskInfo migrateDiskInfo = searchDiskDefOnMigrateDiskInfoList(migrateDiskInfoList, disk);
+            if (migrateDiskInfo != null && migrateDiskInfo.isSourceDiskOnStorageFileSystem()) {
+                deleteLocalVolume(disk.getDiskPath());
+            } else {
+                libvirtComputingResource.cleanupDisk(disk);
+            }
+        }
+    }
+
+    /**
+     * Deletes the local volume from the storage pool.
+     */
+    protected void deleteLocalVolume(String localPath) {
+        try {
+            Connect conn = LibvirtConnection.getConnection();
+            StorageVol storageVolLookupByPath = conn.storageVolLookupByPath(localPath);
+            storageVolLookupByPath.delete(0);
+        } catch (LibvirtException e) {
+            s_logger.error(String.format("Cannot delete local volume [%s] due to: %s", localPath, e));
+        }
+    }
+
+    /**
+     * Searches for a {@link MigrateDiskInfo} with the path matching the {@link DiskDef} path.
+     */
+    protected MigrateDiskInfo searchDiskDefOnMigrateDiskInfoList(List<MigrateDiskInfo> migrateDiskInfoList, DiskDef disk) {
+        for (MigrateDiskInfo migrateDiskInfo : migrateDiskInfoList) {
+            if (StringUtils.contains(disk.getDiskPath(), migrateDiskInfo.getSerialNumber())) {
+                return migrateDiskInfo;
+            }
+        }
+        s_logger.debug(String.format("Cannot find Disk [uuid: %s] on the list of disks to be migrated", disk.getDiskPath()));
+        return null;
+    }
+
+    /**
      * This function assumes an qemu machine description containing a single graphics element like
      *     <graphics type='vnc' port='5900' autoport='yes' listen='10.10.10.1'>
      *       <listen type='address' address='10.10.10.1'/>
@@ -338,7 +382,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
                         String path = getPathFromSourceText(migrateStorage.keySet(), sourceText);
 
                         if (path != null) {
-                            MigrateCommand.MigrateDiskInfo migrateDiskInfo = migrateStorage.remove(path);
+                            MigrateCommand.MigrateDiskInfo migrateDiskInfo = migrateStorage.get(path);
 
                             NamedNodeMap diskNodeAttributes = diskNode.getAttributes();
                             Node diskNodeAttribute = diskNodeAttributes.getNamedItem("type");
@@ -377,10 +421,6 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
             }
         }
 
-        if (!migrateStorage.isEmpty()) {
-            throw new CloudRuntimeException("Disk info was passed into LibvirtMigrateCommandWrapper.replaceStorage that was not used.");
-        }
-
         return getXml(doc);
     }
 
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
index a90c97f..efb51fb 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java
@@ -349,8 +349,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
         String search4 = "-lun-";
 
         if (!localPath.contains(search3)) {
-            // this volume doesn't below to this adaptor, so just return true
-            return true;
+            return false;
         }
 
         int index = localPath.indexOf(search2);
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
index 4d0523c..91cfc4e 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java
@@ -42,6 +42,7 @@ import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.StorageLayer;
 import com.cloud.storage.Volume;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachine;
 
 import org.reflections.Reflections;
 
@@ -138,20 +139,26 @@ public class KVMStoragePoolManager {
         List<DiskTO> disks = Arrays.asList(vmSpec.getDisks());
 
         for (DiskTO disk : disks) {
-            if (disk.getType() != Volume.Type.ISO) {
-                VolumeObjectTO vol = (VolumeObjectTO)disk.getData();
-                PrimaryDataStoreTO store = (PrimaryDataStoreTO)vol.getDataStore();
-                KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid());
+            if (disk.getType() == Volume.Type.ISO) {
+                result = true;
+                continue;
+            }
 
-                StorageAdaptor adaptor = getStorageAdaptor(pool.getType());
+            VolumeObjectTO vol = (VolumeObjectTO)disk.getData();
+            PrimaryDataStoreTO store = (PrimaryDataStoreTO)vol.getDataStore();
+            if (!store.isManaged() && VirtualMachine.State.Migrating.equals(vmSpec.getState())) {
+                result = true;
+                continue;
+            }
 
-                result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails());
+            KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid());
+            StorageAdaptor adaptor = getStorageAdaptor(pool.getType());
 
-                if (!result) {
-                    s_logger.error("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol.toString());
+            result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails());
 
-                    return result;
-                }
+            if (!result) {
+                s_logger.error("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol.toString());
+                return result;
             }
         }
 
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index c2396e8..69954d0 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -1765,8 +1765,7 @@ public class LibvirtComputingResourceTest {
         when(poolManager.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(primary);
 
         when(primary.getPhysicalDisk(command.getTemplateUrl())).thenReturn(baseVol);
-        when(poolManager.createDiskFromTemplate(baseVol,
-                diskCharacteristics.getPath(), diskCharacteristics.getProvisioningType(), primary, 0)).thenReturn(vol);
+        when(poolManager.createDiskFromTemplate(baseVol, diskCharacteristics.getPath(), diskCharacteristics.getProvisioningType(), primary, baseVol.getSize(), 0)).thenReturn(vol);
 
         final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
         assertNotNull(wrapper);
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
index da71e40..f703a4a 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
@@ -21,11 +21,31 @@ package com.cloud.hypervisor.kvm.resource.wrapper;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.libvirt.Connect;
+import org.libvirt.StorageVol;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
 
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo.DiskType;
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo.DriverType;
+import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo.Source;
 import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
 import com.cloud.utils.exception.CloudRuntimeException;
 
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({LibvirtConnection.class, LibvirtMigrateCommandWrapper.class})
 public class LibvirtMigrateCommandWrapperTest {
     String fullfile =
 "<domain type='kvm' id='4'>\n" +
@@ -252,11 +272,12 @@ public class LibvirtMigrateCommandWrapperTest {
 "  </devices>\n" +
 "</domain>";
 
+    LibvirtMigrateCommandWrapper libvirtMigrateCmdWrapper = new LibvirtMigrateCommandWrapper();
+
     @Test
     public void testReplaceIpForVNCInDescFile() {
         final String targetIp = "192.168.22.21";
-        final LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
-        final String result = lw.replaceIpForVNCInDescFile(fullfile, targetIp);
+        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFile(fullfile, targetIp);
         assertTrue("transformation does not live up to expectation:\n" + result, targetfile.equals(result));
     }
 
@@ -279,8 +300,7 @@ public class LibvirtMigrateCommandWrapperTest {
                 "  </devices>" +
                 "</domain>";
         final String targetIp = "10.10.10.10";
-        final LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
-        final String result = lw.replaceIpForVNCInDescFile(xmlDesc, targetIp);
+        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFile(xmlDesc, targetIp);
         assertTrue("transformation does not live up to expectation:\n" + result, expectedXmlDesc.equals(result));
     }
 
@@ -303,26 +323,116 @@ public class LibvirtMigrateCommandWrapperTest {
                 "  </devices>" +
                 "</domain>";
         final String targetIp = "localhost.localdomain";
-        final LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
-        final String result = lw.replaceIpForVNCInDescFile(xmlDesc, targetIp);
+        final String result = libvirtMigrateCmdWrapper.replaceIpForVNCInDescFile(xmlDesc, targetIp);
         assertTrue("transformation does not live up to expectation:\n" + result, expectedXmlDesc.equals(result));
     }
 
     @Test
     public void testMigrationUri() {
         final String ip = "10.1.1.1";
-        LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
         LibvirtComputingResource lcr = new LibvirtComputingResource();
         if (lcr.isHostSecured()) {
-            assertEquals(lw.createMigrationURI(ip, lcr), String.format("qemu+tls://%s/system", ip));
+            assertEquals(libvirtMigrateCmdWrapper.createMigrationURI(ip, lcr), String.format("qemu+tls://%s/system", ip));
         } else {
-            assertEquals(lw.createMigrationURI(ip, lcr), String.format("qemu+tcp://%s/system", ip));
+            assertEquals(libvirtMigrateCmdWrapper.createMigrationURI(ip, lcr), String.format("qemu+tcp://%s/system", ip));
         }
     }
 
     @Test(expected = CloudRuntimeException.class)
     public void testMigrationUriException() {
-        LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper();
-        lw.createMigrationURI(null, new LibvirtComputingResource());
+        libvirtMigrateCmdWrapper.createMigrationURI(null, new LibvirtComputingResource());
+    }
+
+    @Test
+    public void deleteLocalVolumeTest() throws Exception {
+        PowerMockito.mockStatic(LibvirtConnection.class);
+        Connect conn = Mockito.mock(Connect.class);
+
+        PowerMockito.doReturn(conn).when(LibvirtConnection.class, "getConnection");
+
+        StorageVol storageVolLookupByPath = Mockito.mock(StorageVol.class);
+        Mockito.when(conn.storageVolLookupByPath("localPath")).thenReturn(storageVolLookupByPath);
+
+        libvirtMigrateCmdWrapper.deleteLocalVolume("localPath");
+
+        PowerMockito.verifyStatic(Mockito.times(1));
+        LibvirtConnection.getConnection();
+        InOrder inOrder = Mockito.inOrder(conn, storageVolLookupByPath);
+        inOrder.verify(conn, Mockito.times(1)).storageVolLookupByPath("localPath");
+        inOrder.verify(storageVolLookupByPath, Mockito.times(1)).delete(0);
     }
+
+    @Test
+    public void searchDiskDefOnMigrateDiskInfoListTest() {
+        configureAndVerifyTestSearchDiskDefOnMigrateDiskInfoList("f3d49ecc-870c-475a-89fa-fd0124420a9b", "/var/lib/libvirt/images/f3d49ecc-870c-475a-89fa-fd0124420a9b", false);
+    }
+
+    @Test
+    public void searchDiskDefOnMigrateDiskInfoListTestExpectNull() {
+        configureAndVerifyTestSearchDiskDefOnMigrateDiskInfoList("f3d49ecc-870c-475a-89fa-fd0124420a9b", "/var/lib/libvirt/images/f3d49ecc-870c-89fa-fd0124420a9b", true);
+    }
+
+    private void configureAndVerifyTestSearchDiskDefOnMigrateDiskInfoList(String serialNumber, String diskPath, boolean isExpectedDiskInfoNull) {
+        MigrateDiskInfo migrateDiskInfo = new MigrateDiskInfo(serialNumber, DiskType.FILE, DriverType.QCOW2, Source.FILE, "sourceText");
+        List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
+        migrateDiskInfoList.add(migrateDiskInfo);
+
+        DiskDef disk = new DiskDef();
+        disk.setDiskPath(diskPath);
+
+        MigrateDiskInfo returnedMigrateDiskInfo = libvirtMigrateCmdWrapper.searchDiskDefOnMigrateDiskInfoList(migrateDiskInfoList, disk);
+
+        if (isExpectedDiskInfoNull)
+            Assert.assertEquals(null, returnedMigrateDiskInfo);
+        else
+            Assert.assertEquals(migrateDiskInfo, returnedMigrateDiskInfo);
+    }
+
+    @Test
+    public void deleteOrDisconnectDisksOnSourcePoolTest() {
+        LibvirtMigrateCommandWrapper spyLibvirtMigrateCmdWrapper = PowerMockito.spy(libvirtMigrateCmdWrapper);
+        Mockito.doNothing().when(spyLibvirtMigrateCmdWrapper).deleteLocalVolume("volPath");
+
+        List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
+        MigrateDiskInfo migrateDiskInfo0 = createMigrateDiskInfo(true);
+        MigrateDiskInfo migrateDiskInfo2 = createMigrateDiskInfo(false);
+
+        List<DiskDef> disks = new ArrayList<>();
+        DiskDef diskDef0 = new DiskDef();
+        DiskDef diskDef1 = new DiskDef();
+        DiskDef diskDef2 = new DiskDef();
+
+        diskDef0.setDiskPath("volPath");
+        disks.add(diskDef0);
+        disks.add(diskDef1);
+        disks.add(diskDef2);
+
+        LibvirtComputingResource libvirtComputingResource = Mockito.spy(new LibvirtComputingResource());
+        Mockito.doReturn(true).when(libvirtComputingResource).cleanupDisk(diskDef1);
+
+        Mockito.doReturn(migrateDiskInfo0).when(spyLibvirtMigrateCmdWrapper).searchDiskDefOnMigrateDiskInfoList(migrateDiskInfoList, diskDef0);
+        Mockito.doReturn(null).when(spyLibvirtMigrateCmdWrapper).searchDiskDefOnMigrateDiskInfoList(migrateDiskInfoList, diskDef1);
+        Mockito.doReturn(migrateDiskInfo2).when(spyLibvirtMigrateCmdWrapper).searchDiskDefOnMigrateDiskInfoList(migrateDiskInfoList, diskDef2);
+
+        spyLibvirtMigrateCmdWrapper.deleteOrDisconnectDisksOnSourcePool(libvirtComputingResource, migrateDiskInfoList, disks);
+
+        InOrder inOrder = Mockito.inOrder(spyLibvirtMigrateCmdWrapper, libvirtComputingResource);
+        inOrderVerifyDeleteOrDisconnect(inOrder, spyLibvirtMigrateCmdWrapper, libvirtComputingResource, migrateDiskInfoList, diskDef0, 1, 0);
+        inOrderVerifyDeleteOrDisconnect(inOrder, spyLibvirtMigrateCmdWrapper, libvirtComputingResource, migrateDiskInfoList, diskDef1, 0, 1);
+        inOrderVerifyDeleteOrDisconnect(inOrder, spyLibvirtMigrateCmdWrapper, libvirtComputingResource, migrateDiskInfoList, diskDef2, 0, 1);
+    }
+
+    private MigrateDiskInfo createMigrateDiskInfo(boolean isSourceDiskOnStorageFileSystem) {
+        MigrateDiskInfo migrateDiskInfo = new MigrateDiskInfo("serialNumber", DiskType.FILE, DriverType.QCOW2, Source.FILE, "sourceText");
+        migrateDiskInfo.setSourceDiskOnStorageFileSystem(isSourceDiskOnStorageFileSystem);
+        return migrateDiskInfo;
+    }
+
+    private void inOrderVerifyDeleteOrDisconnect(InOrder inOrder, LibvirtMigrateCommandWrapper lw, LibvirtComputingResource virtResource, List<MigrateDiskInfo> diskInfoList,
+            DiskDef disk, int timesDelete, int timesCleanup) {
+        inOrder.verify(lw).searchDiskDefOnMigrateDiskInfoList(diskInfoList, disk);
+        inOrder.verify(lw, Mockito.times(timesDelete)).deleteLocalVolume("volPath");
+        inOrder.verify(virtResource, Mockito.times(timesCleanup)).cleanupDisk(disk);
+    }
+
 }
diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
index 1136305..96a03b7 100644
--- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
+++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java
@@ -206,7 +206,9 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
             result.setResult(errMsg);
         }
 
-        callback.complete(result);
+        if (callback != null) {
+            callback.complete(result);
+        }
     }
 
     @Override
diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
index 9f67d34..52c5a72 100644
--- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
@@ -188,6 +188,7 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
         to.setConfigDriveLabel(vmProfile.getConfigDriveLabel());
         to.setConfigDriveIsoRootFolder(vmProfile.getConfigDriveIsoRootFolder());
         to.setConfigDriveIsoFile(vmProfile.getConfigDriveIsoFile());
+        to.setState(vm.getState());
 
         return to;
     }