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

Hannes Laimer h.laimer at proxmox.com
Wed Jun 8 10:51:51 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           | 132 ++++++++++++++++++++++++++++++-
 4 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
index f4d85d0a..123a8d7b 100644
--- a/src/api2/node/disks/directory.rs
+++ b/src/api2/node/disks/directory.rs
@@ -147,7 +147,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 dac6f535..5cb23e70 100644
--- a/src/api2/node/disks/zfs.rs
+++ b/src/api2/node/disks/zfs.rs
@@ -174,7 +174,7 @@ pub fn create_zpool(
         .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 568dccbf..ea4c687a 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 used by LVM
+    LVM,
+    /// Partition is used by ZFS
+    ZFS,
+    /// Partition is ZFS reserved
+    ZfsReserved,
+    /// 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,27 @@ pub enum DiskUsageType {
     FileSystem,
 }
 
+#[api()]
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+/// Baisc information about a partition
+pub struct PartitionInfo {
+    /// The partition name
+    pub name: String,
+    /// What the partition is used for
+    pub used: PartitionUsageType,
+    /// Is the partition mounted
+    pub mounted: bool,
+    /// The filesystem of the partition
+    pub filesystem: Option<String>,
+    /// The partition devpath
+    pub devpath: Option<String>,
+    /// Size in bytes
+    pub size: Option<u64>,
+    /// GPT partition
+    pub gpt: bool,
+}
+
 #[api(
     properties: {
         used: {
@@ -632,6 +673,12 @@ pub enum DiskUsageType {
         },
         status: {
             type: SmartStatus,
+        },
+        partitions: {
+            optional: true,
+            items: {
+                type: PartitionInfo
+            }
         }
     }
 )]
@@ -656,6 +703,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 +782,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 {
@@ -744,12 +797,72 @@ pub fn get_disk_usage_info(disk: &str, no_smart: bool) -> Result<DiskUsageInfo,
     }
 }
 
+fn get_partitions_info(
+    partitions: HashMap<u64, Disk>,
+    lvm_devices: &HashSet<u64>,
+    zfs_devices: &HashSet<u64>,
+    file_system_devices: &HashSet<u64>,
+) -> Vec<PartitionInfo> {
+    let lsblk_infos = get_lsblk_info().ok();
+    partitions
+        .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;
+                } else if zfs_devices.contains(&devnum) {
+                    used = PartitionUsageType::ZFS;
+                } else if file_system_devices.contains(&devnum) {
+                    used = PartitionUsageType::FileSystem;
+                }
+            }
+
+            let mounted = disk.is_mounted().unwrap_or(false);
+            let mut filesystem = None;
+            if let (Some(devpath), Some(infos)) = (devpath.as_ref(), 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,
+                        Some("6a945a3b-1dd2-11b2-99a6-080020736631") => {
+                            PartitionUsageType::ZfsReserved
+                        }
+                        _ => used,
+                    };
+                    if used == PartitionUsageType::FileSystem {
+                        filesystem = info.file_system_type.clone();
+                    }
+                }
+            }
+
+            PartitionInfo {
+                name: disk.sysname().to_str().unwrap_or("?").to_string(),
+                devpath,
+                used,
+                mounted,
+                filesystem,
+                size: disk.size().ok(),
+                gpt: disk.has_gpt(),
+            }
+        })
+        .collect()
+}
+
 /// Get disk usage information for multiple disks
 pub 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();
 
@@ -837,6 +950,19 @@ pub fn get_disks(
 
         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,
+                ))
+            })
+        } else {
+            None
+        };
+
         if usage != DiskUsageType::Mounted {
             match scan_partitions(disk_manager.clone(), &lvm_devices, &zfs_devices, &name) {
                 Ok(part_usage) => {
@@ -870,6 +996,7 @@ pub fn get_disks(
             name: name.clone(),
             vendor,
             model,
+            partitions,
             serial,
             devpath,
             size,
@@ -989,7 +1116,6 @@ pub fn create_file_system(disk: &Disk, fs_type: FileSystemType) -> Result<(), Er
 
     Ok(())
 }
-
 /// Block device name completion helper
 pub fn complete_disk_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
     let dir =
-- 
2.30.2






More information about the pbs-devel mailing list