From ebcc08cc8fed759ff2afd4b90150292f60fe81aa Mon Sep 17 00:00:00 2001 From: illustris Date: Mon, 14 Apr 2025 08:30:44 +0530 Subject: [PATCH] Use zpool command to measure ZFS pool sizes accurately Replace statvfs with zpool list command for ZFS storage pools to get accurate size and free space metrics. This resolves the 'mountpoint' key error for ZFS pools and provides more accurate capacity information. --- src/pvestorage/__init__.py | 54 ++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/pvestorage/__init__.py b/src/pvestorage/__init__.py index 7c78625..7f4787c 100644 --- a/src/pvestorage/__init__.py +++ b/src/pvestorage/__init__.py @@ -87,15 +87,53 @@ def parse_storage_cfg(file_path='/etc/pve/storage.cfg'): def get_storage_size(storage): try: - if storage["type"] in ["dir", "nfs", "cephfs", "zfspool"]: - if storage["type"] == "zfspool": - path = storage["mountpoint"] - else: - path = storage["path"] - # Get filesystem statistics + if storage["type"] == "zfspool": + if "pool" not in storage: + logging.debug(f"ZFS pool {storage['name']} has no pool name configured") + return None + + # Extract the pool name (could be in format like rpool/data) + pool_name = storage["pool"].split("/")[0] + + # Use zpool command to get accurate size information + import subprocess + try: + result = subprocess.run( + ["zpool", "list", pool_name, "-p"], + capture_output=True, + text=True, + check=True + ) + + # Parse the output + lines = result.stdout.strip().split("\n") + if len(lines) < 2: + logging.warn(f"Unexpected zpool list output format for {pool_name}") + return None + + # Extract values from the second line (the data line) + values = lines[1].split() + if len(values) < 4: + logging.warn(f"Insufficient data in zpool list output for {pool_name}") + return None + + # Values are: NAME SIZE ALLOC FREE ... + # We need the SIZE and FREE values (index 1 and 3) + total_size = int(values[1]) + free_space = int(values[3]) + + return { + "total": total_size, + "free": free_space + } + except (subprocess.SubprocessError, ValueError, IndexError) as e: + logging.warn(f"Error running zpool list for {pool_name}: {e}") + return None + + elif storage["type"] in ["dir", "nfs", "cephfs"]: + # For non-ZFS storage, use statvfs + path = storage["path"] stats = os.statvfs(path) - # Calculate total size and free space in bytes - # TODO: find an alternative way to calculate total_size for ZFS total_size = stats.f_frsize * stats.f_blocks free_space = stats.f_frsize * stats.f_bavail return {