Skip to content

Commit 437f77c

Browse files
committed
support live vm migration between clvm -> clvm-ng (vice-versa), nfs -> clvm (vice-versa) and nfs->clvm-ng (vice-versa)
1 parent 7e5e1e7 commit 437f77c

File tree

5 files changed

+75
-27
lines changed

5 files changed

+75
-27
lines changed

core/src/main/java/com/cloud/agent/api/MigrateCommand.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ public String toString() {
185185
private final String sourceText;
186186
private final String backingStoreText;
187187
private boolean isSourceDiskOnStorageFileSystem;
188+
private Storage.StoragePoolType sourcePoolType;
188189
private Storage.StoragePoolType destPoolType;
189190

190191
public MigrateDiskInfo(final String serialNumber, final DiskType diskType, final DriverType driverType, final Source source, final String sourceText) {
@@ -235,6 +236,14 @@ public void setSourceDiskOnStorageFileSystem(boolean isDiskOnFileSystemStorage)
235236
this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage;
236237
}
237238

239+
public Storage.StoragePoolType getSourcePoolType() {
240+
return sourcePoolType;
241+
}
242+
243+
public void setSourcePoolType(Storage.StoragePoolType sourcePoolType) {
244+
this.sourcePoolType = sourcePoolType;
245+
}
246+
238247
public Storage.StoragePoolType getDestPoolType() {
239248
return destPoolType;
240249
}

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,6 +2121,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
21212121
migrateDiskInfo.setSourceDiskOnStorageFileSystem(isStoragePoolTypeOfFile(sourceStoragePool));
21222122
migrateDiskInfoList.add(migrateDiskInfo);
21232123
}
2124+
migrateDiskInfo.setSourcePoolType(sourceStoragePool.getPoolType());
21242125
migrateDiskInfo.setDestPoolType(destVolumeInfo.getStoragePoolType());
21252126
prepareDiskWithSecretConsumerDetail(vmTO, srcVolumeInfo, destVolumeInfo.getPath());
21262127

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -244,33 +244,14 @@ Use VIR_DOMAIN_XML_SECURE (value = 1) prior to v1.0.0.
244244
final ExecutorService executor = Executors.newFixedThreadPool(1);
245245
boolean migrateNonSharedInc = command.isMigrateNonSharedInc() && !migrateStorageManaged;
246246

247-
// If any of the destination disks target CLVM/CLVM_NG pools, exclude them from
248-
// the migration disk list. These disks are already created/activated on the
249-
// destination storage before VM migration, so libvirt should not try to migrate them.
250-
// Only non-CLVM disks will be migrated by libvirt.
251-
boolean effectiveMigrateStorage = migrateStorage;
252-
if (migrateStorage && migrateDiskLabels != null && hasClvmDestinationDisks(mapMigrateStorage)) {
253-
logger.info("Filtering out CLVM/CLVM_NG disks from migration for VM {} as they are pre-created on destination", vmName);
254-
migrateDiskLabels = filterOutClvmDisks(migrateDiskLabels, disks, mapMigrateStorage);
255-
logger.debug("Remaining disks to migrate via libvirt: {}", migrateDiskLabels);
256-
257-
// If all disks were filtered out (only CLVM/CLVM_NG), disable storage migration entirely
258-
// to prevent libvirt from trying to handle the block devices
259-
if (migrateDiskLabels != null && migrateDiskLabels.isEmpty()) {
260-
logger.info("All disks are CLVM/CLVM_NG and pre-created on destination. Disabling storage migration for VM {}", vmName);
261-
effectiveMigrateStorage = false;
262-
migrateDiskLabels = null;
263-
}
264-
}
265-
266247
// add cancel hook before we start. If migration fails to start and hook is called, it's non-fatal
267248
cancelHook = new MigrationCancelHook(dm);
268249
libvirtComputingResource.addDisconnectHook(cancelHook);
269250

