[pbs-devel] [PATCH proxmox-backup 1/3] api2: disks endpoint return partitions

Hannes Laimer h.laimer at proxmox.com
Mon Apr 4 11:50:46 CEST 2022


Signed-off-by: Hannes Laimer <h.laimer at proxmox.com>
---
 src/api2/node/disks/directory.rs |   2 +-
 src/api2/node/disks/mod.rs       |  11 ++-
 src/api2/node/disks/zfs.rs       |   2 +-
 src/tools/disks/mod.rs           | 121 ++++++++++++++++++++++++++++++-
 4 files changed, 130 insertions(+), 6 deletions(-)

diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
index bf1a1be6..79fe2624 100644
--- a/src/api2/node/disks/directory.rs
+++ b/src/api2/node/disks/directory.rs
@@ -149,7 +149,7 @@ pub fn create_datastore_disk(
 
     let auth_id = rpcenv.get_auth_id().unwrap();
 
-    let info = get_disk_usage_info(&disk, true)?;
+    let info = get_disk_usage_info(&disk, true, false)?;
 
     if info.used != DiskUsageType::Unused {
         bail!("disk '{}' is already in use.", disk);
diff --git a/src/api2/node/disks/mod.rs b/src/api2/node/disks/mod.rs
index c44ccfee..478829fb 100644
--- a/src/api2/node/disks/mod.rs
+++ b/src/api2/node/disks/mod.rs
@@ -33,6 +33,12 @@ pub mod zfs;
                 optional: true,
                 default: false,
             },
+            "include-partitions": {
+                description: "Include partitions.",
+                type: bool,
+                optional: true,
+                default: false,
+            },
             "usage-type": {
                 type: DiskUsageType,
                 optional: true,
@@ -53,11 +59,12 @@ pub mod zfs;
 /// List local disks
 pub fn list_disks(
     skipsmart: bool,
+    include_partitions: bool,
     usage_type: Option<DiskUsageType>,
 ) -> Result<Vec<DiskUsageInfo>, Error> {
     let mut list = Vec::new();
 
-    for (_, info) in get_disks(None, skipsmart)? {
+    for (_, info) in get_disks(None, skipsmart, include_partitions)? {
         if let Some(ref usage_type) = usage_type {
             if info.used == *usage_type {
                 list.push(info);
@@ -140,7 +147,7 @@ pub fn initialize_disk(
 
     let auth_id = rpcenv.get_auth_id().unwrap();
 
-    let info = get_disk_usage_info(&disk, true)?;
+    let info = get_disk_usage_info(&disk, true, false)?;
 
     if info.used != DiskUsageType::Unused {
         bail!("disk '{}' is already in use.", disk);
diff --git a/src/api2/node/disks/zfs.rs b/src/api2/node/disks/zfs.rs
index a542f9e0..5329f44f 100644
--- a/src/api2/node/disks/zfs.rs
+++ b/src/api2/node/disks/zfs.rs
@@ -177,7 +177,7 @@ pub fn create_zpool(
     let devices: Vec<String> = devices.as_array().unwrap().iter()
         .map(|v| v.as_str().unwrap().to_string()).collect();
 
-    let disk_map = crate::tools::disks::get_disks(None, true)?;
+    let disk_map = crate::tools::disks::get_disks(None, true, false)?;
     for disk in devices.iter() {
         match disk_map.get(disk) {
             Some(info) => {
diff --git a/src/tools/disks/mod.rs b/src/tools/disks/mod.rs
index 94da7b3a..fb9c78a9 100644
--- a/src/tools/disks/mod.rs
+++ b/src/tools/disks/mod.rs
@@ -602,6 +602,26 @@ fn get_file_system_devices(lsblk_info: &[LsblkInfo]) -> Result<HashSet<u64>, Err
     Ok(device_set)
 }
 
+#[api()]
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "lowercase")]
+pub enum PartitionUsageType {
+    /// Partition is not used (as far we can tell)
+    Unused,
+    /// Partition is mounted
+    Mounted,
+    /// Partition is used by LVM
+    LVM,
+    /// Partition is used by ZFS
+    ZFS,
+    /// Partition is an EFI partition
+    EFI,
+    /// Partition is a BIOS partition
+    BIOS,
+    /// Partition contains a file system label
+    FileSystem,
+}
+
 #[api()]
 #[derive(Debug, Serialize, Deserialize, PartialEq)]
 #[serde(rename_all = "lowercase")]
@@ -622,6 +642,23 @@ pub enum DiskUsageType {
     FileSystem,
 }
 
+#[api()]
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+/// Baic information about a partition
+pub struct PartitionInfo {
+    /// The partition name
+    pub name: String,
+    /// What the partition is used for
+    pub used: PartitionUsageType,
+    /// The partition devpath
+    pub devpath: Option<String>,
+    /// Size in bytes
+    pub size: Option<u64>,
+    /// Size in bytes
+    pub gpt: bool,
+}
+
 #[api(
     properties: {
         used: {
@@ -632,6 +669,12 @@ pub enum DiskUsageType {
         },
         status: {
             type: SmartStatus,
+        },
+        partitions: {
+            optional: true,
+            items: {
+                type: PartitionInfo
+            }
         }
     }
 )]
@@ -656,6 +699,8 @@ pub struct DiskUsageInfo {
     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
@@ -733,10 +778,14 @@ fn scan_partitions(
 }
 
 /// Get disk usage information for a single disk
-pub fn get_disk_usage_info(disk: &str, no_smart: bool) -> Result<DiskUsageInfo, Error> {
+pub fn get_disk_usage_info(
+    disk: &str,
+    no_smart: bool,
+    include_partitions: bool,
+) -> Result<DiskUsageInfo, Error> {
     let mut filter = Vec::new();
     filter.push(disk.to_string());
-    let mut map = get_disks(Some(filter), no_smart)?;
+    let mut map = get_disks(Some(filter), no_smart, include_partitions)?;
     if let Some(info) = map.remove(disk) {
         Ok(info)
     } else {
@@ -750,6 +799,8 @@ pub fn get_disks(
     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();
 
@@ -837,6 +888,71 @@ pub fn get_disks(
 
         let wwn = disk.wwn().map(|s| s.to_string_lossy().into_owned());
 
+        let partitions: Option<Vec<PartitionInfo>> = if include_partitions {
+            let lsblk_infos = get_lsblk_info();
+            disk.partitions().map_or(None, |parts| {
+                Some(
+                    parts
+                        .iter()
+                        .map(|(nr, disk)| {
+                            let devpath = disk
+                                .device_path()
+                                .map(|p| p.to_owned())
+                                .map(|p| p.to_string_lossy().to_string());
+
+                            let mut used = PartitionUsageType::Unused;
+
+                            if let Some(devnum) = disk.devnum().ok() {
+                                if lvm_devices.contains(&devnum) {
+                                    used = PartitionUsageType::LVM;
+                                }
+                                if zfs_devices.contains(&devnum) {
+                                    used = PartitionUsageType::ZFS;
+                                }
+                            }
+                            match disk.is_mounted() {
+                                Ok(true) => used = PartitionUsageType::Mounted,
+                                Ok(false) => {}
+                                Err(_) => {} // skip devices with undetectable mount status
+                            }
+
+                            if let Some(devpath) = devpath.as_ref() {
+                                if let Ok(infos) = lsblk_infos.as_ref() {
+                                    for info in infos.iter().filter(|i| i.path.eq(devpath)) {
+                                        used = match info.partition_type.as_deref() {
+                                            Some("21686148-6449-6e6f-744e-656564454649") => {
+                                                PartitionUsageType::BIOS
+                                            }
+                                            Some("c12a7328-f81f-11d2-ba4b-00a0c93ec93b") => {
+                                                PartitionUsageType::EFI
+                                            }
+                                            _ => used,
+                                        }
+                                    }
+                                }
+                            }
+
+                            if used == PartitionUsageType::Unused
+                                && file_system_devices.contains(&devnum)
+                            {
+                                used = PartitionUsageType::FileSystem;
+                            }
+
+                            PartitionInfo {
+                                name: format!("{}{}", name, nr),
+                                devpath,
+                                used,
+                                size: disk.size().ok(),
+                                gpt: disk.has_gpt(),
+                            }
+                        })
+                        .collect(),
+                )
+            })
+        } else {
+            None
+        };
+
         if usage != DiskUsageType::Mounted {
             match scan_partitions(disk_manager.clone(), &lvm_devices, &zfs_devices, &name) {
                 Ok(part_usage) => {
@@ -870,6 +986,7 @@ pub fn get_disks(
             name: name.clone(),
             vendor,
             model,
+            partitions,
             serial,
             devpath,
             size,
-- 
2.30.2






More information about the pbs-devel mailing list