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)
> +}
> +
next prev parent reply other threads:[~2026-03-27 21:12 UTC|newest]
Thread overview: 27+ 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
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.