sureshanaparti commented on code in PR #12617:
URL: https://github.com/apache/cloudstack/pull/12617#discussion_r3154477766


##########
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:
   can have separate ClvmStorageAdaptor with all these func ^^^ for 
CLVM/CLVM_NG extending this 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]

Reply via email to