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

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


The following commit(s) were added to refs/heads/main by this push:
     new b392084950d Enable live volume migration for StorPool and small fixes 
(#6661)
b392084950d is described below

commit b392084950dcebddde0284086c9defa9323e7fe5
Author: slavkap <[email protected]>
AuthorDate: Tue Jan 10 19:21:39 2023 +0200

    Enable live volume migration for StorPool and small fixes (#6661)
---
 plugins/storage/volume/storpool/pom.xml            |  10 +
 .../StorPoolDownloadTemplateCommandWrapper.java    |   5 +
 .../StorPoolModifyStorageCommandWrapper.java       |   4 +-
 .../kvm/storage/StorPoolStorageAdaptor.java        |  95 +++-----
 .../driver/StorPoolPrimaryDataStoreDriver.java     | 251 +++++++++++++--------
 .../datastore/provider/StorPoolHostListener.java   |  39 +++-
 .../storage/datastore/util/StorPoolUtil.java       |  71 +++---
 .../snapshot/StorPoolVMSnapshotStrategy.java       |   2 +-
 .../driver/StorPoolPrimaryDataStoreDriverTest.java | 245 ++++++++++++++++++++
 .../com/cloud/storage/VolumeApiServiceImpl.java    |  17 +-
 .../plugins/storpool/MigrateVolumeToStorPool.py    |   7 +
 test/integration/plugins/storpool/sp_util.py       |  31 +++
 tools/marvin/marvin/lib/base.py                    |   5 +-
 13 files changed, 579 insertions(+), 203 deletions(-)

diff --git a/plugins/storage/volume/storpool/pom.xml 
b/plugins/storage/volume/storpool/pom.xml
index 5a1a6257115..648a4fb971d 100644
--- a/plugins/storage/volume/storpool/pom.xml
+++ b/plugins/storage/volume/storpool/pom.xml
@@ -51,6 +51,16 @@
             <artifactId>commons-collections4</artifactId>
             <version>${cs.commons-collections.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>4.7.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <version>4.7.0</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git 
a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java
 
b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java
index 07b08a1da2b..87a46ba62c9 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java
@@ -25,6 +25,7 @@ import java.io.File;
 import java.util.List;
 
 import org.apache.cloudstack.storage.command.CopyCmdAnswer;
+import org.apache.cloudstack.storage.to.TemplateObjectTO;
 import org.apache.cloudstack.utils.qemu.QemuImg;
 import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
 import org.apache.cloudstack.utils.qemu.QemuImgFile;
@@ -106,6 +107,10 @@ public final class StorPoolDownloadTemplateCommandWrapper 
extends CommandWrapper
             final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds());
             StorPoolStorageAdaptor.resize( 
Long.toString(srcDisk.getVirtualSize()), dst.getPath());
 
+            if (dst instanceof TemplateObjectTO) {
+                ((TemplateObjectTO) dst).setSize(srcDisk.getVirtualSize());
+            }
+
             dstPath = dst.getPath();
             StorPoolStorageAdaptor.attachOrDetachVolume("attach", 
cmd.getObjectType(), dstPath);
 
diff --git 
a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java
 
b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java
index b3579708335..b797b3c20d1 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java
@@ -49,7 +49,7 @@ public final class StorPoolModifyStorageCommandWrapper 
extends CommandWrapper<St
     public Answer execute(final StorPoolModifyStoragePoolCommand command, 
final LibvirtComputingResource libvirtComputingResource) {
         String clusterId = getSpClusterId();
         if (clusterId == null) {
-            log.debug(String.format("Could not get StorPool cluster id for a 
command $s", command.getClass()));
+            log.debug(String.format("Could not get StorPool cluster id for a 
command [%s]", command.getClass()));
             return new Answer(command, false, "spNotFound");
         }
         try {
@@ -83,8 +83,6 @@ public final class StorPoolModifyStorageCommandWrapper 
extends CommandWrapper<St
         String SP_CLUSTER_ID = null;
         final String err = sc.execute(parser);
         if (err != null) {
-            final String errMsg = String.format("Could not execute 
storpool_confget. Error: %s", err);
-            log.warn(errMsg);
             StorPoolStorageAdaptor.SP_LOG("Could not execute storpool_confget. 
Error: %s", err);
             return SP_CLUSTER_ID;
         }
diff --git 
a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java
 
b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java
index 915ad55934e..273f088d77f 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java
@@ -58,7 +58,7 @@ public class StorPoolStorageAdaptor implements StorageAdaptor 
{
 
     @Override
     public KVMStoragePool createStoragePool(String uuid, String host, int 
port, String path, String userInfo, StoragePoolType storagePoolType, 
Map<String, String> details) {
-        SP_LOG("StorpooolStorageAdaptor.createStoragePool: uuid=%s, 
host=%s:%d, path=%s, userInfo=%s, type=%s", uuid, host, port, path, userInfo, 
storagePoolType);
+        SP_LOG("StorPoolStorageAdaptor.createStoragePool: uuid=%s, host=%s:%d, 
path=%s, userInfo=%s, type=%s", uuid, host, port, path, userInfo, 
storagePoolType);
 
         StorPoolStoragePool storagePool = new StorPoolStoragePool(uuid, host, 
port, storagePoolType, this);
         storageUuidToStoragePool.put(uuid, storagePool);
@@ -67,30 +67,30 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     @Override
     public KVMStoragePool getStoragePool(String uuid) {
-        SP_LOG("StorpooolStorageAdaptor.getStoragePool: uuid=%s", uuid);
+        SP_LOG("StorPoolStorageAdaptor.getStoragePool: uuid=%s", uuid);
         return storageUuidToStoragePool.get(uuid);
     }
 
     @Override
     public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) {
-        SP_LOG("StorpooolStorageAdaptor.getStoragePool: uuid=%s, refresh=%s", 
uuid, refreshInfo);
+        SP_LOG("StorPoolStorageAdaptor.getStoragePool: uuid=%s, refresh=%s", 
uuid, refreshInfo);
         return storageUuidToStoragePool.get(uuid);
     }
 
     @Override
     public boolean deleteStoragePool(String uuid) {
-        SP_LOG("StorpooolStorageAdaptor.deleteStoragePool: uuid=%s", uuid);
+        SP_LOG("StorPoolStorageAdaptor.deleteStoragePool: uuid=%s", uuid);
         return storageUuidToStoragePool.remove(uuid) != null;
     }
 
     @Override
     public boolean deleteStoragePool(KVMStoragePool pool) {
-        SP_LOG("StorpooolStorageAdaptor.deleteStoragePool: uuid=%s", 
pool.getUuid());
+        SP_LOG("StorPoolStorageAdaptor.deleteStoragePool: uuid=%s", 
pool.getUuid());
         return deleteStoragePool(pool.getUuid());
     }
 
     private static long getDeviceSize(final String devPath) {
-        SP_LOG("StorpooolStorageAdaptor.getDeviceSize: path=%s", devPath);
+        SP_LOG("StorPoolStorageAdaptor.getDeviceSize: path=%s", devPath);
 
         if (getVolumeNameFromPath(devPath, true) == null) {
             return 0;
@@ -149,7 +149,7 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
             return false;
         }
 
-        SP_LOG("StorpooolStorageAdaptor.attachOrDetachVolume: cmd=%s, type=%s, 
uuid=%s, name=%s", command, type, volumeUuid, name);
+        SP_LOG("StorPoolStorageAdaptor.attachOrDetachVolume: cmd=%s, type=%s, 
uuid=%s, name=%s", command, type, volumeUuid, name);
 
         final int numTries = 10;
         final int sleepTime = 1000;
@@ -205,7 +205,7 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
             return false;
         }
 
-        SP_LOG("StorpooolStorageAdaptor.resize: size=%s, uuid=%s, name=%s", 
newSize, volumeUuid, name);
+        SP_LOG("StorPoolStorageAdaptor.resize: size=%s, uuid=%s, name=%s", 
newSize, volumeUuid, name);
 
         Script sc = new Script("storpool", 0, log);
         sc.add("-M");
@@ -230,7 +230,7 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     @Override
     public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool 
pool) {
-        SP_LOG("StorpooolStorageAdaptor.getPhysicalDisk: uuid=%s, pool=%s", 
volumeUuid, pool);
+        SP_LOG("StorPoolStorageAdaptor.getPhysicalDisk: uuid=%s, pool=%s", 
volumeUuid, pool);
 
         log.debug(String.format("getPhysicalDisk: uuid=%s, pool=%s", 
volumeUuid, pool));
 
@@ -245,7 +245,7 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     @Override
     public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, 
Map<String, String> details) {
-        SP_LOG("StorpooolStorageAdaptor.connectPhysicalDisk: uuid=%s, 
pool=%s", volumeUuid, pool);
+        SP_LOG("StorPoolStorageAdaptor.connectPhysicalDisk: uuid=%s, pool=%s", 
volumeUuid, pool);
 
         log.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", 
volumeUuid, pool));
 
@@ -254,7 +254,7 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     @Override
     public boolean disconnectPhysicalDisk(String volumeUuid, KVMStoragePool 
pool) {
-        SP_LOG("StorpooolStorageAdaptor.disconnectPhysicalDisk: uuid=%s, 
pool=%s", volumeUuid, pool);
+        SP_LOG("StorPoolStorageAdaptor.disconnectPhysicalDisk: uuid=%s, 
pool=%s", volumeUuid, pool);
 
         log.debug(String.format("disconnectPhysicalDisk: uuid=%s, pool=%s", 
volumeUuid, pool));
         return attachOrDetachVolume("detach", "volume", volumeUuid);
@@ -262,32 +262,23 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     public boolean disconnectPhysicalDisk(Map<String, String> 
volumeToDisconnect) {
         String volumeUuid = volumeToDisconnect.get(DiskTO.UUID);
-        SP_LOG("StorpooolStorageAdaptor.disconnectPhysicalDisk: map. uuid=%s", 
volumeUuid);
+        
log.debug(String.format("StorPoolStorageAdaptor.disconnectPhysicalDisk: map. 
uuid=%s", volumeUuid));
         return attachOrDetachVolume("detach", "volume", volumeUuid);
     }
 
     @Override
     public boolean disconnectPhysicalDiskByPath(String localPath) {
-        SP_LOG("StorpooolStorageAdaptor.disconnectPhysicalDiskByPath: 
localPath=%s", localPath);
-
         log.debug(String.format("disconnectPhysicalDiskByPath: localPath=%s", 
localPath));
         return attachOrDetachVolume("detach", "volume", localPath);
     }
 
-    // The following do not apply for StorpoolStorageAdaptor?
-    @Override
-    public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, 
KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType 
provisioningType, long size, byte[] passphrase) {
-        SP_LOG("StorpooolStorageAdaptor.createPhysicalDisk: uuid=%s, pool=%s, 
format=%s, size=%d", volumeUuid, pool, format, size);
-        throw new UnsupportedOperationException("Creating a physical disk is 
not supported.");
-    }
-
     @Override
     public boolean deletePhysicalDisk(String volumeUuid, KVMStoragePool pool, 
Storage.ImageFormat format) {
         // Should only come here when cleaning-up StorPool snapshots 
associated with CloudStack templates.
-        SP_LOG("StorpooolStorageAdaptor.deletePhysicalDisk: uuid=%s, pool=%s, 
format=%s", volumeUuid, pool, format);
+        SP_LOG("StorPoolStorageAdaptor.deletePhysicalDisk: uuid=%s, pool=%s, 
format=%s", volumeUuid, pool, format);
         final String name = getVolumeNameFromPath(volumeUuid, true);
         if (name == null) {
-            final String err = 
String.format("StorpooolStorageAdaptor.deletePhysicalDisk: '%s' is not a 
StorPool volume?", volumeUuid);
+            final String err = 
String.format("StorPoolStorageAdaptor.deletePhysicalDisk: '%s' is not a 
StorPool volume?", volumeUuid);
             SP_LOG(err);
             throw new UnsupportedOperationException(err);
         }
@@ -311,21 +302,13 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     @Override
     public List<KVMPhysicalDisk> listPhysicalDisks(String storagePoolUuid, 
KVMStoragePool pool) {
-        SP_LOG("StorpooolStorageAdaptor.listPhysicalDisks: uuid=%s, pool=%s", 
storagePoolUuid, pool);
+        SP_LOG("StorPoolStorageAdaptor.listPhysicalDisks: uuid=%s, pool=%s", 
storagePoolUuid, pool);
         throw new UnsupportedOperationException("Listing disks is not 
supported for this configuration.");
     }
 
-    @Override
-    public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, 
String name, PhysicalDiskFormat format,
-            ProvisioningType provisioningType, long size, KVMStoragePool 
destPool, int timeout, byte[] passphrase) {
-        SP_LOG("StorpooolStorageAdaptor.createDiskFromTemplate: template=%s, 
name=%s, fmt=%s, ptype=%s, size=%d, dst_pool=%s, to=%d",
-            template, name, format, provisioningType, size, 
destPool.getUuid(), timeout);
-        throw new UnsupportedOperationException("Creating a disk from a 
template is not yet supported for this configuration.");
-    }
-
     @Override
     public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String 
name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) {
-        SP_LOG("StorpooolStorageAdaptor.createTemplateFromDisk: disk=%s, 
name=%s, fmt=%s, size=%d, dst_pool=%s", disk, name, format, size, 
destPool.getUuid());
+        SP_LOG("StorPoolStorageAdaptor.createTemplateFromDisk: disk=%s, 
name=%s, fmt=%s, size=%d, dst_pool=%s", disk, name, format, size, 
destPool.getUuid());
         throw new UnsupportedOperationException("Creating a template from a 
disk is not yet supported for this configuration.");
     }
 
@@ -336,58 +319,52 @@ public class StorPoolStorageAdaptor implements 
StorageAdaptor {
 
     @Override
     public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, 
KVMStoragePool destPool, int timeout) {
-        SP_LOG("StorpooolStorageAdaptor.copyPhysicalDisk: disk=%s, name=%s, 
dst_pool=%s, to=%d", disk, name, destPool.getUuid(), timeout);
+        SP_LOG("StorPoolStorageAdaptor.copyPhysicalDisk: disk=%s, name=%s, 
dst_pool=%s, to=%d", disk, name, destPool.getUuid(), timeout);
         throw new UnsupportedOperationException("Copying a disk is not 
supported in this configuration.");
     }
 
     public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, 
String snapshotName, String name, KVMStoragePool destPool) {
-        SP_LOG("StorpooolStorageAdaptor.createDiskFromSnapshot: snap=%s, 
snap_name=%s, name=%s, dst_pool=%s", snapshot, snapshotName, name, 
destPool.getUuid());
+        SP_LOG("StorPoolStorageAdaptor.createDiskFromSnapshot: snap=%s, 
snap_name=%s, name=%s, dst_pool=%s", snapshot, snapshotName, name, 
destPool.getUuid());
         throw new UnsupportedOperationException("Creating a disk from a 
snapshot is not supported in this configuration.");
     }
 
     @Override
     public boolean refresh(KVMStoragePool pool) {
-        SP_LOG("StorpooolStorageAdaptor.refresh: pool=%s", pool);
+        SP_LOG("StorPoolStorageAdaptor.refresh: pool=%s", pool);
         return true;
     }
 
     @Override
     public boolean createFolder(String uuid, String path) {
-        SP_LOG("StorpooolStorageAdaptor.createFolder: uuid=%s, path=%s", uuid, 
path);
+        SP_LOG("StorPoolStorageAdaptor.createFolder: uuid=%s, path=%s", uuid, 
path);
         throw new UnsupportedOperationException("A folder cannot be created in 
this configuration.");
     }
 
-    public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, 
String snapshotName, String name,
-            KVMStoragePool destPool, int timeout) {
-        SP_LOG("StorpooolStorageAdaptor.createDiskFromSnapshot: snap=%s, 
snap_name=%s, name=%s, dst_pool=%s", snapshot,
-                snapshotName, name, destPool.getUuid());
-        throw new UnsupportedOperationException(
-                "Creating a disk from a snapshot is not supported in this 
configuration.");
+    public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String 
templateFilePath, String destTemplatePath,
+            KVMStoragePool destPool, ImageFormat format, int timeout) {
+        return null;
     }
 
-    public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk 
template, String name,
-            PhysicalDiskFormat format, long size, KVMStoragePool destPool, int 
timeout, byte[] passphrase) {
-        SP_LOG("StorpooolStorageAdaptor.createDiskFromTemplateBacking: 
template=%s, name=%s, dst_pool=%s", template,
-                name, destPool.getUuid());
-        throw new UnsupportedOperationException(
-                "Creating a disk from a template is not supported in this 
configuration.");
+    @Override
+    public boolean createFolder(String uuid, String path, String localPath) {
+        return false;
     }
 
-    public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String 
templateFilePath, KVMStoragePool destPool,
-            boolean isIso) {
-        SP_LOG("StorpooolStorageAdaptor.createTemplateFromDirectDownloadFile: 
templateFilePath=%s, dst_pool=%s",
-                templateFilePath, destPool.getUuid());
-        throw new UnsupportedOperationException(
-                "Creating a template from direct download is not supported in 
this configuration.");
+    @Override
+    public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool 
pool, PhysicalDiskFormat format,
+            ProvisioningType provisioningType, long size, byte[] passphrase) {
+        return null;
     }
 
-    public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String 
templateFilePath, String destTemplatePath,
-            KVMStoragePool destPool, ImageFormat format, int timeout) {
+    @Override
+    public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, 
String name, PhysicalDiskFormat format,
+            ProvisioningType provisioningType, long size, KVMStoragePool 
destPool, int timeout, byte[] passphrase) {
         return null;
     }
 
     @Override
-    public boolean createFolder(String uuid, String path, String localPath) {
-        return false;
+    public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk 
template, String name,
+            PhysicalDiskFormat format, long size, KVMStoragePool destPool, int 
timeout, byte[] passphrase) {
+        return null;
     }
 }
diff --git 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java
 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java
index c62680a956a..6eced6fc5d0 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java
@@ -17,46 +17,6 @@
  * under the License.
  */
 package org.apache.cloudstack.storage.datastore.driver;
-import java.util.Map;
-
-import javax.inject.Inject;
-
-import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
-import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
-import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
-import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
-import 
org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
-import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
-import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
-import org.apache.cloudstack.storage.RemoteHostEndPoint;
-import org.apache.cloudstack.storage.command.CommandResult;
-import org.apache.cloudstack.storage.command.CopyCmdAnswer;
-import org.apache.cloudstack.storage.command.CreateObjectAnswer;
-import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
-import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
-import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
-import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
-import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
-import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
-import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
-import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
-import 
org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
-import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager;
-import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
-import org.apache.cloudstack.storage.to.SnapshotObjectTO;
-import org.apache.cloudstack.storage.to.TemplateObjectTO;
-import org.apache.cloudstack.storage.to.VolumeObjectTO;
-import org.apache.cloudstack.storage.volume.VolumeObject;
-import org.apache.log4j.Logger;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.storage.ResizeVolumeAnswer;
@@ -87,16 +47,56 @@ import com.cloud.storage.VolumeDetailVO;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.SnapshotDetailsDao;
 import com.cloud.storage.dao.SnapshotDetailsVO;
+import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.storage.dao.VMTemplateDetailsDao;
-import com.cloud.storage.dao.VMTemplatePoolDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.dao.VolumeDetailsDao;
 import com.cloud.tags.dao.ResourceTagDao;
 import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
+import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
+import 
org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.storage.RemoteHostEndPoint;
+import org.apache.cloudstack.storage.command.CommandResult;
+import org.apache.cloudstack.storage.command.CopyCmdAnswer;
+import org.apache.cloudstack.storage.command.CreateObjectAnswer;
+import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
+import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
+import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
+import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
+import 
org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
+import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager;
+import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
+import org.apache.cloudstack.storage.to.SnapshotObjectTO;
+import org.apache.cloudstack.storage.to.TemplateObjectTO;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.cloudstack.storage.volume.VolumeObject;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import java.util.Map;
 
 public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
 
@@ -133,7 +133,7 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
     @Inject
     private StoragePoolDetailsDao storagePoolDetailsDao;
     @Inject
-    private VMTemplatePoolDao vmTemplatePoolDao;
+    private StoragePoolHostDao storagePoolHostDao;
 
     @Override
     public Map<String, String> getCapabilities() {
@@ -629,33 +629,26 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
                 final String name = vinfo.getUuid();
                 SpConnectionDesc conn = 
StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), 
vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao);
 
-                Long snapshotSize = StorPoolUtil.snapshotSize(parentName, 
conn);
-                if (snapshotSize == null) {
-                    err = String.format("Snapshot=%s does not exist on 
StorPool. Will recreate it first on primary", parentName);
-                    vmTemplatePoolDao.remove(templStoragePoolVO.getId());
+                Long snapshotSize = templStoragePoolVO.getTemplateSize();
+                long size = vinfo.getSize();
+                if (snapshotSize != null && size < snapshotSize) {
+                    StorPoolUtil.spLog(String.format("provided size is too 
small for snapshot. Provided %d, snapshot %d. Using snapshot size", size, 
snapshotSize));
+                    size = snapshotSize;
                 }
-                if (err == null) {
-                    long size = vinfo.getSize();
-                    if( size < snapshotSize )
-                    {
-                        StorPoolUtil.spLog(String.format("provided size is too 
small for snapshot. Provided %d, snapshot %d. Using snapshot size", size, 
snapshotSize));
-                        size = snapshotSize;
-                    }
-                    StorPoolUtil.spLog(String.format("volume size is: %d", 
size));
-                    Long vmId = vinfo.getInstanceId();
-                    SpApiResponse resp = StorPoolUtil.volumeCreate(name, 
parentName, size, getVMInstanceUUID(vmId),
-                            getVcPolicyTag(vmId), "volume", 
vinfo.getMaxIops(), conn);
-                    if (resp.getError() == null) {
-                        updateStoragePool(dstData.getDataStore().getId(), 
vinfo.getSize());
-
-                        VolumeObjectTO to = (VolumeObjectTO) vinfo.getTO();
-                        to.setSize(vinfo.getSize());
-                        
to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false)));
-
-                        answer = new CopyCmdAnswer(to);
-                    } else {
-                        err = String.format("Could not create Storpool volume 
%s. Error: %s", name, resp.getError());
-                    }
+                StorPoolUtil.spLog(String.format("volume size is: %d", size));
+                Long vmId = vinfo.getInstanceId();
+                SpApiResponse resp = StorPoolUtil.volumeCreate(name, 
parentName, size, getVMInstanceUUID(vmId),
+                        getVcPolicyTag(vmId), "volume", vinfo.getMaxIops(), 
conn);
+                if (resp.getError() == null) {
+                    updateStoragePool(dstData.getDataStore().getId(), 
vinfo.getSize());
+
+                    VolumeObjectTO to = (VolumeObjectTO) vinfo.getTO();
+                    to.setSize(vinfo.getSize());
+                    
to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false)));
+
+                    answer = new CopyCmdAnswer(to);
+                } else {
+                    err = String.format("Could not create Storpool volume %s. 
Error: %s", name, resp.getError());
                 }
             } else if (srcType == DataObjectType.VOLUME && dstType == 
DataObjectType.VOLUME) {
                 StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.copyAsync 
src Data Store=%s", srcData.getDataStore().getDriver());
@@ -684,9 +677,9 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
 
                         EndPoint ep = selector.select(srcData, dstData);
 
-                        if( ep == null) {
-                            StorPoolUtil.spLog("select(srcData, dstData) 
returned NULL. trying srcOnly");
-                            ep = selector.select(srcData); // Storpool is zone
+                        if (ep == null || 
storagePoolHostDao.findByPoolHost(dstData.getId(), ep.getId()) == null) {
+                            StorPoolUtil.spLog("select(srcData, dstData) 
returned NULL or the destination pool is not connected to the selected host. 
Trying dstData");
+                            ep = selector.select(dstData); // Storpool is zone
                         }
                         if (ep == null) {
                             err = "No remote endpoint to send command, check 
if host or ssvm is down?";
@@ -711,27 +704,15 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
                 } else {
                     // download volume - first copies to secondary
                     VolumeObjectTO srcTO = (VolumeObjectTO)srcData.getTO();
-                    
StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc SRC path=%s ", 
srcTO.getPath());
-                    
StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST 
canonicalName=%s ", dstData.getDataStore().getClass().getCanonicalName());
+                    
StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc SRC path=%s DST 
canonicalName=%s ", srcTO.getPath(), 
dstData.getDataStore().getClass().getCanonicalName());
                     PrimaryDataStoreTO checkStoragePool = 
dstData.getTO().getDataStore() instanceof PrimaryDataStoreTO ? 
(PrimaryDataStoreTO)dstData.getTO().getDataStore() : null;
-                    final String name = 
StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true);
-                    
StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST 
tmpSnapName=%s ,srcUUID=%s", name, srcTO.getUuid());
+                    final String volumeName = 
StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true);
 
                     if (checkStoragePool != null && 
checkStoragePool.getPoolType().equals(StoragePoolType.StorPool)) {
-                        SpConnectionDesc conn = 
StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), 
dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao);
-                        String baseOn = 
StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true);
-                        //uuid tag will be the same as srcData.uuid
-                        String volumeName = srcData.getUuid();
-                        
StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc volumeName=%s, 
baseOn=%s", volumeName, baseOn);
-                        final SpApiResponse response = 
StorPoolUtil.volumeCopy(volumeName, baseOn, "volume", srcInfo.getMaxIops(), 
conn);
-                        srcTO.setSize(srcData.getSize());
-                        
srcTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(response, 
false)));
-                        
StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST to=%s", 
srcTO);
-
-                        answer = new CopyCmdAnswer(srcTO);
+                        answer = migrateVolumeToStorPool(srcData, dstData, 
srcInfo, srcTO, volumeName);
                     } else {
                         SpConnectionDesc conn = 
StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), 
srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao);
-                        final SpApiResponse resp = 
StorPoolUtil.volumeSnapshot(name, srcTO.getUuid(), srcInfo.getInstanceId() != 
null ? getVMInstanceUUID(srcInfo.getInstanceId()) : null, "temporary", null, 
conn);
+                        final SpApiResponse resp = 
StorPoolUtil.volumeSnapshot(volumeName, srcTO.getUuid(), 
srcInfo.getInstanceId() != null ? getVMInstanceUUID(srcInfo.getInstanceId()) : 
null, "temporary", null, conn);
                         String snapshotName = 
StorPoolUtil.getSnapshotNameFromResponse(resp, true, StorPoolUtil.GLOBAL_ID);
                         if (resp.getError() == null) {
                             srcTO.setPath(StorPoolUtil.devPath(
@@ -794,6 +775,96 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
         callback.complete(res);
     }
 
+    /**
+     * Live migrate/copy volume from one StorPool storage to another
+     * @param srcData The source volume data
+     * @param dstData The destination volume data
+     * @param srcInfo The source volume info
+     * @param srcTO The source Volume TO
+     * @param volumeName The name of the volume
+     * @return Answer
+     */
+    private Answer migrateVolumeToStorPool(DataObject srcData, DataObject 
dstData, VolumeInfo srcInfo,
+            VolumeObjectTO srcTO, final String volumeName) {
+        Answer answer;
+        SpConnectionDesc conn = 
StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), 
dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao);
+        String baseOn = 
StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true);
+
+        String vmUuid = null;
+        String vcPolicyTag = null;
+
+        VMInstanceVO vm = null;
+        if (srcInfo.getInstanceId() != null) {
+            vm = vmInstanceDao.findById(srcInfo.getInstanceId());
+        }
+
+        if (vm != null) {
+            vmUuid = vm.getUuid();
+            vcPolicyTag = getVcPolicyTag(vm.getId());
+        }
+
+        if (vm != null && vm.getState().equals(State.Running)) {
+            answer = migrateVolume(srcData, dstData, volumeName, conn);
+        } else {
+            answer = copyVolume(srcInfo, srcTO, conn, baseOn, vmUuid, 
vcPolicyTag);
+        }
+        return answer;
+    }
+
+    /**
+     * Copy the volume from StorPool primary storage to another StorPool 
primary storage
+     * @param srcInfo The source volume info
+     * @param srcTO The source Volume TO
+     * @param conn StorPool connection
+     * @param baseOn The name of an already existing volume that the new 
volume is to be a copy of.
+     * @param vmUuid The UUID of the VM
+     * @param vcPolicyTag The VC policy tag
+     * @return Answer
+     */
+    private Answer copyVolume(VolumeInfo srcInfo, VolumeObjectTO srcTO, 
SpConnectionDesc conn, String baseOn, String vmUuid, String vcPolicyTag) {
+        //uuid tag will be the same as srcData.uuid
+        String volumeName = srcInfo.getUuid();
+        Long iops = (srcInfo.getMaxIops() != null && 
srcInfo.getMaxIops().longValue() > 0) ? srcInfo.getMaxIops() : null;
+        SpApiResponse response = StorPoolUtil.volumeCopy(volumeName, baseOn, 
"volume", iops, vmUuid, vcPolicyTag, conn);
+        if (response.getError() != null) {
+            return new CopyCmdAnswer(String.format("Could not copy volume [%s] 
due to %s", baseOn, response.getError()));
+        }
+        String newVolume = StorPoolUtil.getNameFromResponse(response, false);
+
+        StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc copy 
volume[%s] from pool[%s] with a new name [%s]",
+                baseOn, srcInfo.getDataStore().getName(), newVolume);
+
+        srcTO.setSize(srcInfo.getSize());
+        srcTO.setPath(StorPoolUtil.devPath(newVolume));
+
+        return new CopyCmdAnswer(srcTO);
+    }
+
+    /**
+     * Live migrate the StorPool's volume to another StorPool template
+     * @param srcData The source data volume
+     * @param dstData The destination data volume
+     * @param name The volume's name
+     * @param conn StorPool's connection
+     * @return Answer
+     */
+    private Answer migrateVolume(DataObject srcData, DataObject dstData, 
String name, SpConnectionDesc conn) {
+        Answer answer;
+        SpApiResponse resp = StorPoolUtil.volumeUpdateTemplate(name, conn);
+        if (resp.getError() != null) {
+            answer = new Answer(null, false, String.format("Could not migrate 
volume %s to %s due to %s", name, conn.getTemplateName(), resp.getError()));
+        } else {
+            StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc 
migrate volume[%s] from pool[%s] to pool[%s]",
+                    name, 
srcData.getDataStore().getName(),dstData.getDataStore().getName());
+            VolumeVO updatedVolume = volumeDao.findById(srcData.getId());
+            updatedVolume.setPoolId(dstData.getDataStore().getId());
+            updatedVolume.setLastPoolId(srcData.getDataStore().getId());
+            volumeDao.update(updatedVolume.getId(), updatedVolume);
+            answer = new Answer(null, true, null);
+        }
+        return answer;
+    }
+
     @Override
     public void takeSnapshot(SnapshotInfo snapshot, 
AsyncCompletionCallback<CreateCmdResult> callback) {
         String snapshotName = snapshot.getUuid();
@@ -883,7 +954,7 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
         }
 
         if (vinfo.getMaxIops() != null) {
-            response = StorPoolUtil.volumeUpadateTags(volumeName, null, 
vinfo.getMaxIops(), conn, null);
+            response = StorPoolUtil.volumeUpdateTags(volumeName, null, 
vinfo.getMaxIops(), conn, null);
             if (response.getError() != null) {
                 StorPoolUtil.spLog("Volume was reverted successfully but max 
iops could not be set due to %s", response.getError().getDescr());
             }
@@ -942,7 +1013,7 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
                 SpConnectionDesc conn = 
StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), 
storagePoolDetailsDao, primaryStoreDao);
                 String volName = 
StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true);
                 VMInstanceVO userVM = vmInstanceDao.findById(vmId);
-                SpApiResponse resp = StorPoolUtil.volumeUpadateTags(volName, 
volume.getInstanceId() != null ? userVM.getUuid() : "", null, conn, 
getVcPolicyTag(vmId));
+                SpApiResponse resp = StorPoolUtil.volumeUpdateTags(volName, 
volume.getInstanceId() != null ? userVM.getUuid() : "", null, conn, 
getVcPolicyTag(vmId));
                 if (resp.getError() != null) {
                     log.warn(String.format("Could not update VC policy tags of 
a volume with id [%s]", volume.getUuid()));
                 }
@@ -965,7 +1036,7 @@ public class StorPoolPrimaryDataStoreDriver implements 
PrimaryDataStoreDriver {
             try {
                 SpConnectionDesc conn = 
StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), 
storagePoolDetailsDao, primaryStoreDao);
                 String volName = 
StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true);
-                SpApiResponse resp = StorPoolUtil.volumeUpadateVCTags(volName, 
conn, getVcPolicyTag(vmId));
+                SpApiResponse resp = StorPoolUtil.volumeUpdateVCTags(volName, 
conn, getVcPolicyTag(vmId));
                 if (resp.getError() != null) {
                     log.warn(String.format("Could not update VC policy tags of 
a volume with id [%s]", volume.getUuid()));
                 }
diff --git 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java
 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java
index 4a5ce4012d7..9b5320df6d8 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java
@@ -37,6 +37,7 @@ import 
org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
 import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
 import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
 import 
org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.agent.AgentManager;
@@ -118,21 +119,22 @@ public class StorPoolHostListener implements 
HypervisorHostListener {
         final Answer answer = agentMgr.easySend(hostId, cmd);
 
         StoragePoolHostVO poolHost = 
storagePoolHostDao.findByPoolHost(pool.getId(), hostId);
+        boolean isPoolConnectedToTheHost = poolHost != null;
 
         if (answer == null) {
+            StorPoolUtil.spLog("Storage pool [%s] is not connected to the host 
[%s]", poolVO.getName(), host.getName());
+            deleteVolumeWhenHostCannotConnectPool(conn, volumeOnPool);
+            removePoolOnHost(poolHost, isPoolConnectedToTheHost);
             throw new CloudRuntimeException("Unable to get an answer to the 
modify storage pool command" + pool.getId());
         }
 
         if (!answer.getResult()) {
-            if (answer.getDetails() != null) {
-                if (answer.getDetails().equals("objectDoesNotExist")) {
-                    
StorPoolUtil.volumeDelete(StorPoolStorageAdaptor.getVolumeNameFromPath(volumeOnPool.getValue(),
 true), conn);
-                    storagePoolDetailsDao.remove(volumeOnPool.getId());
-                    return false;
-                } else if (answer.getDetails().equals("spNotFound")) {
-                    return false;
-                }
+            StorPoolUtil.spLog("Storage pool [%s] is not connected to the host 
[%s]", poolVO.getName(), host.getName());
+            removePoolOnHost(poolHost, isPoolConnectedToTheHost);
 
+            if (answer.getDetails() != null && 
isStorPoolVolumeOrStorageNotExistsOnHost(answer)) {
+                deleteVolumeWhenHostCannotConnectPool(conn, volumeOnPool);
+                return false;
             }
             String msg = "Unable to attach storage pool" + poolId + " to the 
host" + hostId;
             alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, 
pool.getDataCenterId(), pool.getPodId(), msg, msg);
@@ -140,8 +142,6 @@ public class StorPoolHostListener implements 
HypervisorHostListener {
                 pool.getId());
         }
 
-        StorPoolUtil.spLog("hostConnect: hostId=%d, poolId=%d", hostId, 
poolId);
-
         StorPoolModifyStoragePoolAnswer mspAnswer = 
(StorPoolModifyStoragePoolAnswer)answer;
         if (mspAnswer.getLocalDatastoreName() != null && pool.isShared()) {
             String datastoreName = mspAnswer.getLocalDatastoreName();
@@ -155,7 +155,7 @@ public class StorPoolHostListener implements 
HypervisorHostListener {
             }
         }
 
-        if (poolHost == null) {
+        if (!isPoolConnectedToTheHost) {
             poolHost = new StoragePoolHostVO(pool.getId(), hostId, 
mspAnswer.getPoolInfo().getLocalPath().replaceAll("//", "/"));
             storagePoolHostDao.persist(poolHost);
         } else {
@@ -164,10 +164,25 @@ public class StorPoolHostListener implements 
HypervisorHostListener {
 
         StorPoolHelper.setSpClusterIdIfNeeded(hostId, 
mspAnswer.getClusterId(), clusterDao, hostDao, clusterDetailsDao);
 
-        log.info("Connection established between storage pool " + pool + " and 
host " + hostId);
+        StorPoolUtil.spLog("Connection established between storage pool [%s] 
and host [%s]", poolVO.getName(), host.getName());
         return true;
     }
 
+    private boolean isStorPoolVolumeOrStorageNotExistsOnHost(final Answer 
answer) {
+        return StringUtils.equalsAny(answer.getDetails(), 
"objectDoesNotExist", "spNotFound");
+    }
+
+    private void deleteVolumeWhenHostCannotConnectPool(SpConnectionDesc conn, 
StoragePoolDetailVO volumeOnPool) {
+        
StorPoolUtil.volumeDelete(StorPoolStorageAdaptor.getVolumeNameFromPath(volumeOnPool.getValue(),
 true), conn);
+        storagePoolDetailsDao.remove(volumeOnPool.getId());
+    }
+
+    private void removePoolOnHost(StoragePoolHostVO poolHost, boolean 
isPoolConnectedToTheHost) {
+        if (isPoolConnectedToTheHost) {
+            storagePoolHostDao.remove(poolHost.getId());
+        }
+    }
+
     private synchronized StoragePoolDetailVO verifyVolumeIsOnCluster(long 
poolId, SpConnectionDesc conn, long clusterId) {
         StoragePoolDetailVO volumeOnPool = 
storagePoolDetailsDao.findDetail(poolId, StorPoolUtil.SP_VOLUME_ON_CLUSTER + 
"-" + clusterId);
         if (volumeOnPool == null) {
diff --git 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
index 484a9b98931..f859a46ba36 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java
@@ -18,24 +18,16 @@
  */
 package org.apache.cloudstack.storage.datastore.util;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
+import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.OutputInterpreter;
+import com.cloud.utils.script.Script;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
@@ -54,16 +46,23 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.log4j.Logger;
 
-import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor;
-import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.utils.script.OutputInterpreter;
-import com.cloud.utils.script.Script;
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
 public class StorPoolUtil {
     private static final Logger log = Logger.getLogger(StorPoolUtil.class);
@@ -467,7 +466,7 @@ public class StorPoolUtil {
         return POST("MultiCluster/VolumeCreate", json, conn);
     }
 
-    public static SpApiResponse volumeCopy(final String name, final String 
baseOn, String csTag, Long iops,
+    public static SpApiResponse volumeCopy(final String name, final String 
baseOn, String csTag, Long iops, String cvmTag, String vcPolicyTag,
             SpConnectionDesc conn) {
         Map<String, Object> json = new HashMap<>();
         json.put("baseOn", baseOn);
@@ -475,7 +474,7 @@ public class StorPoolUtil {
             json.put("iops", iops);
         }
         json.put("template", conn.getTemplateName());
-        Map<String, String> tags = StorPoolHelper.addStorPoolTags(name, null, 
csTag, null);
+        Map<String, String> tags = StorPoolHelper.addStorPoolTags(name, 
cvmTag, csTag, vcPolicyTag);
         json.put("tags", tags);
         return POST("MultiCluster/VolumeCreate", json, conn);
     }
@@ -501,7 +500,7 @@ public class StorPoolUtil {
         return POST("MultiCluster/VolumeUpdate/" + name, json, conn);
     }
 
-    public static SpApiResponse volumeUpadateTags(final String name, final 
String uuid, Long iops,
+    public static SpApiResponse volumeUpdateTags(final String name, final 
String uuid, Long iops,
             SpConnectionDesc conn, String vcPolicy) {
         Map<String, Object> json = new HashMap<>();
         Map<String, String> tags = StorPoolHelper.addStorPoolTags(null, uuid, 
null, vcPolicy);
@@ -510,20 +509,26 @@ public class StorPoolUtil {
         return POST("MultiCluster/VolumeUpdate/" + name, json, conn);
     }
 
-    public static SpApiResponse volumeUpadateCvmTags(final String name, final 
String uuid, SpConnectionDesc conn) {
+    public static SpApiResponse volumeUpdateCvmTags(final String name, final 
String uuid, SpConnectionDesc conn) {
         Map<String, Object> json = new HashMap<>();
         Map<String, String> tags = StorPoolHelper.addStorPoolTags(null, uuid, 
null, null);
         json.put("tags", tags);
         return POST("MultiCluster/VolumeUpdate/" + name, json, conn);
     }
 
-    public static SpApiResponse volumeUpadateVCTags(final String name, 
SpConnectionDesc conn, String vcPolicy) {
+    public static SpApiResponse volumeUpdateVCTags(final String name, 
SpConnectionDesc conn, String vcPolicy) {
         Map<String, Object> json = new HashMap<>();
         Map<String, String> tags = StorPoolHelper.addStorPoolTags(null, null, 
null, vcPolicy);
         json.put("tags", tags);
         return POST("MultiCluster/VolumeUpdate/" + name, json, conn);
     }
 
+    public static SpApiResponse volumeUpdateTemplate(final String name, 
SpConnectionDesc conn) {
+        Map<String, Object> json = new HashMap<>();
+        json.put("template", conn.getTemplateName());
+        return POST("MultiCluster/VolumeUpdate/" + name, json, conn);
+    }
+
     public static SpApiResponse volumeSnapshot(final String volumeName, final 
String snapshotName, String vmUuid,
             String csTag, String vcPolicy, SpConnectionDesc conn) {
         Map<String, Object> json = new HashMap<>();
diff --git 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java
 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java
index ec7e89a2391..1172600c342 100644
--- 
a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java
+++ 
b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java
@@ -335,7 +335,7 @@ public class StorPoolVMSnapshotStrategy extends 
DefaultVMSnapshotStrategy {
                 }
                 VolumeInfo vinfo = 
volFactory.getVolume(volumeObjectTO.getId());
                 if (vinfo.getMaxIops() != null) {
-                    resp = StorPoolUtil.volumeUpadateTags(volumeName, null, 
vinfo.getMaxIops(), conn, null);
+                    resp = StorPoolUtil.volumeUpdateTags(volumeName, null, 
vinfo.getMaxIops(), conn, null);
 
                     if (resp.getError() != null) {
                         StorPoolUtil.spLog("Volume was reverted successfully 
but max iops could not be set due to %s",
diff --git 
a/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java
 
b/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java
new file mode 100644
index 00000000000..356cac9f569
--- /dev/null
+++ 
b/plugins/storage/volume/storpool/src/test/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriverTest.java
@@ -0,0 +1,245 @@
+/*
+ * 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.datastore.driver;
+
+import com.cloud.agent.api.to.DataObjectType;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.tags.dao.ResourceTagDao;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
+import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
+import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.junit.After;
+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.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.mock;
+
+
+@RunWith(MockitoJUnitRunner.class)
+@PrepareForTest(StorPoolUtil.class)
+public class StorPoolPrimaryDataStoreDriverTest {
+
+    @Mock
+    private VMInstanceDao vmInstanceDao;
+
+    @Mock
+    private ResourceTagDao _resourceTagDao;
+
+    @Mock
+    private AsyncCompletionCallback<CopyCommandResult> callback;
+    @Mock
+    private PrimaryDataStoreDao storagePool;
+    @Mock
+    private StoragePoolDetailsDao detailsDao;
+    @Mock
+    private VolumeDao volumeDao;
+
+    DataStore srcStore;
+    DataStore destStore;
+
+    DataObject srcObj;
+    DataObject destObj;
+
+    VolumeObjectTO srcTO;
+    VolumeObjectTO dstTO;
+
+    PrimaryDataStoreTO dstPrimaryTo;
+    MockedStatic<StorPoolUtil> utilities;
+    StorPoolUtil.SpConnectionDesc conn;
+
+    @Before
+    public void setUp(){
+        utilities = Mockito.mockStatic(StorPoolUtil.class);
+        conn = new StorPoolUtil.SpConnectionDesc("1.1.1.1:81", "123", "tmp");
+
+        srcStore = mock(DataStore.class);
+        destStore = mock(DataStore.class);
+
+        srcObj = mock(VolumeInfo.class);
+        destObj = mock(VolumeInfo.class);
+
+        srcTO = mock(VolumeObjectTO.class);
+        dstTO = mock(VolumeObjectTO.class);
+
+        dstPrimaryTo = mock(PrimaryDataStoreTO.class);
+    }
+
+    @After
+    public void tearDown(){
+        utilities.close();
+    }
+    @InjectMocks
+    private StorPoolPrimaryDataStoreDriver storPoolPrimaryDataStoreDriver;
+
+    @Test
+    public void testMigrateVolumePassed(){
+
+
+        VMInstanceVO vm = mock(VMInstanceVO.class);
+        setReturnsWhenSourceAndDestinationAreVolumes(srcStore, destStore, 
srcObj, destObj, srcTO, dstTO, dstPrimaryTo, vm);
+        when(vm.getState()).thenReturn(State.Running);
+
+        when(StorPoolUtil.getSpConnection(destObj.getDataStore().getUuid(), 
destObj.getDataStore().getId(), detailsDao, storagePool)).thenReturn(conn);
+        StorPoolUtil.SpApiResponse resp = new StorPoolUtil.SpApiResponse();
+        when(StorPoolUtil.volumeUpdateTemplate("~t.t.t", 
conn)).thenReturn(resp);
+
+        
when(volumeDao.findById(srcObj.getId())).thenReturn(mock(VolumeVO.class));
+        storPoolPrimaryDataStoreDriver.copyAsync(srcObj, destObj, callback);
+        utilities.verify(() -> StorPoolUtil.volumeUpdateTemplate("~t.t.t", 
conn), times(1));
+        Assert.assertNull(resp.getError());
+    }
+    @Test
+    public void testMigrateVolumeNotPassed() {
+
+        VMInstanceVO vm = mock(VMInstanceVO.class);
+
+        setReturnsWhenSourceAndDestinationAreVolumes(srcStore, destStore, 
srcObj, destObj, srcTO, dstTO, dstPrimaryTo, vm);
+        when(vm.getState()).thenReturn(State.Running);
+
+        when(StorPoolUtil.getSpConnection(destObj.getDataStore().getUuid(), 
destObj.getDataStore().getId(), detailsDao, storagePool)).thenReturn(conn);
+        StorPoolUtil.SpApiResponse resp = new StorPoolUtil.SpApiResponse();
+        setResponseError(resp);
+        when(StorPoolUtil.volumeUpdateTemplate("~t.t.t", 
conn)).thenReturn(resp);
+        storPoolPrimaryDataStoreDriver.copyAsync(srcObj, destObj, callback);
+        Assert.assertNotNull(resp.getError());
+    }
+
+    @Test
+    public void testCopyVolumeAttachedToVmPassed() {
+
+        VMInstanceVO vm = mock(VMInstanceVO.class);
+
+        setReturnsWhenSourceAndDestinationAreVolumes(srcStore, destStore, 
srcObj, destObj, srcTO, dstTO, dstPrimaryTo, vm);
+        when(vm.getState()).thenReturn(State.Stopped);
+        String vmUuid = UUID.randomUUID().toString();
+        when(vm.getUuid()).thenReturn(vmUuid);
+
+        when(StorPoolUtil.getSpConnection(destObj.getDataStore().getUuid(), 
destObj.getDataStore().getId(), detailsDao, storagePool)).thenReturn(conn);
+
+        StorPoolUtil.SpApiResponse response = new StorPoolUtil.SpApiResponse();
+        String volumeUuid = UUID.randomUUID().toString();
+        when(srcObj.getUuid()).thenReturn(volumeUuid);
+        when(StorPoolUtil.volumeCopy(volumeUuid, "~t.t.t", "volume", null, 
vmUuid, "", conn)).thenReturn(response);
+        storPoolPrimaryDataStoreDriver.copyAsync(srcObj, destObj, callback);
+        Assert.assertNull(response.getError());
+    }
+
+    @Test
+    public void testCopyVolumeAttachedToVmNotPassed() {
+
+        VMInstanceVO vm = mock(VMInstanceVO.class);
+
+        setReturnsWhenSourceAndDestinationAreVolumes(srcStore, destStore, 
srcObj, destObj, srcTO, dstTO, dstPrimaryTo, vm);
+        when(vm.getState()).thenReturn(State.Stopped);
+        String vmUuid = UUID.randomUUID().toString();
+        when(vm.getUuid()).thenReturn(vmUuid);
+
+        when(StorPoolUtil.getSpConnection(destObj.getDataStore().getUuid(), 
destObj.getDataStore().getId(), detailsDao, storagePool)).thenReturn(conn);
+        StorPoolUtil.SpApiResponse response = new StorPoolUtil.SpApiResponse();
+        setResponseError(response);
+        String volumeUuid = UUID.randomUUID().toString();
+        when(srcObj.getUuid()).thenReturn(volumeUuid);
+        when(StorPoolUtil.volumeCopy(volumeUuid, "~t.t.t", "volume", null, 
vmUuid, "", conn)).thenReturn(response);
+        storPoolPrimaryDataStoreDriver.copyAsync(srcObj, destObj, callback);
+        Assert.assertNotNull(response.getError());
+    }
+    @Test
+    public void testCopyVolumeNotAttachedToVmNotPassed() {
+        setReturnsWhenSourceAndDestinationAreVolumes(srcStore, destStore, 
srcObj, destObj, srcTO, dstTO, dstPrimaryTo, null);
+
+        when(StorPoolUtil.getSpConnection(destObj.getDataStore().getUuid(), 
destObj.getDataStore().getId(), detailsDao, storagePool)).thenReturn(conn);
+        StorPoolUtil.SpApiResponse response = new StorPoolUtil.SpApiResponse();
+        setResponseError(response);
+        String volumeUuid = UUID.randomUUID().toString();
+        when(srcObj.getUuid()).thenReturn(volumeUuid);
+        when(StorPoolUtil.volumeCopy(volumeUuid, "~t.t.t", "volume", null, 
null, null, conn)).thenReturn(response);
+        storPoolPrimaryDataStoreDriver.copyAsync(srcObj, destObj, callback);
+        utilities.verify(() -> StorPoolUtil.volumeCopy(volumeUuid, "~t.t.t", 
"volume", null, null, null, conn), times(1));
+
+        Assert.assertNotNull(response.getError());
+    }
+
+    @Test
+    public void testCopyVolumeNotAttachedToVmPassed() {
+
+        setReturnsWhenSourceAndDestinationAreVolumes(srcStore, destStore, 
srcObj, destObj, srcTO, dstTO, dstPrimaryTo, null);
+
+        when(StorPoolUtil.getSpConnection(destObj.getDataStore().getUuid(), 
destObj.getDataStore().getId(), detailsDao, storagePool)).thenReturn(conn);
+        StorPoolUtil.SpApiResponse response = new StorPoolUtil.SpApiResponse();
+        String volumeUuid = UUID.randomUUID().toString();
+        when(srcObj.getUuid()).thenReturn(volumeUuid);
+        when(StorPoolUtil.volumeCopy(volumeUuid, "~t.t.t", "volume", null, 
null, null, conn)).thenReturn(response);
+        storPoolPrimaryDataStoreDriver.copyAsync(srcObj, destObj, callback);
+        utilities.verify(() -> StorPoolUtil.volumeCopy(volumeUuid, "~t.t.t", 
"volume", null, null, null, conn), times(1));
+        Assert.assertNull(response.getError());
+    }
+
+    private void setReturnsWhenSourceAndDestinationAreVolumes(DataStore 
srcStore, DataStore destStore, DataObject srcObj, DataObject destObj, 
VolumeObjectTO srcTO, VolumeObjectTO dstTO, PrimaryDataStoreTO dstPrimaryTo, 
VMInstanceVO vm) {
+        when(srcStore.getRole()).thenReturn(DataStoreRole.Primary);
+        when(destStore.getRole()).thenReturn(DataStoreRole.Primary);
+        when(srcObj.getDataStore()).thenReturn(srcStore);
+        when(destObj.getDataStore()).thenReturn(destStore);
+        when(srcObj.getType()).thenReturn(DataObjectType.VOLUME);
+        when(destObj.getType()).thenReturn(DataObjectType.VOLUME);
+        when(destObj.getTO()).thenReturn(dstTO);
+        when(srcObj.getTO()).thenReturn(srcTO);
+
+        
when(srcObj.getDataStore().getDriver()).thenReturn(storPoolPrimaryDataStoreDriver);
+        when(destObj.getTO().getDataStore()).thenReturn(dstPrimaryTo);
+        
when(destObj.getDataStore().getUuid()).thenReturn("SP_API_HTTP=1.1.1.1:81;SP_AUTH_TOKEN=token;SP_TEMPLATE=template_name");
+        when(destObj.getDataStore().getId()).thenReturn(1L);
+
+        when(srcTO.getPath()).thenReturn("/dev/storpool-byid/t.t.t");
+        when(dstPrimaryTo.getPoolType()).thenReturn(StoragePoolType.StorPool);
+        when(vmInstanceDao.findById(anyLong())).thenReturn(vm);
+    }
+
+    private static void setResponseError(StorPoolUtil.SpApiResponse resp) {
+        StorPoolUtil.SpApiError respErr = new StorPoolUtil.SpApiError();
+        respErr.setName("error");
+        respErr.setDescr("Failed");
+        resp.setError(respErr);
+    }
+}
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java 
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 86849a87a91..5f7b7fb7a83 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -2927,7 +2927,7 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
             throw new InvalidParameterValueException("Volume must be in ready 
state");
         }
 
-        if (vol.getPoolId() == storagePoolId) {
+        if (vol.getPoolId() == storagePoolId.longValue()) {
             throw new InvalidParameterValueException("Volume " + vol + " is 
already on the destination storage pool");
         }
 
@@ -2976,9 +2976,13 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
                 }
 
                 if (liveMigrateVolume && 
HypervisorType.KVM.equals(host.getHypervisorType())) {
-                    throw new InvalidParameterValueException("KVM does not 
support volume live migration due to the limited possibility to refresh VM XML 
domain. " +
-                            "Therefore, to live migrate a volume between 
storage pools, one must migrate the VM to a different host as well to force the 
VM XML domain update. " +
-                            "Use 'migrateVirtualMachineWithVolumes' instead.");
+                    StoragePoolVO destinationStoragePoolVo = 
_storagePoolDao.findById(storagePoolId);
+
+                    if (isSourceOrDestNotOnStorPool(storagePoolVO, 
destinationStoragePoolVo)) {
+                        throw new InvalidParameterValueException("KVM does not 
support volume live migration due to the limited possibility to refresh VM XML 
domain. " +
+                                "Therefore, to live migrate a volume between 
storage pools, one must migrate the VM to a different host as well to force the 
VM XML domain update. " +
+                                "Use 'migrateVirtualMachineWithVolumes' 
instead.");
+                    }
                 }
             }
 
@@ -3125,6 +3129,11 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         return orchestrateMigrateVolume(vol, destPool, liveMigrateVolume, 
newDiskOffering);
     }
 
+    private boolean isSourceOrDestNotOnStorPool(StoragePoolVO storagePoolVO, 
StoragePoolVO destinationStoragePoolVo) {
+        return storagePoolVO.getPoolType() != Storage.StoragePoolType.StorPool
+                || destinationStoragePoolVo.getPoolType() != 
Storage.StoragePoolType.StorPool;
+    }
+
     /**
      * Retrieves the new disk offering UUID that might be sent to replace the 
current one in the volume being migrated.
      * If no disk offering UUID is provided we return null. Otherwise, we 
perform the following checks.
diff --git a/test/integration/plugins/storpool/MigrateVolumeToStorPool.py 
b/test/integration/plugins/storpool/MigrateVolumeToStorPool.py
index 1849718f3de..a7f87d9fa87 100644
--- a/test/integration/plugins/storpool/MigrateVolumeToStorPool.py
+++ b/test/integration/plugins/storpool/MigrateVolumeToStorPool.py
@@ -194,9 +194,12 @@ class TestMigrateVolumeToAnotherPool(cloudstackTestCase):
         securitygroup = SecurityGroup.list(cls.apiclient, account = 
cls.account.name, domainid= cls.account.domainid)[0]
         cls.helper.set_securityGroups(cls.apiclient, account = 
cls.account.name, domainid= cls.account.domainid, id = securitygroup.id)
 
+        cls.clusters = cls.helper.getClustersWithStorPool(cls.apiclient, 
cls.zone.id,)
+
         cls.vm = VirtualMachine.create(cls.apiclient,
             {"name":"StorPool-%s" % uuid.uuid4() },
             zoneid=cls.zone.id,
+            clusterid=random.choice(cls.clusters),
             templateid=template.id,
             accountid=cls.account.name,
             domainid=cls.account.domainid,
@@ -207,6 +210,7 @@ class TestMigrateVolumeToAnotherPool(cloudstackTestCase):
         cls.vm2 = VirtualMachine.create(cls.apiclient,
             {"name":"StorPool-%s" % uuid.uuid4() },
             zoneid=cls.zone.id,
+            clusterid=random.choice(cls.clusters),
             templateid=template.id,
             accountid=cls.account.name,
             domainid=cls.account.domainid,
@@ -217,6 +221,7 @@ class TestMigrateVolumeToAnotherPool(cloudstackTestCase):
         cls.vm3 = VirtualMachine.create(cls.apiclient,
             {"name":"StorPool-%s" % uuid.uuid4() },
             zoneid=cls.zone.id,
+            clusterid=random.choice(cls.clusters),
             templateid=template.id,
             accountid=cls.account.name,
             domainid=cls.account.domainid,
@@ -227,6 +232,7 @@ class TestMigrateVolumeToAnotherPool(cloudstackTestCase):
         cls.vm4 = VirtualMachine.create(cls.apiclient,
             {"name":"StorPool-%s" % uuid.uuid4() },
             zoneid=cls.zone.id,
+            clusterid=random.choice(cls.clusters),
             templateid=template.id,
             accountid=cls.account.name,
             domainid=cls.account.domainid,
@@ -237,6 +243,7 @@ class TestMigrateVolumeToAnotherPool(cloudstackTestCase):
         cls.vm5 = VirtualMachine.create(cls.apiclient,
             {"name":"StorPool-%s" % uuid.uuid4() },
             zoneid=cls.zone.id,
+            clusterid=random.choice(cls.clusters),
             templateid=template.id,
             accountid=cls.account.name,
             domainid=cls.account.domainid,
diff --git a/test/integration/plugins/storpool/sp_util.py 
b/test/integration/plugins/storpool/sp_util.py
index 68da18d0075..76edba075ed 100644
--- a/test/integration/plugins/storpool/sp_util.py
+++ b/test/integration/plugins/storpool/sp_util.py
@@ -44,6 +44,7 @@ from marvin.lib.common import (get_zone,
 from marvin.cloudstackAPI import (listOsTypes,
                                   listTemplates,
                                   listHosts,
+                                  listClusters,
                                   createTemplate,
                                   createVolume,
                                   getVolumeSnapshotDetails,
@@ -745,3 +746,33 @@ class StorPoolHelper():
         cmd.id = vmid
         cmd.hostid = hostid
         return (apiclient.startVirtualMachine(cmd))
+
+    @classmethod
+    def getClustersWithStorPool(cls, apiclient, zoneId,):
+        cmd = listClusters.listClustersCmd()
+        cmd.zoneid = zoneId
+        cmd.allocationstate = "Enabled"
+        clusters = apiclient.listClusters(cmd)
+        clustersToDeploy = []
+        for cluster in clusters:
+            if cluster.resourcedetails['sp.cluster.id']:
+                clustersToDeploy.append(cluster.id)
+
+        return clustersToDeploy
+
+    @classmethod
+    def getHostToDeployOrMigrate(cls, apiclient, hostsToavoid, 
clustersToDeploy):
+        hostsOnCluster = []
+        for c in clustersToDeploy:
+            hostsOnCluster.append(cls.list_hosts_by_cluster_id(apiclient, c))
+
+        destinationHost = None
+        for host in hostsOnCluster:
+
+            if hostsToavoid is None:
+                return host[0]
+            if host[0].id not in hostsToavoid:
+                destinationHost = host[0]
+                break
+
+        return destinationHost
\ No newline at end of file
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index e22bfafa4df..c9633210aa0 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -518,7 +518,7 @@ class VirtualMachine:
                serviceofferingid=None, securitygroupids=None,
                projectid=None, startvm=None, diskofferingid=None,
                affinitygroupnames=None, affinitygroupids=None, group=None,
-               hostid=None, keypair=None, ipaddress=None, mode='default',
+               hostid=None, clusterid=None, keypair=None, ipaddress=None, 
mode='default',
                method='GET', hypervisor=None, customcpunumber=None,
                customcpuspeed=None, custommemory=None, rootdisksize=None,
                rootdiskcontroller=None, vpcid=None, macaddress=None, 
datadisktemplate_diskoffering_list={},
@@ -609,6 +609,9 @@ class VirtualMachine:
         if hostid:
             cmd.hostid = hostid
 
+        if clusterid:
+            cmd.clusterid = clusterid
+
         if "userdata" in services:
             cmd.userdata = 
base64.urlsafe_b64encode(services["userdata"].encode()).decode()
 

Reply via email to