Pearl1594 commented on code in PR #12617:
URL: https://github.com/apache/cloudstack/pull/12617#discussion_r3162734534
##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java:
##########
@@ -693,11 +848,248 @@ public KVMPhysicalDisk getPhysicalDisk(String
volumeUuid, KVMStoragePool pool) {
}
return disk;
} catch (LibvirtException e) {
- logger.debug("Failed to get physical disk:", e);
+ logger.debug("Failed to get volume from libvirt: " +
e.getMessage());
+ if (isClvmPool) {
+ return getPhysicalDiskWithClvmFallback(volumeUuid, pool,
libvirtPool);
+ }
+
throw new CloudRuntimeException(e.toString());
}
}
+ /**
+ * CLVM fallback: First tries to refresh libvirt pool to make volume
visible,
+ * if that fails, accesses volume directly via block device path.
+ */
+ private KVMPhysicalDisk getPhysicalDiskWithClvmFallback(String volumeUuid,
KVMStoragePool pool, LibvirtStoragePool libvirtPool) {
+ logger.info("CLVM volume not visible to libvirt, attempting pool
refresh for volume: {}", volumeUuid);
+
+ try {
+ logger.debug("Refreshing libvirt storage pool: {}",
pool.getUuid());
+ libvirtPool.getPool().refresh(0);
+
+ StorageVol vol = getVolume(libvirtPool.getPool(), volumeUuid);
+ if (vol != null) {
+ logger.info("Volume found after pool refresh: {}", volumeUuid);
+ KVMPhysicalDisk disk;
+ LibvirtStorageVolumeDef voldef =
getStorageVolumeDef(libvirtPool.getPool().getConnect(), vol);
+ disk = new KVMPhysicalDisk(vol.getPath(), vol.getName(), pool);
+ disk.setSize(vol.getInfo().allocation);
+ disk.setVirtualSize(vol.getInfo().capacity);
+ disk.setFormat(voldef.getFormat() ==
LibvirtStorageVolumeDef.VolumeFormat.QCOW2 ?
+ PhysicalDiskFormat.QCOW2 : PhysicalDiskFormat.RAW);
+ return disk;
+ }
+ } catch (LibvirtException refreshEx) {
+ logger.debug("Pool refresh failed or volume still not found: {}",
refreshEx.getMessage());
+ }
+
+ logger.info("Falling back to direct block device access for volume:
{}", volumeUuid);
+ return getPhysicalDiskViaDirectBlockDevice(volumeUuid, pool);
+ }
+
+ private String getVgName(String sourceDir) {
+ String vgName = sourceDir;
+ if (vgName.startsWith("/")) {
+ String[] parts = vgName.split("/");
+ List<String> tokens = Arrays.stream(parts)
+ .filter(s -> !s.isEmpty()).collect(Collectors.toList());
+
+ vgName = tokens.size() > 1 ? tokens.get(1)
+ : tokens.size() == 1 ? tokens.get(0)
+ : "";
+ }
+ return vgName;
+ }
+
+ /**
+ * For CLVM volumes that exist in LVM but are not visible to libvirt,
+ * access them directly via block device path.
+ */
+ private KVMPhysicalDisk getPhysicalDiskViaDirectBlockDevice(String
volumeUuid, KVMStoragePool pool) {
+ try {
+ String vgName = extractVgNameFromPool(pool);
+ verifyLvExistsInVg(volumeUuid, vgName);
+
+ logger.info("Volume {} exists in LVM but not visible to libvirt,
accessing directly", volumeUuid);
+
+ String lvPath = findAccessibleDeviceNode(volumeUuid, vgName, pool);
+ long size = getClvmVolumeSize(lvPath);
+
+ KVMPhysicalDisk disk = createPhysicalDiskFromClvmLv(lvPath,
volumeUuid, pool, size);
+
+ ensureTemplateAccessibility(volumeUuid, lvPath, pool);
+
+ return disk;
+
+ } catch (CloudRuntimeException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ logger.error("Failed to access CLVM volume via direct block
device: {}", volumeUuid, ex);
+ throw new CloudRuntimeException(String.format("Could not find
volume %s: %s ", volumeUuid, ex.getMessage()));
+ }
+ }
+
+ private String extractVgNameFromPool(KVMStoragePool pool) {
+ String sourceDir = pool.getLocalPath();
+ if (sourceDir == null || sourceDir.isEmpty()) {
+ throw new CloudRuntimeException("CLVM pool sourceDir is not set,
cannot determine VG name");
+ }
+ String vgName = getVgName(sourceDir);
+ logger.debug("Using VG name: {} (from sourceDir: {})", vgName,
sourceDir);
+ return vgName;
+ }
+
+ private void verifyLvExistsInVg(String volumeUuid, String vgName) {
+ logger.debug("Checking if volume {} exists in VG {}", volumeUuid,
vgName);
+ Script checkLvCmd = new Script("/usr/sbin/lvs", 5000, logger);
+ checkLvCmd.add("--noheadings");
+ checkLvCmd.add("--unbuffered");
+ checkLvCmd.add(vgName + "/" + volumeUuid);
+
+ String checkResult = checkLvCmd.execute();
+ if (checkResult != null) {
+ logger.debug("Volume {} does not exist in VG {}: {}", volumeUuid,
vgName, checkResult);
+ throw new CloudRuntimeException(String.format("Storage volume not
found: no storage vol with matching name '%s'", volumeUuid));
+ }
+ }
+
+ private String findAccessibleDeviceNode(String volumeUuid, String vgName,
KVMStoragePool pool) {
+ String lvPath = "/dev/" + vgName + "/" + volumeUuid;
+ File lvDevice = new File(lvPath);
+
+ if (!lvDevice.exists()) {
+ lvPath = tryDeviceMapperPath(volumeUuid, vgName, lvDevice);
+
+ if (!lvDevice.exists()) {
+ lvPath = handleMissingDeviceNode(volumeUuid, vgName, pool);
+ }
+ }
+
+ return lvPath;
+ }
+
+ private String tryDeviceMapperPath(String volumeUuid, String vgName, File
lvDevice) {
+ String vgNameEscaped = vgName.replace("-", "--");
+ String volumeUuidEscaped = volumeUuid.replace("-", "--");
+ String mapperPath = "/dev/mapper/" + vgNameEscaped + "-" +
volumeUuidEscaped;
+ File mapperDevice = new File(mapperPath);
+
+ if (!mapperDevice.exists()) {
+ lvDevice = mapperDevice;
+ }
+
+ return mapperPath;
+ }
+
+ private String handleMissingDeviceNode(String volumeUuid, String vgName,
KVMStoragePool pool) {
+ if (pool.getType() == StoragePoolType.CLVM_NG &&
volumeUuid.startsWith("template-")) {
+ return activateTemplateAndGetPath(volumeUuid, vgName);
+ } else {
+ logger.warn("Volume exists in LVM but device node not found: {}",
volumeUuid);
+ throw new CloudRuntimeException(String.format("Could not find
volume %s " +
+ "in VG %s - volume exists in LVM but device node not
accessible", volumeUuid, vgName));
+ }
+ }
+
+ private String activateTemplateAndGetPath(String volumeUuid, String
vgName) {
+ logger.info("Template volume {} device node not found. Attempting to
activate in shared mode.", volumeUuid);
+ String templateLvPath = "/dev/" + vgName + "/" + volumeUuid;
+
+ try {
+ ensureTemplateLvInSharedMode(templateLvPath, false);
+
+ String lvPath = findDeviceNodeAfterActivation(templateLvPath,
volumeUuid, vgName);
+
+ logger.info("Successfully activated template volume {} at {}",
volumeUuid, lvPath);
+ return lvPath;
+
+ } catch (CloudRuntimeException e) {
+ throw new CloudRuntimeException(String.format("Failed to activate
template volume %s " +
+ "in VG %s: %s", volumeUuid, vgName, e.getMessage()), e);
+ }
+ }
+
+ private String findDeviceNodeAfterActivation(String templateLvPath, String
volumeUuid, String vgName) {
+ File lvDevice = new File(templateLvPath);
+ String lvPath = templateLvPath;
+
+ if (!lvDevice.exists()) {
+ String vgNameEscaped = vgName.replace("-", "--");
+ String volumeUuidEscaped = volumeUuid.replace("-", "--");
+ lvPath = "/dev/mapper/" + vgNameEscaped + "-" + volumeUuidEscaped;
+ lvDevice = new File(lvPath);
+ }
+
+ if (!lvDevice.exists()) {
+ logger.error("Template volume {} still not accessible after
activation attempt", volumeUuid);
+ throw new CloudRuntimeException(String.format("Could not activate
template volume %s " +
+ "in VG %s - device node not accessible even after
activation", volumeUuid, vgName));
+ }
+
+ return lvPath;
+ }
+
+ private long getClvmVolumeSize(String lvPath) {
+ try {
+ Script lvsCmd = new Script("/usr/sbin/lvs", 5000, logger);
+ lvsCmd.add("--noheadings");
+ lvsCmd.add("--units");
+ lvsCmd.add("b");
+ lvsCmd.add("-o");
+ lvsCmd.add("lv_size");
+ lvsCmd.add(lvPath);
+
+ OutputInterpreter.AllLinesParser parser = new
OutputInterpreter.AllLinesParser();
+ String result = lvsCmd.execute(parser);
+
+ String output = (result == null) ? parser.getLines() : result;
+
+ if (output != null && !output.isEmpty()) {
+ String sizeStr = output.trim().replaceAll("[^0-9]", "");
+ if (!sizeStr.isEmpty()) {
+ return Long.parseLong(sizeStr);
+ }
+ }
+ } catch (Exception sizeEx) {
+ logger.warn("Failed to get size for CLVM volume via lvs: {}",
sizeEx.getMessage());
+ File lvDevice = new File(lvPath);
+ if (lvDevice.isFile()) {
+ return lvDevice.length();
+ }
+ }
+ return 0;
+ }
+
+ private KVMPhysicalDisk createPhysicalDiskFromClvmLv(String lvPath, String
volumeUuid,
+ KVMStoragePool pool,
long size) {
+ KVMPhysicalDisk disk = new KVMPhysicalDisk(lvPath, volumeUuid, pool);
+
+ PhysicalDiskFormat diskFormat = (pool.getType() ==
StoragePoolType.CLVM_NG)
+ ? PhysicalDiskFormat.QCOW2
+ : PhysicalDiskFormat.RAW;
+
+ logger.debug("{} pool detected, setting disk format to {} for volume
{}",
+ pool.getType(), diskFormat, volumeUuid);
+
+ disk.setFormat(diskFormat);
+ disk.setSize(size);
+ disk.setVirtualSize(size);
+
+ logger.info("Successfully accessed CLVM/CLVM_NG volume via direct
block device: {} " +
+ "with format: {} and size: {} bytes", lvPath, diskFormat,
size);
+
+ return disk;
+ }
+
+ private void ensureTemplateAccessibility(String volumeUuid, String lvPath,
KVMStoragePool pool) {
+ if (pool.getType() == StoragePoolType.CLVM_NG &&
volumeUuid.startsWith("template-")) {
+ logger.info("Detected template volume {}. Ensuring it's activated
in shared mode for backing file access.",
+ volumeUuid);
+ ensureTemplateLvInSharedMode(lvPath, false);
+ }
+ }
+
Review Comment:
Moved to ClvmStorageAdaptor class
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]