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()