Skip to content

Commit 22f9d0e

Browse files
committed
fix clvm_ng to nfs migration, and handle overhead calc
1 parent 798401e commit 22f9d0e

File tree

3 files changed

+72
-57
lines changed

3 files changed

+72
-57
lines changed

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ private void updateClvmLockHostAfterMigration(Volume migratedVolume, StoragePool
818818
}
819819

820820
StoragePoolVO pool = _storagePoolDao.findById(destPool.getId());
821-
if (pool == null || pool.getPoolType() != Storage.StoragePoolType.CLVM || pool.getPoolType() != Storage.StoragePoolType.CLVM_NG) {
821+
if (pool == null || !ClvmLockManager.isClvmPoolType(pool.getPoolType())) {
822822
return;
823823
}
824824

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,11 +2785,13 @@ public Answer deleteVolume(final DeleteCommand cmd) {
27852785
final PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getDataStore();
27862786
try {
27872787
final KVMStoragePool pool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid());
2788-
try {
2789-
pool.getPhysicalDisk(vol.getPath());
2790-
} catch (final Exception e) {
2791-
logger.debug(String.format("can't find volume: %s, return true", vol));
2792-
return new Answer(null);
2788+
if (pool.getType() != StoragePoolType.CLVM && pool.getType() != StoragePoolType.CLVM_NG) {
2789+
try {
2790+
pool.getPhysicalDisk(vol.getPath());
2791+
} catch (final Exception e) {
2792+
logger.debug(String.format("can't find volume: %s, return true", vol));
2793+
return new Answer(null);
2794+
}
27932795
}
27942796
pool.deletePhysicalDisk(vol.getPath(), vol.getFormat());
27952797
return new Answer(null);

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

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool,
14061406
return (dataPool == null) ? createPhysicalDiskByLibVirt(name, pool, PhysicalDiskFormat.RAW, provisioningType, size) :
14071407
createPhysicalDiskByQemuImg(name, pool, PhysicalDiskFormat.RAW, provisioningType, size, passphrase);
14081408
} else if (StoragePoolType.CLVM_NG.equals(poolType)) {
1409-
return createClvmNgDiskWithBacking(name, 0, size, null, pool);
1409+
return createClvmNgDiskWithBacking(name, 0, size, null, pool, provisioningType);
14101410
} else if (StoragePoolType.NetworkFilesystem.equals(poolType) || StoragePoolType.Filesystem.equals(poolType)) {
14111411
switch (format) {
14121412
case QCOW2:
@@ -1578,6 +1578,8 @@ private void ensureClvmNgBackingFileAccessible(String volumeName, KVMStoragePool
15781578
}
15791579
}
15801580
}
1581+
} else {
1582+
logger.debug("Volume {} does not have a backing file (full clone)", volumePath);
15811583
}
15821584
} else {
15831585
logger.debug("Volume {} does not have a backing file (full clone)", volumePath);
@@ -1828,7 +1830,7 @@ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template,
18281830
logger.info("Creating CLVM_NG volume {} with backing file from template {}", newUuid, template.getName());
18291831
String backingFile = getClvmBackingFile(template, destPool);
18301832

1831-
disk = createClvmNgDiskWithBacking(newUuid, timeout, size, backingFile, destPool);
1833+
disk = createClvmNgDiskWithBacking(newUuid, timeout, size, backingFile, destPool, provisioningType);
18321834
return disk;
18331835
}
18341836
List<QemuObject> passphraseObjects = new ArrayList<>();
@@ -2060,9 +2062,7 @@ private KVMPhysicalDisk createDiskFromTemplateOnRBD(KVMPhysicalDisk template,
20602062
rbd.close(destImage);
20612063
} else {
20622064
logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName()
2063-
+ " is RBD format 2. We will perform a RBD clone using snapshot "
2064-
+ rbdTemplateSnapName);
2065-
/* The source image is format 2, we can do a RBD snapshot+clone (layering) */
2065+
+ " is RBD format 2. We will perform a RBD snapshot+clone (layering)");
20662066

