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 <gabrasc...@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;
     }

Reply via email to