mirror of https://github.com/apache/cloudstack.git
support live vm migration between clvm -> clvm-ng (vice-versa), nfs -> clvm (vice-versa) and nfs->clvm-ng (vice-versa)
This commit is contained in:
parent
7e5e1e70e3
commit
437f77c97e
|
|
@ -185,6 +185,7 @@ public class MigrateCommand extends Command {
|
|||
private final String sourceText;
|
||||
private final String backingStoreText;
|
||||
private boolean isSourceDiskOnStorageFileSystem;
|
||||
private Storage.StoragePoolType sourcePoolType;
|
||||
private Storage.StoragePoolType destPoolType;
|
||||
|
||||
public MigrateDiskInfo(final String serialNumber, final DiskType diskType, final DriverType driverType, final Source source, final String sourceText) {
|
||||
|
|
@ -235,6 +236,14 @@ public class MigrateCommand extends Command {
|
|||
this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage;
|
||||
}
|
||||
|
||||
public Storage.StoragePoolType getSourcePoolType() {
|
||||
return sourcePoolType;
|
||||
}
|
||||
|
||||
public void setSourcePoolType(Storage.StoragePoolType sourcePoolType) {
|
||||
this.sourcePoolType = sourcePoolType;
|
||||
}
|
||||
|
||||
public Storage.StoragePoolType getDestPoolType() {
|
||||
return destPoolType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2121,6 +2121,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
|||
migrateDiskInfo.setSourceDiskOnStorageFileSystem(isStoragePoolTypeOfFile(sourceStoragePool));
|
||||
migrateDiskInfoList.add(migrateDiskInfo);
|
||||
}
|
||||
migrateDiskInfo.setSourcePoolType(sourceStoragePool.getPoolType());
|
||||
migrateDiskInfo.setDestPoolType(destVolumeInfo.getStoragePoolType());
|
||||
prepareDiskWithSecretConsumerDetail(vmTO, srcVolumeInfo, destVolumeInfo.getPath());
|
||||
|
||||
|
|
|
|||
|
|
@ -244,25 +244,6 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||
final ExecutorService executor = Executors.newFixedThreadPool(1);
|
||||
boolean migrateNonSharedInc = command.isMigrateNonSharedInc() && !migrateStorageManaged;
|
||||
|
||||
// If any of the destination disks target CLVM/CLVM_NG pools, exclude them from
|
||||
// the migration disk list. These disks are already created/activated on the
|
||||
// destination storage before VM migration, so libvirt should not try to migrate them.
|
||||
// Only non-CLVM disks will be migrated by libvirt.
|
||||
boolean effectiveMigrateStorage = migrateStorage;
|
||||
if (migrateStorage && migrateDiskLabels != null && hasClvmDestinationDisks(mapMigrateStorage)) {
|
||||
logger.info("Filtering out CLVM/CLVM_NG disks from migration for VM {} as they are pre-created on destination", vmName);
|
||||
migrateDiskLabels = filterOutClvmDisks(migrateDiskLabels, disks, mapMigrateStorage);
|
||||
logger.debug("Remaining disks to migrate via libvirt: {}", migrateDiskLabels);
|
||||
|
||||
// If all disks were filtered out (only CLVM/CLVM_NG), disable storage migration entirely
|
||||
// to prevent libvirt from trying to handle the block devices
|
||||
if (migrateDiskLabels != null && migrateDiskLabels.isEmpty()) {
|
||||
logger.info("All disks are CLVM/CLVM_NG and pre-created on destination. Disabling storage migration for VM {}", vmName);
|
||||
effectiveMigrateStorage = false;
|
||||
migrateDiskLabels = null;
|
||||
}
|
||||
}
|
||||
|
||||
// add cancel hook before we start. If migration fails to start and hook is called, it's non-fatal
|
||||
cancelHook = new MigrationCancelHook(dm);
|
||||
libvirtComputingResource.addDisconnectHook(cancelHook);
|
||||
|
|
@ -270,7 +251,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||
libvirtComputingResource.createOrUpdateLogFileForCommand(command, Command.State.PROCESSING);
|
||||
|
||||
final Callable<Domain> worker = new MigrateKVMAsync(libvirtComputingResource, dm, dconn, xmlDesc,
|
||||
effectiveMigrateStorage, migrateNonSharedInc,
|
||||
migrateStorage, migrateNonSharedInc,
|
||||
command.isAutoConvergence(), vmName, command.getDestinationIp(), migrateDiskLabels);
|
||||
final Future<Domain> migrateThread = executor.submit(worker);
|
||||
executor.shutdown();
|
||||
|
|
@ -827,11 +808,8 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||
for (int z = 0; z < diskChildNodes.getLength(); z++) {
|
||||
Node diskChildNode = diskChildNodes.item(z);
|
||||
|
||||
// Update driver type for managed storage OR when migrating to CLVM
|
||||
// CLVM uses RAW format requiring XML driver type update
|
||||
// Note: CLVM_NG uses QCOW2-on-block, so no format change needed (already QCOW2)
|
||||
boolean shouldUpdateDriverType = migrateStorageManaged ||
|
||||
(migrateDiskInfo.getDestPoolType() == Storage.StoragePoolType.CLVM);
|
||||
boolean shouldUpdateDriverType = shouldUpdateDriverTypeForMigration(
|
||||
migrateStorageManaged, migrateDiskInfo);
|
||||
|
||||
if (shouldUpdateDriverType && "driver".equals(diskChildNode.getNodeName())) {
|
||||
Node driverNode = diskChildNode;
|
||||
|
|
@ -1188,4 +1166,29 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||
}
|
||||
return (Storage.StoragePoolType.CLVM.equals(diskInfo.getDestPoolType()) || Storage.StoragePoolType.CLVM_NG.equals(diskInfo.getDestPoolType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the driver type should be updated during migration based on CLVM involvement.
|
||||
* The driver type needs to be updated when:
|
||||
* - Managed storage is being migrated, OR
|
||||
* - Source pool is CLVM or CLVM_NG, OR
|
||||
* - Destination pool is CLVM or CLVM_NG
|
||||
*
|
||||
* This ensures the libvirt XML driver type matches the destination format (raw/qcow2/etc).
|
||||
*
|
||||
* @param migrateStorageManaged true if migrating managed storage
|
||||
* @param migrateDiskInfo the migration disk information containing source and destination pool types
|
||||
* @return true if driver type should be updated, false otherwise
|
||||
*/
|
||||
private boolean shouldUpdateDriverTypeForMigration(boolean migrateStorageManaged,
|
||||
MigrateCommand.MigrateDiskInfo migrateDiskInfo) {
|
||||
boolean sourceIsClvm = Storage.StoragePoolType.CLVM == migrateDiskInfo.getSourcePoolType() ||
|
||||
Storage.StoragePoolType.CLVM_NG == migrateDiskInfo.getSourcePoolType();
|
||||
|
||||
boolean destIsClvm = Storage.StoragePoolType.CLVM == migrateDiskInfo.getDestPoolType() ||
|
||||
Storage.StoragePoolType.CLVM_NG == migrateDiskInfo.getDestPoolType();
|
||||
|
||||
boolean isClvmRelatedMigration = sourceIsClvm || destIsClvm;
|
||||
return migrateStorageManaged || isClvmRelatedMigration;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1871,13 +1871,22 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid());
|
||||
disksize = volume.getSize();
|
||||
PhysicalDiskFormat format;
|
||||
if (volume.getFormat() == null || StoragePoolType.RBD.equals(primaryStore.getPoolType())) {
|
||||
|
||||
MigrationOptions migrationOptions = volume.getMigrationOptions();
|
||||
boolean useDstPoolFormat = useDestPoolFormat(migrationOptions, primaryStore);
|
||||
|
||||
if (volume.getFormat() == null || StoragePoolType.RBD.equals(primaryStore.getPoolType()) || useDstPoolFormat) {
|
||||
format = primaryPool.getDefaultFormat();
|
||||
if (useDstPoolFormat) {
|
||||
logger.debug("Using destination pool default format {} for volume {} due to CLVM migration (src: {}, dst: {})",
|
||||
format, volume.getUuid(),
|
||||
migrationOptions != null ? migrationOptions.getSrcPoolType() : "unknown",
|
||||
primaryStore.getPoolType());
|
||||
}
|
||||
} else {
|
||||
format = PhysicalDiskFormat.valueOf(volume.getFormat().toString().toUpperCase());
|
||||
}
|
||||
|
||||
MigrationOptions migrationOptions = volume.getMigrationOptions();
|
||||
if (migrationOptions != null) {
|
||||
int timeout = migrationOptions.getTimeout();
|
||||
|
||||
|
|
@ -1914,6 +1923,29 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For migration involving CLVM (RAW format), use destination pool's default format
|
||||
* CLVM uses RAW format which may not match destination pool's format (e.g., NFS uses QCOW2)
|
||||
* This specifically handles:
|
||||
* - CLVM (RAW) -> NFS/Local/CLVM_NG (QCOW2)
|
||||
* - NFS/Local/CLVM_NG (QCOW2) -> CLVM (RAW)
|
||||
* @param migrationOptions
|
||||
* @param primaryStore
|
||||
* @return
|
||||
*/
|
||||
private boolean useDestPoolFormat(MigrationOptions migrationOptions, PrimaryDataStoreTO primaryStore) {
|
||||
boolean useDstPoolFormat = false;
|
||||
if (migrationOptions != null && migrationOptions.getSrcPoolType() != null) {
|
||||
StoragePoolType srcPoolType = migrationOptions.getSrcPoolType();
|
||||
StoragePoolType dstPoolType = primaryStore.getPoolType();
|
||||
|
||||
if (srcPoolType != dstPoolType) {
|
||||
useDstPoolFormat = (srcPoolType == StoragePoolType.CLVM || dstPoolType == StoragePoolType.CLVM);
|
||||
}
|
||||
}
|
||||
return useDstPoolFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML to take disk-only snapshot of the VM.<br><br>
|
||||
* 1st parameter: snapshot's name;<br>
|
||||
|
|
|
|||
|
|
@ -2609,6 +2609,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
|
||||
lvcreate.add("-n", volumeUuid);
|
||||
lvcreate.add("-L", lvSize + "B");
|
||||
lvcreate.add("--yes");
|
||||
lvcreate.add(vgName);
|
||||
|
||||
String result = lvcreate.execute();
|
||||
|
|
@ -2686,6 +2687,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
|
||||
lvcreate.add("-n", lvName);
|
||||
lvcreate.add("-L", lvSize + "B");
|
||||
lvcreate.add("--yes");
|
||||
lvcreate.add(vgName);
|
||||
String result = lvcreate.execute();
|
||||
if (result != null) {
|
||||
|
|
@ -2757,6 +2759,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
|||
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
|
||||
lvcreate.add("-n", volumeName);
|
||||
lvcreate.add("-L", size + "B");
|
||||
lvcreate.add("--yes");
|
||||
lvcreate.add(vgName);
|
||||
|
||||
String result = lvcreate.execute();
|
||||
|
|
|
|||
Loading…
Reference in New Issue