|
34 | 34 |
|
35 | 35 | import com.cloud.agent.properties.AgentProperties; |
36 | 36 | import com.cloud.agent.properties.AgentPropertiesFileHandler; |
| 37 | +import com.cloud.utils.script.OutputInterpreter; |
37 | 38 | import org.apache.cloudstack.api.ApiConstants; |
38 | 39 | import org.apache.cloudstack.utils.cryptsetup.KeyFile; |
39 | 40 | import org.apache.cloudstack.utils.qemu.QemuImageOptions; |
@@ -254,9 +255,12 @@ public StorageVol getVolume(StoragePool pool, String volName) { |
254 | 255 |
|
255 | 256 | try { |
256 | 257 | vol = pool.storageVolLookupByName(volName); |
257 | | - logger.debug("Found volume " + volName + " in storage pool " + pool.getName() + " after refreshing the pool"); |
| 258 | + if (vol != null) { |
| 259 | + logger.debug("Found volume " + volName + " in storage pool " + pool.getName() + " after refreshing the pool"); |
| 260 | + } |
258 | 261 | } catch (LibvirtException e) { |
259 | | - throw new CloudRuntimeException("Could not find volume " + volName + ": " + e.getMessage()); |
| 262 | + logger.debug("Volume " + volName + " still not found after pool refresh: " + e.getMessage()); |
| 263 | + return null; |
260 | 264 | } |
261 | 265 | } |
262 | 266 |
|
@@ -663,6 +667,17 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) { |
663 | 667 |
|
664 | 668 | try { |
665 | 669 | StorageVol vol = getVolume(libvirtPool.getPool(), volumeUuid); |
| 670 | + |
| 671 | + // Check if volume was found - if null, treat as not found and trigger fallback for CLVM |
| 672 | + if (vol == null) { |
| 673 | + logger.debug("Volume " + volumeUuid + " not found in libvirt, will check for CLVM fallback"); |
| 674 | + if (pool.getType() == StoragePoolType.CLVM) { |
| 675 | + return getPhysicalDisk(volumeUuid, pool, libvirtPool); |
| 676 | + } |
| 677 | + |
| 678 | + throw new CloudRuntimeException("Volume " + volumeUuid + " not found in libvirt pool"); |
| 679 | + } |
| 680 | + |
666 | 681 | KVMPhysicalDisk disk; |
667 | 682 | LibvirtStorageVolumeDef voldef = getStorageVolumeDef(libvirtPool.getPool().getConnect(), vol); |
668 | 683 | disk = new KVMPhysicalDisk(vol.getPath(), vol.getName(), pool); |
@@ -693,11 +708,153 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) { |
693 | 708 | } |
694 | 709 | return disk; |
695 | 710 | } catch (LibvirtException e) { |
696 | | - logger.debug("Failed to get physical disk:", e); |
| 711 | + logger.debug("Failed to get volume from libvirt: " + e.getMessage()); |
| 712 | + // For CLVM, try direct block device access as fallback |
| 713 | + if (pool.getType() == StoragePoolType.CLVM) { |
| 714 | + return getPhysicalDisk(volumeUuid, pool, libvirtPool); |
| 715 | + } |
| 716 | + |
697 | 717 | throw new CloudRuntimeException(e.toString()); |
698 | 718 | } |
699 | 719 | } |
700 | 720 |
|
| 721 | + private KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool, LibvirtStoragePool libvirtPool) { |
| 722 | + logger.info("CLVM volume not visible to libvirt, attempting direct block device access for volume: {}", volumeUuid); |
| 723 | + |
| 724 | + try { |
| 725 | + logger.debug("Refreshing libvirt storage pool: {}", pool.getUuid()); |
| 726 | + libvirtPool.getPool().refresh(0); |
| 727 | + |
| 728 | + StorageVol vol = getVolume(libvirtPool.getPool(), volumeUuid); |
| 729 | + if (vol != null) { |
| 730 | + logger.info("Volume found after pool refresh: {}", volumeUuid); |
| 731 | + KVMPhysicalDisk disk; |
| 732 | + LibvirtStorageVolumeDef voldef = getStorageVolumeDef(libvirtPool.getPool().getConnect(), vol); |
| 733 | + disk = new KVMPhysicalDisk(vol.getPath(), vol.getName(), pool); |
| 734 | + disk.setSize(vol.getInfo().allocation); |
| 735 | + disk.setVirtualSize(vol.getInfo().capacity); |
| 736 | + disk.setFormat(voldef.getFormat() == LibvirtStorageVolumeDef.VolumeFormat.QCOW2 ? |
| 737 | + PhysicalDiskFormat.QCOW2 : PhysicalDiskFormat.RAW); |
| 738 | + return disk; |
| 739 | + } |
| 740 | + } catch (LibvirtException refreshEx) { |
| 741 | + logger.debug("Pool refresh failed or volume still not found: {}", refreshEx.getMessage()); |
| 742 | + } |
| 743 | + |
| 744 | + // Still not found after refresh, try direct block device access |
| 745 | + return getPhysicalDiskViaDirectBlockDevice(volumeUuid, pool); |
| 746 | + } |
| 747 | + |
| 748 | + /** |
| 749 | + * For CLVM volumes that exist in LVM but are not visible to libvirt, |
| 750 | + * access them directly via block device path. |
| 751 | + */ |
| 752 | + private KVMPhysicalDisk getPhysicalDiskViaDirectBlockDevice(String volumeUuid, KVMStoragePool pool) { |
| 753 | + try { |
| 754 | + // For CLVM, pool sourceDir contains the VG path (e.g., "/dev/acsvg") |
| 755 | + // Extract the VG name |
| 756 | + String sourceDir = pool.getLocalPath(); |
| 757 | + if (sourceDir == null || sourceDir.isEmpty()) { |
| 758 | + throw new CloudRuntimeException("CLVM pool sourceDir is not set, cannot determine VG name"); |
| 759 | + } |
| 760 | + |
| 761 | + String vgName = sourceDir; |
| 762 | + if (vgName.startsWith("/")) { |
| 763 | + String[] parts = vgName.split("/"); |
| 764 | + List<String> tokens = Arrays.stream(parts) |
| 765 | + .filter(s -> !s.isEmpty()).collect(Collectors.toList()); |
| 766 | + |
| 767 | + vgName = tokens.size() > 1 ? tokens.get(1) |
| 768 | + : tokens.size() == 1 ? tokens.get(0) |
| 769 | + : ""; |
| 770 | + } |
| 771 | + |
| 772 | + logger.debug("Using VG name: {} (from sourceDir: {}) ", vgName, sourceDir); |
| 773 | + |
| 774 | + // Check if the LV exists in LVM using lvs command |
| 775 | + logger.debug("Checking if volume {} exsits in VG {}", volumeUuid, vgName); |
| 776 | + Script checkLvCmd = new Script("/usr/sbin/lvs", 5000, logger); |
| 777 | + checkLvCmd.add("--noheadings"); |
| 778 | + checkLvCmd.add("--unbuffered"); |
| 779 | + checkLvCmd.add(vgName + "/" + volumeUuid); |
| 780 | + |
| 781 | + String checkResult = checkLvCmd.execute(); |
| 782 | + if (checkResult != null) { |
| 783 | + logger.debug("Volume {} does not exist in VG {}: {}", volumeUuid, vgName, checkResult); |
| 784 | + throw new CloudRuntimeException(String.format("Storage volume not found: no storage vol with matching name '%s'", volumeUuid)); |
| 785 | + } |
| 786 | + |
| 787 | + logger.info("Volume {} exists in LVM but not visible to libvirt, accessing directly", volumeUuid); |
| 788 | + |
| 789 | + // Try standard device path first |
| 790 | + String lvPath = "/dev/" + vgName + "/" + volumeUuid; |
| 791 | + File lvDevice = new File(lvPath); |
| 792 | + |
| 793 | + if (!lvDevice.exists()) { |
| 794 | + // Try device-mapper path with escaped hyphens |
| 795 | + String vgNameEscaped = vgName.replace("-", "--"); |
| 796 | + String volumeUuidEscaped = volumeUuid.replace("-", "--"); |
| 797 | + lvPath = "/dev/mapper/" + vgNameEscaped + "-" + volumeUuidEscaped; |
| 798 | + lvDevice = new File(lvPath); |
| 799 | + |
| 800 | + if (!lvDevice.exists()) { |
| 801 | + logger.warn("Volume exists in LVM but device node not found: {}", volumeUuid); |
| 802 | + throw new CloudRuntimeException(String.format("Could not find volume %s " + |
| 803 | + "in VG %s - volume exists in LVM but device node not accessible", volumeUuid, vgName)); |
| 804 | + } |
| 805 | + } |
| 806 | + |
| 807 | + long size = 0; |
| 808 | + try { |
| 809 | + Script lvsCmd = new Script("/usr/sbin/lvs", 5000, logger); |
| 810 | + lvsCmd.add("--noheadings"); |
| 811 | + lvsCmd.add("--units"); |
| 812 | + lvsCmd.add("b"); |
| 813 | + lvsCmd.add("-o"); |
| 814 | + lvsCmd.add("lv_size"); |
| 815 | + lvsCmd.add(lvPath); |
| 816 | + |
| 817 | + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); |
| 818 | + String result = lvsCmd.execute(parser); |
| 819 | + |
| 820 | + String output = null; |
| 821 | + if (result == null) { |
| 822 | + output = parser.getLines(); |
| 823 | + } else { |
| 824 | + output = result; |
| 825 | + } |
| 826 | + |
| 827 | + if (output != null && !output.isEmpty()) { |
| 828 | + String sizeStr = output.trim().replaceAll("[^0-9]", ""); |
| 829 | + if (!sizeStr.isEmpty()) { |
| 830 | + size = Long.parseLong(sizeStr); |
| 831 | + } |
| 832 | + } |
| 833 | + } catch (Exception sizeEx) { |
| 834 | + logger.warn("Failed to get size for CLVM volume via lvs: {}", sizeEx.getMessage()); |
| 835 | + if (lvDevice.isFile()) { |
| 836 | + size = lvDevice.length(); |
| 837 | + } |
| 838 | + } |
| 839 | + |
| 840 | + KVMPhysicalDisk disk = new KVMPhysicalDisk(lvPath, volumeUuid, pool); |
| 841 | + disk.setFormat(PhysicalDiskFormat.RAW); |
| 842 | + disk.setSize(size); |
| 843 | + disk.setVirtualSize(size); |
| 844 | + |
| 845 | + logger.info("Successfully accessed CLVM volume via direct block device: {} " + |
| 846 | + "with size: {} bytes",lvPath, size); |
| 847 | + |
| 848 | + return disk; |
| 849 | + |
| 850 | + } catch (CloudRuntimeException ex) { |
| 851 | + throw ex; |
| 852 | + } catch (Exception ex) { |
| 853 | + logger.error("Failed to access CLVM volume via direct block device: {}",volumeUuid, ex); |
| 854 | + throw new CloudRuntimeException(String.format("Could not find volume %s: %s ",volumeUuid, ex.getMessage())); |
| 855 | + } |
| 856 | + } |
| 857 | + |
701 | 858 | /** |
702 | 859 | * adjust refcount |
703 | 860 | */ |
@@ -1227,7 +1384,11 @@ public boolean deletePhysicalDisk(String uuid, KVMStoragePool pool, Storage.Imag |
1227 | 1384 | LibvirtStoragePool libvirtPool = (LibvirtStoragePool)pool; |
1228 | 1385 | try { |
1229 | 1386 | StorageVol vol = getVolume(libvirtPool.getPool(), uuid); |
1230 | | - logger.debug("Instructing libvirt to remove volume " + uuid + " from pool " + pool.getUuid()); |
| 1387 | + if (vol == null) { |
| 1388 | + logger.warn("Volume %s not found in libvirt pool %s, it may have been already deleted", uuid, pool.getUuid()); |
| 1389 | + return true; |
| 1390 | + } |
| 1391 | + logger.debug("Instructing libvirt to remove volume %s from pool %s", uuid, pool.getUuid()); |
1231 | 1392 | if(Storage.ImageFormat.DIR.equals(format)){ |
1232 | 1393 | deleteDirVol(libvirtPool, vol); |
1233 | 1394 | } else { |
|
0 commit comments