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:
Pearl Dsilva 2026-03-24 15:07:36 -04:00
parent 7e5e1e70e3
commit 437f77c97e
5 changed files with 75 additions and 27 deletions

View File

@ -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;
}

View File

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

View File

@ -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;
}
}

View File

@ -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>

View File

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