20672067

20682068
logger.debug("Checking if RBD snapshot " + srcPool.getSourceDir() + "/" + template.getName()
@@ -2553,51 +2553,54 @@ private long getVgPhysicalExtentSize(String vgName) {
25532553
}
25542554

25552555
/**
2556-
* Calculate LVM LV size for CLVM_NG template allocation.
2557-
* Templates need space for actual data plus overhead for QCOW2 metadata expansion during conversion.
2558-
* Formula: physical_size + 50% of virtual_size
2556+
* Calculate LVM LV size for CLVM_NG volume allocation.
2557+
* Volumes use QCOW2-on-LVM with extended_l2=on and need:
2558+
* - Base size (virtual size)
2559+
* - QCOW2 metadata overhead (L1/L2 tables, refcount tables, headers)
2560+
*
2561+
* For QCOW2 with 64k clusters and extended L2 tables (extended_l2=on):
2562+
* - Each 64KB cluster contains data
2563+
* - Each L2 table entry is 16 bytes (extended L2, double the standard 8 bytes)
2564+
* - Each 64KB L2 cluster can hold 4096 entries (64KB / 16 bytes)
2565+
* Formula: Total overhead (MiB) = ((virtualSize_GiB × 1024 × 1024) / (64 × 4096)) × 2 + 2 MiB headers
25592566
*
2560-
* @param physicalSize Physical size in bytes (actual allocated data in source template)
2561-
* @param virtualSize Virtual size in bytes (full disk capacity)
2567+
* Quick reference (64k clusters, extended_l2=on):
2568+
* 10 GiB virtual → ~7 MiB overhead → 2 PEs (8 MiB)
2569+
* 100 GiB virtual → ~52 MiB overhead → 13 PEs (52 MiB)
2570+
* 1 TiB virtual → ~514 MiB overhead → 129 PEs (516 MiB)
2571+
* 2 TiB virtual → ~1026 MiB overhead → 257 PEs (1028 MiB)
2572+
*
2573+
* @param virtualSize Virtual disk size in bytes (for overhead calculation)
25622574
* @param vgName Volume group name to query PE size
2563-
* @return Size in bytes to allocate for template LV
2575+
* @return Size in bytes to allocate for LV
25642576
*/
2565-
private long calculateClvmNgTemplateLvSize(long physicalSize, long virtualSize, String vgName) {
2577+
private long calculateClvmNgLvSize(long virtualSize, String vgName) {
25662578
long peSize = getVgPhysicalExtentSize(vgName);
25672579

2568-
long minOverhead = 64 * 1024 * 1024L;
2569-
long virtualSizeOverhead = (long) (virtualSize * 0.30);
2570-
long overhead = Math.max(minOverhead, virtualSizeOverhead);
2580+
long clusterSize = 64 * 1024L;
2581+
// Each L2 entry is 16 bytes (extended_l2=on), and each 64KB cluster holds 4096 entries (64KB / 16 bytes)
2582+
long l2Multiplier = 4096L;
2583+
2584+
long numDataClusters = (virtualSize + clusterSize - 1) / clusterSize;
2585+
long numL2Clusters = (numDataClusters + l2Multiplier - 1) / l2Multiplier;
2586+
long l2TableSize = numL2Clusters * clusterSize;
2587+
long refcountTableSize = l2TableSize;
25712588

2572-
long targetSize = physicalSize + overhead;
2589+
// Headers and other metadata (L1 table, QCOW2 header, etc.)
2590+
long headerOverhead = 2 * 1024 * 1024L; // 2 MiB for headers
2591+
long metadataOverhead = l2TableSize + refcountTableSize + headerOverhead;
2592+
long targetSize = virtualSize + metadataOverhead;
25732593
long roundedSize = ((targetSize + peSize - 1) / peSize) * peSize;
2594+
long virtualSizeGiB = virtualSize / (1024 * 1024 * 1024L);
2595+
long overheadMiB = metadataOverhead / (1024 * 1024L);
25742596

2575-
logger.info("Calculated template LV size: {} bytes (physical: {}, virtual: {}, overhead: {} (50% of virtual), rounded to {} PEs, PE size = {} bytes)",
2576-
roundedSize, physicalSize, virtualSize, overhead, roundedSize / peSize, peSize);
2597+
logger.info("Calculated volume LV size: {} bytes (virtual: {} GiB, " +
2598+
"QCOW2 metadata overhead: {} MiB (64k clusters, extended_l2=on), rounded to {} PEs, PE size = {} bytes)",
2599+
roundedSize, virtualSizeGiB, overheadMiB, roundedSize / peSize, peSize);
25772600

25782601
return roundedSize;
25792602
}
25802603

2581-
/**
2582-
* Calculate LVM LV size for CLVM_NG volume allocation.
2583-
* Volumes with backing files need approximately the virtual size allocated on block devices.
2584-
* Formula: virtual_size + 1 PE (for QCOW2 metadata and header)
2585-
*
2586-
* @param virtualSize Virtual size in bytes (full disk capacity)
2587-
* @param vgName Volume group name to query PE size
2588-
* @return Size in bytes to allocate for volume LV
2589-
*/
2590-
private long calculateClvmNgVolumeLvSize(long virtualSize, String vgName) {
2591-
long peSize = getVgPhysicalExtentSize(vgName);
2592-
2593-
long roundedSize = ((virtualSize + peSize - 1) / peSize) * peSize;
2594-
long finalSize = roundedSize + peSize;
2595-
2596-
logger.info("Calculated volume LV size: {} bytes (virtual: {}, rounded to {} PEs + 1 PE overhead, PE size = {} bytes)",
2597-
finalSize, virtualSize, roundedSize / peSize, peSize);
2598-
2599-
return finalSize;
2600-
}
26012604

26022605
private long getQcow2VirtualSize(String imagePath) {
26032606
Script qemuImg = new Script("qemu-img", 300000, logger);
@@ -2623,7 +2626,7 @@ private long getQcow2VirtualSize(String imagePath) {
26232626

26242627

26252628
private long getQcow2PhysicalSize(String imagePath) {
2626-
Script qemuImg = new Script("qemu-img", 300000, logger);
2629+
Script qemuImg = new Script("qemu-img", Duration.millis(300000), logger);
26272630
qemuImg.add("info");
26282631
qemuImg.add("--output=json");
26292632
qemuImg.add(imagePath);
@@ -2644,12 +2647,14 @@ private long getQcow2PhysicalSize(String imagePath) {
26442647
return info.get("actual-size").getAsLong();
26452648
}
26462649

2647-
private KVMPhysicalDisk createClvmNgDiskWithBacking(String volumeUuid, int timeout, long virtualSize, String backingFile, KVMStoragePool pool) {
2650+
private KVMPhysicalDisk createClvmNgDiskWithBacking(String volumeUuid, int timeout, long virtualSize, String backingFile,
2651+
KVMStoragePool pool, Storage.ProvisioningType provisioningType) {
26482652
String vgName = getVgName(pool.getLocalPath());
2649-
long lvSize = calculateClvmNgVolumeLvSize(virtualSize, vgName);
2653+
long lvSize = calculateClvmNgLvSize(virtualSize, vgName);
26502654
String volumePath = "/dev/" + vgName + "/" + volumeUuid;
26512655

2652-
logger.debug("Creating CLVM_NG volume {} with LV size {} bytes (virtual size: {} bytes)", volumeUuid, lvSize, virtualSize);
2656+
logger.debug("Creating CLVM_NG volume {} with LV size {} bytes (virtual size: {} bytes, provisioning: {})",
2657+
volumeUuid, lvSize, virtualSize, provisioningType);
26532658

26542659
Script lvcreate = new Script("lvcreate", Duration.millis(timeout), logger);
26552660
lvcreate.add("-n", volumeUuid);
@@ -2666,9 +2671,20 @@ private KVMPhysicalDisk createClvmNgDiskWithBacking(String volumeUuid, int timeo
26662671
qemuImg.add("-f", "qcow2");
26672672

26682673
StringBuilder qcow2Options = new StringBuilder();
2669-
qcow2Options.append("preallocation=metadata");
2674+
2675+
// Set preallocation based on provisioning type
2676+
// THIN: preallocation=off (sparse file, allocate on write)
2677+
// SPARSE / FAT: preallocation=metadata (allocate metadata only)
2678+
String preallocation;
2679+
if (provisioningType == Storage.ProvisioningType.THIN) {
2680+
preallocation = "off";
2681+
} else {
2682+
preallocation = "metadata";
2683+
}
2684+
2685+
qcow2Options.append("preallocation=").append(preallocation);
26702686
qcow2Options.append(",extended_l2=on");
2671-
qcow2Options.append(",cluster_size=128k");
2687+
qcow2Options.append(",cluster_size=64k");
26722688

26732689
if (backingFile != null && !backingFile.isEmpty()) {
26742690
qcow2Options.append(",backing_file=").append(backingFile);
@@ -2691,8 +2707,8 @@ private KVMPhysicalDisk createClvmNgDiskWithBacking(String volumeUuid, int timeo
26912707
disk.setSize(lvSize);
26922708
disk.setVirtualSize(virtualSize);
26932709

2694-
logger.info("Successfully created CLVM_NG volume {} with backing file (LV size: {}, virtual size: {})",
2695-
volumeUuid, lvSize, virtualSize);
2710+
logger.info("Successfully created CLVM_NG volume {} with backing file (LV size: {}, virtual size: {}, provisioning: {}, preallocation: {})",
2711+
volumeUuid, lvSize, virtualSize, provisioningType, preallocation);
26962712

26972713
return disk;
26982714
}
@@ -2711,7 +2727,7 @@ public void createTemplateOnClvmNg(String templatePath, String templateUuid, int
27112727

27122728
long virtualSize = getQcow2VirtualSize(templatePath);
27132729
long physicalSize = getQcow2PhysicalSize(templatePath);
2714-
long lvSize = calculateClvmNgTemplateLvSize(physicalSize, virtualSize, vgName);
2730+
long lvSize = virtualSize; // as extended_l2=off and preallocation=off
27152731

27162732
logger.info("Template source - Physical: {} bytes, Virtual: {} bytes, LV will be: {} bytes",
27172733
physicalSize, virtualSize, lvSize);
@@ -2724,7 +2740,6 @@ public void createTemplateOnClvmNg(String templatePath, String templateUuid, int
27242740
if (result != null) {
27252741
throw new CloudRuntimeException("Failed to create LV for CLVM_NG template: " + result);
27262742
}
2727-
27282743
Script qemuImgConvert = new Script("qemu-img", Duration.millis(timeout), logger);
27292744
qemuImgConvert.add("convert");
27302745
qemuImgConvert.add(templatePath);
@@ -2739,8 +2754,7 @@ public void createTemplateOnClvmNg(String templatePath, String templateUuid, int
27392754
}
27402755

27412756
long actualVirtualSize = getQcow2VirtualSize(lvPath);
2742-
logger.info("Created template LV {} with size {} bytes (source physical: {}, actual virtual: {}, overhead: {})",
2743-
lvName, lvSize, physicalSize, actualVirtualSize, lvSize - physicalSize);
2757+
logger.info("Created template LV {} with size {} bytes (source physical: {}, actual virtual: {})", lvName, lvSize, physicalSize, actualVirtualSize);
27442758

27452759
try {
27462760
ensureTemplateLvInSharedMode(lvPath, true);
@@ -2754,7 +2768,6 @@ public void createTemplateOnClvmNg(String templatePath, String templateUuid, int
27542768
templateDisk.setFormat(PhysicalDiskFormat.QCOW2);
27552769
templateDisk.setVirtualSize(actualVirtualSize);
27562770
templateDisk.setSize(lvSize);
2757-
templateDisk.setSize(lvSize);
27582771

27592772
}
27602773

0 commit comments

Comments
 (0)