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 @@
commons-collections4
${cs.commons-collections.version}
+
+ org.mockito
+ mockito-core
+ 4.7.0
+
+
+ org.mockito
+ mockito-inline
+ 4.7.0
+
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 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 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 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 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,51 +319,27 @@ 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 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.");
- }
-
- 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.");
- }
-
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath,
KVMStoragePool destPool, ImageFormat format, int timeout) {
return null;
@@ -390,4 +349,22 @@ public class StorPoolStorageAdaptor implements StorageAdaptor {
public boolean createFolder(String uuid, String path, String localPath) {
return false;
}
+
+ @Override
+ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format,
+ ProvisioningType provisioningType, long size, byte[] passphrase) {
+ return null;
+ }
+
+ @Override
+ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format,
+ ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) {
+ return null;
+ }
+
+ @Override
+ 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,10 +17,47 @@
* under the License.
*/
package org.apache.cloudstack.storage.datastore.driver;
-import java.util.Map;
-
-import javax.inject.Inject;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.storage.ResizeVolumeAnswer;
+import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand;
+import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand;
+import com.cloud.agent.api.storage.StorPoolCopyVolumeToSecondaryCommand;
+import com.cloud.agent.api.storage.StorPoolDownloadTemplateCommand;
+import com.cloud.agent.api.storage.StorPoolDownloadVolumeCommand;
+import com.cloud.agent.api.storage.StorPoolResizeVolumeCommand;
+import com.cloud.agent.api.to.DataObjectType;
+import com.cloud.agent.api.to.DataStoreTO;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.dc.dao.ClusterDao;
+import com.cloud.host.Host;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor;
+import com.cloud.server.ResourceTag;
+import com.cloud.server.ResourceTag.ResourceObjectType;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.ResizeVolumePayload;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.StorageManager;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.VMTemplateDetailVO;
+import com.cloud.storage.VMTemplateStoragePoolVO;
+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.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;
@@ -58,45 +95,8 @@ 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;
-import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand;
-import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand;
-import com.cloud.agent.api.storage.StorPoolCopyVolumeToSecondaryCommand;
-import com.cloud.agent.api.storage.StorPoolDownloadTemplateCommand;
-import com.cloud.agent.api.storage.StorPoolDownloadVolumeCommand;
-import com.cloud.agent.api.storage.StorPoolResizeVolumeCommand;
-import com.cloud.agent.api.to.DataObjectType;
-import com.cloud.agent.api.to.DataStoreTO;
-import com.cloud.agent.api.to.DataTO;
-import com.cloud.agent.api.to.StorageFilerTO;
-import com.cloud.dc.dao.ClusterDao;
-import com.cloud.host.Host;
-import com.cloud.host.dao.HostDao;
-import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor;
-import com.cloud.server.ResourceTag;
-import com.cloud.server.ResourceTag.ResourceObjectType;
-import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.ResizeVolumePayload;
-import com.cloud.storage.Storage.StoragePoolType;
-import com.cloud.storage.StorageManager;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.VMTemplateDetailVO;
-import com.cloud.storage.VMTemplateStoragePoolVO;
-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.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.VirtualMachineManager;
-import com.cloud.vm.dao.VMInstanceDao;
+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 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());
+ 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)));
+ 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());
- }
+ 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 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 json = new HashMap<>();
json.put("baseOn", baseOn);
@@ -475,7 +474,7 @@ public class StorPoolUtil {
json.put("iops", iops);
}
json.put("template", conn.getTemplateName());
- Map tags = StorPoolHelper.addStorPoolTags(name, null, csTag, null);
+ Map 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 json = new HashMap<>();
Map 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 json = new HashMap<>();
Map 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 json = new HashMap<>();
Map 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 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 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 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 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()