270251
libvirtComputingResource.createOrUpdateLogFileForCommand(command, Command.State.PROCESSING);
271252

272253
final Callable<Domain> worker = new MigrateKVMAsync(libvirtComputingResource, dm, dconn, xmlDesc,
273-
effectiveMigrateStorage, migrateNonSharedInc,
254+
migrateStorage, migrateNonSharedInc,
274255
command.isAutoConvergence(), vmName, command.getDestinationIp(), migrateDiskLabels);
275256
final Future<Domain> migrateThread = executor.submit(worker);
276257
executor.shutdown();
@@ -827,11 +808,8 @@ protected String replaceStorage(String xmlDesc, Map<String, MigrateCommand.Migra
827808
for (int z = 0; z < diskChildNodes.getLength(); z++) {
828809
Node diskChildNode = diskChildNodes.item(z);
829810

830-
// Update driver type for managed storage OR when migrating to CLVM
831-
// CLVM uses RAW format requiring XML driver type update
832-
// Note: CLVM_NG uses QCOW2-on-block, so no format change needed (already QCOW2)
833-
boolean shouldUpdateDriverType = migrateStorageManaged ||
834-
(migrateDiskInfo.getDestPoolType() == Storage.StoragePoolType.CLVM);
811+
boolean shouldUpdateDriverType = shouldUpdateDriverTypeForMigration(
812+
migrateStorageManaged, migrateDiskInfo);
835813

836814
if (shouldUpdateDriverType && "driver".equals(diskChildNode.getNodeName())) {
837815
Node driverNode = diskChildNode;
@@ -1188,4 +1166,29 @@ private boolean isClvmBlockDevice(MigrateCommand.MigrateDiskInfo diskInfo) {
11881166
}
11891167
return (Storage.StoragePoolType.CLVM.equals(diskInfo.getDestPoolType()) || Storage.StoragePoolType.CLVM_NG.equals(diskInfo.getDestPoolType()));
11901168
}
1169+
1170+
/**
1171+
* Determines if the driver type should be updated during migration based on CLVM involvement.
1172+
* The driver type needs to be updated when:
1173+
* - Managed storage is being migrated, OR
1174+
* - Source pool is CLVM or CLVM_NG, OR
1175+
* - Destination pool is CLVM or CLVM_NG
1176+
*
1177+
* This ensures the libvirt XML driver type matches the destination format (raw/qcow2/etc).
1178+
*
1179+
* @param migrateStorageManaged true if migrating managed storage
1180+
* @param migrateDiskInfo the migration disk information containing source and destination pool types
1181+
* @return true if driver type should be updated, false otherwise
1182+
*/
1183+
private boolean shouldUpdateDriverTypeForMigration(boolean migrateStorageManaged,
1184+
MigrateCommand.MigrateDiskInfo migrateDiskInfo) {
1185+
boolean sourceIsClvm = Storage.StoragePoolType.CLVM == migrateDiskInfo.getSourcePoolType() ||
1186+
Storage.StoragePoolType.CLVM_NG == migrateDiskInfo.getSourcePoolType();
1187+
1188+
boolean destIsClvm = Storage.StoragePoolType.CLVM == migrateDiskInfo.getDestPoolType() ||
1189+
Storage.StoragePoolType.CLVM_NG == migrateDiskInfo.getDestPoolType();
1190+
1191+
boolean isClvmRelatedMigration = sourceIsClvm || destIsClvm;
1192+
return migrateStorageManaged || isClvmRelatedMigration;
1193+
}
11911194
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,13 +1871,22 @@ public Answer createVolume(final CreateObjectCommand cmd) {
18711871
primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid());
18721872
disksize = volume.getSize();
18731873
PhysicalDiskFormat format;
1874-
if (volume.getFormat() == null || StoragePoolType.RBD.equals(primaryStore.getPoolType())) {
1874+
1875+
MigrationOptions migrationOptions = volume.getMigrationOptions();
1876+
boolean useDstPoolFormat = useDestPoolFormat(migrationOptions, primaryStore);
1877+
1878+
if (volume.getFormat() == null || StoragePoolType.RBD.equals(primaryStore.getPoolType()) || useDstPoolFormat) {
18751879
format = primaryPool.getDefaultFormat();
1880+
if (useDstPoolFormat) {
1881+
logger.debug("Using destination pool default format {} for volume {} due to CLVM migration (src: {}, dst: {})",
1882+
format, volume.getUuid(),
1883+
migrationOptions != null ? migrationOptions.getSrcPoolType() : "unknown",
1884+
primaryStore.getPoolType());
1885+
}
18761886
} else {
18771887
format = PhysicalDiskFormat.valueOf(volume.getFormat().toString().toUpperCase());
18781888
}
18791889

1880-
MigrationOptions migrationOptions = volume.getMigrationOptions();
18811890
if (migrationOptions != null) {
18821891
int timeout = migrationOptions.getTimeout();
18831892

@@ -1914,6 +1923,29 @@ public Answer createVolume(final CreateObjectCommand cmd) {
19141923
}
19151924
}
19161925

1926+
/**
1927+
* For migration involving CLVM (RAW format), use destination pool's default format
1928+
* CLVM uses RAW format which may not match destination pool's format (e.g., NFS uses QCOW2)
1929+
* This specifically handles:
1930+
* - CLVM (RAW) -> NFS/Local/CLVM_NG (QCOW2)
1931+
* - NFS/Local/CLVM_NG (QCOW2) -> CLVM (RAW)
1932+
* @param migrationOptions
1933+
* @param primaryStore
1934+
* @return
1935+
*/
1936+
private boolean useDestPoolFormat(MigrationOptions migrationOptions, PrimaryDataStoreTO primaryStore) {
1937+
boolean useDstPoolFormat = false;
1938+
if (migrationOptions != null && migrationOptions.getSrcPoolType() != null) {
1939+
StoragePoolType srcPoolType = migrationOptions.getSrcPoolType();
1940+
StoragePoolType dstPoolType = primaryStore.getPoolType();
1941+
1942+
if (srcPoolType != dstPoolType) {
1943+
useDstPoolFormat = (srcPoolType == StoragePoolType.CLVM || dstPoolType == StoragePoolType.CLVM);
1944+
}
1945+
}
1946+
return useDstPoolFormat;
1947+
}
1948+
19171949
/**
19181950
* XML to take disk-only snapshot of the VM.<br><br>
19191951
* 1st parameter: snapshot's name;<br>

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,6 +2609,7 @@ private KVMPhysicalDisk createClvmNgDiskWithBacking(String volumeUuid, int timeo
26092609
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
26102610
lvcreate.add("-n", volumeUuid);
26112611
lvcreate.add("-L", lvSize + "B");
2612+
lvcreate.add("--yes");
26122613
lvcreate.add(vgName);
26132614

26142615
String result = lvcreate.execute();
@@ -2686,6 +2687,7 @@ public void createTemplateOnClvmNg(String templatePath, String templateUuid, int
26862687
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
26872688
lvcreate.add("-n", lvName);
26882689
lvcreate.add("-L", lvSize + "B");
2690+
lvcreate.add("--yes");
26892691
lvcreate.add(vgName);
26902692
String result = lvcreate.execute();
26912693
if (result != null) {
@@ -2757,6 +2759,7 @@ private KVMPhysicalDisk createClvmVolume(String volumeName, long size, KVMStorag
27572759
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
27582760
lvcreate.add("-n", volumeName);
27592761
lvcreate.add("-L", size + "B");
2762+
lvcreate.add("--yes");
27602763
lvcreate.add(vgName);
27612764

27622765
String result = lvcreate.execute();

0 commit comments

Comments
 (0)