api2: disks endpoint return partitions

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
This commit is contained in:
Hannes Laimer 2022-06-08 08:51:51 +00:00 committed by Wolfgang Bumiller
parent a1896bffb6
commit 6a6ba4cdac
4 changed files with 140 additions and 7 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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) => {

View File

@ -584,6 +584,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")]
@ -604,6 +624,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: {
@ -614,6 +655,12 @@ pub enum DiskUsageType {
},
status: {
type: SmartStatus,
},
partitions: {
optional: true,
items: {
type: PartitionInfo
}
}
}
)]
@ -638,6 +685,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
@ -715,10 +764,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 {
@ -726,12 +779,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();
@ -819,6 +932,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) => {
@ -852,6 +978,7 @@ pub fn get_disks(
name: name.clone(),
vendor,
model,
partitions,
serial,
devpath,
size,
@ -971,7 +1098,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 =