mirror of https://github.com/apache/cloudstack.git
Enable live volume migration for StorPool and small fixes (#6661)
This commit is contained in:
parent
9cfebdf6e5
commit
b392084950
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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());
|
||||
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<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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<>();
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue