public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Thomas Lamprecht <t.lamprecht@proxmox.com>
To: Lukas Wagner <l.wagner@proxmox.com>, pdm-devel@lists.proxmox.com
Subject: Re: [PATCH proxmox v2 05/25] disks: import from Proxmox Backup Server
Date: Fri, 27 Mar 2026 22:13:00 +0100	[thread overview]
Message-ID: <8fe38e72-9f7b-4df2-9434-0ca9ea8c4f0d@proxmox.com> (raw)
In-Reply-To: <20260319094617.169594-6-l.wagner@proxmox.com>

Am 20.03.26 um 11:23 schrieb Lukas Wagner:
> This is based on the disks module from PBS and left unchanged.
> 
> The version has not been set to 1.0 yet since it seems like this crate
> could use a bit a cleanup (custom error type instead of anyhow,
> documentation).

+1, and also some other clean up for pre-existing pain points w.r.t.
S.M.A.R.T, see inline.


> ---
>  Cargo.toml                         |    6 +
>  proxmox-disks/Cargo.toml           |   30 +
>  proxmox-disks/debian/changelog     |    5 +
>  proxmox-disks/debian/control       |   94 ++
>  proxmox-disks/debian/copyright     |   18 +
>  proxmox-disks/debian/debcargo.toml |    7 +
>  proxmox-disks/src/lib.rs           | 1396 ++++++++++++++++++++++++++++
>  proxmox-disks/src/lvm.rs           |   60 ++
>  proxmox-disks/src/parse_helpers.rs |   52 ++
>  proxmox-disks/src/smart.rs         |  227 +++++
>  proxmox-disks/src/zfs.rs           |  205 ++++
>  proxmox-disks/src/zpool_list.rs    |  294 ++++++
>  proxmox-disks/src/zpool_status.rs  |  496 ++++++++++
>  13 files changed, 2890 insertions(+)
>  create mode 100644 proxmox-disks/Cargo.toml
>  create mode 100644 proxmox-disks/debian/changelog
>  create mode 100644 proxmox-disks/debian/control
>  create mode 100644 proxmox-disks/debian/copyright
>  create mode 100644 proxmox-disks/debian/debcargo.toml
>  create mode 100644 proxmox-disks/src/lib.rs
>  create mode 100644 proxmox-disks/src/lvm.rs
>  create mode 100644 proxmox-disks/src/parse_helpers.rs
>  create mode 100644 proxmox-disks/src/smart.rs
>  create mode 100644 proxmox-disks/src/zfs.rs
>  create mode 100644 proxmox-disks/src/zpool_list.rs
>  create mode 100644 proxmox-disks/src/zpool_status.rs
> 


> diff --git a/proxmox-disks/src/lib.rs b/proxmox-disks/src/lib.rs
> new file mode 100644
> index 00000000..e6056c14
> --- /dev/null
> +++ b/proxmox-disks/src/lib.rs

[...]

> +#[api(
> +    properties: {
> +        used: {
> +            type: DiskUsageType,
> +        },
> +        "disk-type": {
> +            type: DiskType,
> +        },
> +        status: {
> +            type: SmartStatus,
> +        },
> +        partitions: {
> +            optional: true,
> +            items: {
> +                type: PartitionInfo
> +            }
> +        }
> +    }
> +)]
> +#[derive(Debug, Serialize, Deserialize)]
> +#[serde(rename_all = "kebab-case")]
> +/// Information about how a Disk is used
> +pub struct DiskUsageInfo {
> +    /// Disk name (`/sys/block/<name>`)
> +    pub name: String,
> +    pub used: DiskUsageType,
> +    pub disk_type: DiskType,
> +    pub status: SmartStatus,

pre-existing, I know, but after taking a third look I remembered the s.m.a.r.t slow
saga we had a while ago [0], and I think factoring this out would be a good time to
finally address the bigger pain points of that. One would be to make this optional
or drop this from the struct completely.

[0]: https://lore.proxmox.com/all/7b0ac740-096b-4850-a81d-5fa6bc9c347a@proxmox.com/

> +    /// Disk wearout
> +    pub wearout: Option<f64>,
> +    /// Vendor
> +    pub vendor: Option<String>,
> +    /// Model
> +    pub model: Option<String>,
> +    /// WWN
> +    pub wwn: Option<String>,
> +    /// Disk size
> +    pub size: u64,
> +    /// Serisal number
> +    pub serial: Option<String>,
> +    /// Partitions on the device
> +    pub partitions: Option<Vec<PartitionInfo>>,
> +    /// Linux device path (/dev/xxx)
> +    pub devpath: Option<String>,
> +    /// Set if disk contains a GPT partition table
> +    pub gpt: bool,
> +    /// RPM
> +    pub rpm: Option<u64>,
> +}
> +

[...]

> +/// Get disk usage information for multiple disks
> +fn get_disks(
> +    // filter - list of device names (without leading /dev)
> +    disks: Option<Vec<String>>,
> +    // do no include data from smartctl
> +    no_smart: bool,
> +    // include partitions
> +    include_partitions: bool,
> +) -> Result<HashMap<String, DiskUsageInfo>, Error> {
> +    let disk_manager = DiskManage::new();
> +
> +    let lsblk_info = get_lsblk_info()?;
> +
> +    let zfs_devices =
> +        zfs_devices(&lsblk_info, None).or_else(|err| -> Result<HashSet<u64>, Error> {
> +            eprintln!("error getting zfs devices: {err}");
> +            Ok(HashSet::new())
> +        })?;
> +
> +    let lvm_devices = get_lvm_devices(&lsblk_info)?;
> +
> +    let file_system_devices = get_file_system_devices(&lsblk_info)?;
> +
> +    // fixme: ceph journals/volumes
> +
> +    let mut result = HashMap::new();
> +    let mut device_paths = Vec::new();
> +
> +    for item in proxmox_sys::fs::scan_subdir(libc::AT_FDCWD, "/sys/block", &BLOCKDEVICE_NAME_REGEX)?
> +    {
> +        let item = item?;
> +
> +        let name = item.file_name().to_str().unwrap().to_string();
> +
> +        if let Some(ref disks) = disks {
> +            if !disks.contains(&name) {
> +                continue;
> +            }
> +        }
> +
> +        let sys_path = format!("/sys/block/{name}");
> +
> +        if let Ok(target) = std::fs::read_link(&sys_path) {
> +            if let Some(target) = target.to_str() {
> +                if ISCSI_PATH_REGEX.is_match(target) {
> +                    continue;
> +                } // skip iSCSI devices
> +            }
> +        }
> +
> +        let disk = disk_manager.clone().disk_by_sys_path(&sys_path)?;
> +
> +        let devnum = disk.devnum()?;
> +
> +        let size = match disk.size() {
> +            Ok(size) => size,
> +            Err(_) => continue, // skip devices with unreadable size
> +        };
> +
> +        let disk_type = match disk.guess_disk_type() {
> +            Ok(disk_type) => disk_type,
> +            Err(_) => continue, // skip devices with undetectable type
> +        };
> +
> +        let mut usage = DiskUsageType::Unused;
> +
> +        if lvm_devices.contains(&devnum) {
> +            usage = DiskUsageType::LVM;
> +        }
> +
> +        match disk.is_mounted() {
> +            Ok(true) => usage = DiskUsageType::Mounted,
> +            Ok(false) => {}
> +            Err(_) => continue, // skip devices with undetectable mount status
> +        }
> +
> +        if zfs_devices.contains(&devnum) {
> +            usage = DiskUsageType::ZFS;
> +        }
> +
> +        let vendor = disk
> +            .vendor()
> +            .unwrap_or(None)
> +            .map(|s| s.to_string_lossy().trim().to_string());
> +
> +        let model = disk.model().map(|s| s.to_string_lossy().into_owned());
> +
> +        let serial = disk.serial().map(|s| s.to_string_lossy().into_owned());
> +
> +        let devpath = disk
> +            .device_path()
> +            .map(|p| p.to_owned())
> +            .map(|p| p.to_string_lossy().to_string());
> +
> +        device_paths.push((name.clone(), devpath.clone()));
> +
> +        let wwn = disk.wwn().map(|s| s.to_string_lossy().into_owned());
> +
> +        let partitions: Option<Vec<PartitionInfo>> = if include_partitions {
> +            disk.partitions().map_or(None, |parts| {
> +                Some(get_partitions_info(
> +                    parts,
> +                    &lvm_devices,
> +                    &zfs_devices,
> +                    &file_system_devices,
> +                    &lsblk_info,
> +                ))
> +            })
> +        } else {
> +            None
> +        };
> +
> +        if usage != DiskUsageType::Mounted {
> +            match scan_partitions(disk_manager.clone(), &lvm_devices, &zfs_devices, &name) {
> +                Ok(part_usage) => {
> +                    if part_usage != DiskUsageType::Unused {
> +                        usage = part_usage;
> +                    }
> +                }
> +                Err(_) => continue, // skip devices if scan_partitions fail
> +            };
> +        }
> +
> +        if usage == DiskUsageType::Unused && file_system_devices.contains(&devnum) {
> +            usage = DiskUsageType::FileSystem;
> +        }
> +
> +        if usage == DiskUsageType::Unused && disk.has_holders()? {
> +            usage = DiskUsageType::DeviceMapper;
> +        }
> +
> +        let info = DiskUsageInfo {
> +            name: name.clone(),
> +            vendor,
> +            model,
> +            partitions,
> +            serial,
> +            devpath,
> +            size,
> +            wwn,
> +            disk_type,
> +            status: SmartStatus::Unknown,
> +            wearout: None,
> +            used: usage,
> +            gpt: disk.has_gpt(),
> +            rpm: disk.ata_rotation_rate_rpm(),
> +        };
> +
> +        result.insert(name, info);
> +    }
> +
> +    if !no_smart {

I still think such a branch should simple not exist for a get_disk (list) method, like
mentioned in (potentially now slightly outdated) [0].

> +        let (tx, rx) = crossbeam_channel::bounded(result.len());
> +
> +        let parallel_handler =
> +            ParallelHandler::new("smartctl data", 4, move |device: (String, String)| {
> +                match get_smart_data(Path::new(&device.1), false) {
> +                    Ok(smart_data) => tx.send((device.0, smart_data))?,
> +                    // do not fail the whole disk output just because smartctl couldn't query one
> +                    Err(err) => {
> +                        proxmox_log::error!("failed to gather smart data for {} – {err}", device.1)
> +                    }
> +                }
> +                Ok(())
> +            });
> +
> +        for (name, path) in device_paths.into_iter() {
> +            if let Some(p) = path {
> +                parallel_handler.send((name, p))?;
> +            }
> +        }
> +
> +        parallel_handler.complete()?;
> +        while let Ok(msg) = rx.recv() {
> +            if let Some(value) = result.get_mut(&msg.0) {
> +                value.wearout = msg.1.wearout;
> +                value.status = msg.1.status;
> +            }
> +        }
> +    }
> +    Ok(result)
> +}
> +






  reply	other threads:[~2026-03-27 21:12 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-19  9:45 [PATCH datacenter-manager/proxmox{,-backup,-yew-comp} v2 00/25] metric collection for the PDM host Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 01/25] parallel-handler: import code from Proxmox Backup Server Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 02/25] parallel-handler: introduce custom error type Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 03/25] parallel-handler: add documentation Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 04/25] parallel-handler: add simple unit-test suite Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 05/25] disks: import from Proxmox Backup Server Lukas Wagner
2026-03-27 21:13   ` Thomas Lamprecht [this message]
2026-03-19  9:45 ` [PATCH proxmox v2 06/25] disks: fix typo in `initialize_gpt_disk` Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 07/25] disks: add parts of gather_disk_stats from PBS Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 08/25] disks: gate api macro behind 'api-types' feature Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 09/25] disks: clippy: collapse if-let chains where possible Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 10/25] procfs: add helpers for querying pressure stall information Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox v2 11/25] time: use u64 parse helper from nom Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox-backup v2 12/25] tools: move ParallelHandler to new proxmox-parallel-handler crate Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox-backup v2 13/25] tools: replace disks module with proxmox-disks Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox-backup v2 14/25] metric collection: use blockdev_stat_for_path from proxmox_disks Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox-yew-comp v2 15/25] node status panel: add `children` property Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox-yew-comp v2 16/25] RRDGrid: fix size observer by attaching node reference to rendered container Lukas Wagner
2026-03-19  9:45 ` [PATCH proxmox-yew-comp v2 17/25] RRDGrid: add padding and increase gap between elements Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 18/25] metric collection: clarify naming for remote metric collection Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 19/25] metric collection: fix minor typo in error message Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 20/25] metric collection: collect PDM host metrics in a new collection task Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 21/25] api: fix /nodes/localhost/rrddata endpoint Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 22/25] pdm: node rrd data: rename 'total-time' to 'metric-collection-total-time' Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 23/25] pdm-api-types: add PDM host metric fields Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 24/25] ui: node status: add RRD graphs for PDM host metrics Lukas Wagner
2026-03-19  9:45 ` [PATCH datacenter-manager v2 25/25] ui: lxc/qemu/node: use RRD value render helpers Lukas Wagner
2026-03-28 17:52 ` partially-applied: [PATCH datacenter-manager/proxmox{,-backup,-yew-comp} v2 00/25] metric collection for the PDM host Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8fe38e72-9f7b-4df2-9434-0ca9ea8c4f0d@proxmox.com \
    --to=t.lamprecht@proxmox.com \
    --cc=l.wagner@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal