add support to migrate to and from clvm to nfs

This commit is contained in:
Pearl Dsilva 2026-03-19 16:09:20 -04:00
parent c0cf89584b
commit 01aa259eff
3 changed files with 62 additions and 72 deletions

View File

@ -33,7 +33,6 @@ import javax.inject.Inject;
import com.cloud.dc.DedicatedResourceVO;
import com.cloud.dc.dao.DedicatedResourceDao;
import com.cloud.storage.ClvmLockManager;
import com.cloud.storage.Storage;
import com.cloud.storage.VolumeDetailVO;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.user.Account;
@ -423,9 +422,7 @@ public class DefaultEndPointSelector implements EndPointSelector {
// Check if this is a CLVM pool
StoragePoolVO pool = _storagePoolDao.findById(store.getId());
if (pool == null ||
(pool.getPoolType() != Storage.StoragePoolType.CLVM ||
pool.getPoolType() != Storage.StoragePoolType.CLVM_NG)) {
if (pool == null || !ClvmLockManager.isClvmPoolType(pool.getPoolType())) {
return null;
}

View File

@ -827,7 +827,13 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
for (int z = 0; z < diskChildNodes.getLength(); z++) {
Node diskChildNode = diskChildNodes.item(z);
if (migrateStorageManaged && "driver".equals(diskChildNode.getNodeName())) {
// 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);
if (shouldUpdateDriverType && "driver".equals(diskChildNode.getNodeName())) {
Node driverNode = diskChildNode;
NamedNodeMap driverNodeAttributes = driverNode.getAttributes();

View File

@ -1407,6 +1407,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
createPhysicalDiskByQemuImg(name, pool, PhysicalDiskFormat.RAW, provisioningType, size, passphrase);
} else if (StoragePoolType.CLVM_NG.equals(poolType)) {
return createClvmNgDiskWithBacking(name, 0, size, null, pool, provisioningType);
} else if (StoragePoolType.CLVM.equals(poolType)) {
return createClvmVolume(name, size, pool);
} else if (StoragePoolType.NetworkFilesystem.equals(poolType) || StoragePoolType.Filesystem.equals(poolType)) {
switch (format) {
case QCOW2:
@ -2207,75 +2209,23 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
disk.getFormat() : PhysicalDiskFormat.RAW;
String sourcePath = disk.getPath();
boolean isSourceClvm = srcPool.getType() == StoragePoolType.CLVM || srcPool.getType() == StoragePoolType.CLVM_NG;
boolean isDestClvm = destPool.getType() == StoragePoolType.CLVM || destPool.getType() == StoragePoolType.CLVM_NG;
String clvmLockVolume = null;
boolean shouldDeactivateSource = false;
try {
if (isSourceClvm) {
logger.info("Activating source CLVM volume {} in shared mode for copy", sourcePath);
LibvirtComputingResource.setClvmVolumeToSharedMode(sourcePath);
shouldDeactivateSource = !isDestClvm;
}
KVMPhysicalDisk newDisk;
logger.debug("copyPhysicalDisk: disk size:{}, virtualsize:{} format:{}", toHumanReadableSize(disk.getSize()), toHumanReadableSize(disk.getVirtualSize()), disk.getFormat());
if (destPool.getType() != StoragePoolType.RBD) {
if (disk.getFormat() == PhysicalDiskFormat.TAR) {
newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, Storage.ProvisioningType.THIN, disk.getVirtualSize(), null);
} else {
newDisk = destPool.createPhysicalDisk(name, Storage.ProvisioningType.THIN, disk.getVirtualSize(), null);
}
KVMPhysicalDisk newDisk;
logger.debug("copyPhysicalDisk: disk size:{}, virtualsize:{} format:{}", toHumanReadableSize(disk.getSize()), toHumanReadableSize(disk.getVirtualSize()), disk.getFormat());
if (destPool.getType() != StoragePoolType.RBD) {
if (disk.getFormat() == PhysicalDiskFormat.TAR) {
newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, Storage.ProvisioningType.THIN, disk.getVirtualSize(), null);
} else {
newDisk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + name, name, destPool);
newDisk.setFormat(PhysicalDiskFormat.RAW);
newDisk.setSize(disk.getVirtualSize());
newDisk.setVirtualSize(disk.getSize());
}
String destPath = newDisk.getPath();
PhysicalDiskFormat destFormat = newDisk.getFormat();
if (isDestClvm) {
logger.info("Activating destination CLVM volume {} in shared mode for copy", destPath);
LibvirtComputingResource.setClvmVolumeToSharedMode(destPath);
clvmLockVolume = destPath;
}
boolean formatConversion = sourceFormat != destFormat;
if (formatConversion) {
logger.info("Format conversion required: {} -> {}", sourceFormat, destFormat);
}
return performCopy(disk, name, destPool, timeout, srcPool, sourceFormat,
sourcePath, newDisk, destPath, destFormat, formatConversion);
} finally {
if (isSourceClvm && shouldDeactivateSource) {
try {
logger.info("Deactivating source CLVM volume {} after copy", sourcePath);
LibvirtComputingResource.deactivateClvmVolume(sourcePath);
} catch (Exception e) {
logger.warn("Failed to deactivate source CLVM volume {}: {}", sourcePath, e.getMessage());
}
}
if (isDestClvm && clvmLockVolume != null) {
try {
logger.info("Claiming exclusive lock on destination CLVM volume {} after copy", clvmLockVolume);
LibvirtComputingResource.activateClvmVolumeExclusive(clvmLockVolume);
} catch (Exception e) {
logger.warn("Failed to claim exclusive lock on destination CLVM volume {}: {}", clvmLockVolume, e.getMessage());
}
newDisk = destPool.createPhysicalDisk(name, Storage.ProvisioningType.THIN, disk.getVirtualSize(), null);
}
} else {
newDisk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + name, name, destPool);
newDisk.setFormat(PhysicalDiskFormat.RAW);
newDisk.setSize(disk.getVirtualSize());
newDisk.setVirtualSize(disk.getSize());
}
}
private KVMPhysicalDisk performCopy(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout,
KVMStoragePool srcPool, PhysicalDiskFormat sourceFormat, String sourcePath,
KVMPhysicalDisk newDisk, String destPath, PhysicalDiskFormat destFormat,
boolean formatConversion) {
String destPath = newDisk.getPath();
PhysicalDiskFormat destFormat = newDisk.getFormat();
QemuImg qemu;
@ -2376,8 +2326,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
}
} else {
/**
We let Qemu-Img do the work here. Although we could work with librbd and have that do the cloning
it doesn't benefit us. It's better to keep the current code in place which works
We let Qemu-Img do the work here. Although we could work with librbd and have that do the cloning
it doesn't benefit us. It's better to keep the current code in place which works
*/
srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool, sourcePath));
srcFile.setFormat(sourceFormat);
@ -2786,4 +2736,41 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
lvremove.add(lvPath);
lvremove.execute();
}
/**
* Creates a raw LV volume for CLVM pools using direct LVM commands.
* This bypasses libvirt since CLVM pools are kept inactive in libvirt.
*
* @param volumeName the name of the volume to create
* @param size the size of the volume in bytes
* @param pool the CLVM storage pool
* @return the created KVMPhysicalDisk
*/
private KVMPhysicalDisk createClvmVolume(String volumeName, long size, KVMStoragePool pool) {
String vgName = getVgName(pool.getLocalPath());
String volumePath = "/dev/" + vgName + "/" + volumeName;
int timeout = 30000; // 30 seconds timeout for LV creation
logger.info("Creating CLVM volume {} in VG {} with size {} bytes", volumeName, vgName, size);
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
lvcreate.add("-n", volumeName);
lvcreate.add("-L", size + "B");
lvcreate.add(vgName);
String result = lvcreate.execute();
if (result != null) {
throw new CloudRuntimeException("Failed to create CLVM volume: " + result);
}
logger.info("Successfully created CLVM volume {} at {} with size {}", volumeName, volumePath, toHumanReadableSize(size));
long actualSize = getClvmVolumeSize(volumePath);
KVMPhysicalDisk disk = new KVMPhysicalDisk(volumePath, volumeName, pool);
disk.setFormat(PhysicalDiskFormat.RAW);
disk.setSize(actualSize);
disk.setVirtualSize(actualSize);
return disk;
}
}