From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 5D7A1D301 for ; Thu, 13 Jul 2023 11:50:20 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 00017379B0 for ; Thu, 13 Jul 2023 11:49:49 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Thu, 13 Jul 2023 11:49:48 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id D21FA40881 for ; Thu, 13 Jul 2023 11:49:47 +0200 (CEST) From: Christoph Heiss To: pve-devel@lists.proxmox.com Date: Thu, 13 Jul 2023 11:49:30 +0200 Message-ID: <20230713094941.475486-7-c.heiss@proxmox.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230713094941.475486-1-c.heiss@proxmox.com> References: <20230713094941.475486-1-c.heiss@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.061 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH installer 6/7] tui: add RAID setup checks for ZFS/Btrfs X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 13 Jul 2023 09:50:20 -0000 This adds early checks when setting up ZFS and Btrfs RAIDs, such as minimum number of disks in the RAID, mirror sizes and legacy BIOS compatibility. The same rules as the GUI uses are applied, which unfortunaly means that this logic is essentially duplicated between the GUI and TUI. Signed-off-by: Christoph Heiss --- proxmox-tui-installer/src/views/bootdisk.rs | 136 +++++++++++++++++++- 1 file changed, 129 insertions(+), 7 deletions(-) diff --git a/proxmox-tui-installer/src/views/bootdisk.rs b/proxmox-tui-installer/src/views/bootdisk.rs index 19a71cf..9056323 100644 --- a/proxmox-tui-installer/src/views/bootdisk.rs +++ b/proxmox-tui-installer/src/views/bootdisk.rs @@ -10,11 +10,15 @@ use cursive::{ }; use super::{DiskSizeEditView, FormView, IntegerEditView}; -use crate::options::{ - AdvancedBootdiskOptions, BootdiskOptions, BtrfsBootdiskOptions, Disk, FsType, - LvmBootdiskOptions, ZfsBootdiskOptions, FS_TYPES, ZFS_CHECKSUM_OPTIONS, ZFS_COMPRESS_OPTIONS, +use crate::{ + options::{ + AdvancedBootdiskOptions, BootdiskOptions, BtrfsBootdiskOptions, BtrfsRaidLevel, Disk, + FsType, LvmBootdiskOptions, ZfsBootdiskOptions, ZfsRaidLevel, FS_TYPES, + ZFS_CHECKSUM_OPTIONS, ZFS_COMPRESS_OPTIONS, + }, + setup::{BootType, RuntimeInfo}, }; -use crate::setup::ProxmoxProduct; +use crate::{setup::ProxmoxProduct, InstallerState}; pub struct BootdiskOptionsView { view: LinearLayout, @@ -164,7 +168,7 @@ impl AdvancedBootdiskOptionsView { ); } - fn get_values(&mut self) -> Result { + fn get_values(&mut self, runinfo: &RuntimeInfo) -> Result { let fstype = self .view .get_child(1) @@ -193,6 +197,11 @@ impl AdvancedBootdiskOptionsView { .get_values() .ok_or("Failed to retrieve advanced bootdisk options")?; + if let FsType::Zfs(level) = fstype { + check_zfs_raid_config(runinfo, level, &disks) + .map_err(|err| format!("{fstype}: {err}"))?; + } + Ok(BootdiskOptions { disks, fstype, @@ -203,6 +212,10 @@ impl AdvancedBootdiskOptionsView { .get_values() .ok_or("Failed to retrieve advanced bootdisk options")?; + if let FsType::Btrfs(level) = fstype { + check_btrfs_raid_config(level, &disks).map_err(|err| format!("{fstype}: {err}"))?; + } + Ok(BootdiskOptions { disks, fstype, @@ -541,11 +554,17 @@ fn advanced_options_view(disks: &[Disk], options: Rc>) .button("Ok", { let options_ref = options.clone(); move |siv| { + let runinfo = siv + .user_data::() + .unwrap() + .runtime_info + .clone(); + let options = siv .call_on_name("advanced-bootdisk-options-dialog", |view: &mut Dialog| { view.get_content_mut() - .downcast_mut() - .map(AdvancedBootdiskOptionsView::get_values) + .downcast_mut::() + .map(|v| v.get_values(&runinfo)) }) .flatten(); @@ -592,3 +611,106 @@ fn check_for_duplicate_disks(disks: &[Disk]) -> Result<(), &Disk> { Ok(()) } + +/// Simple wrapper which returns an descriptive error if the list of disks is too short. +/// +/// # Arguments +/// +/// * `disks` - A list of disks to check the lenght of. +/// * `min` - Minimum number of disks +fn check_raid_min_disks(disks: &[Disk], min: usize) -> Result<(), String> { + if disks.len() < min { + Err(format!("Need at least {min} disks")) + } else { + Ok(()) + } +} + +/// Checks whether a user-supplied ZFS RAID setup is valid or not, such as disk sizes, minimum +/// number of disks and legacy BIOS compatibility. +/// +/// # Arguments +/// +/// * `runinfo` - `RuntimeInfo` instance of currently running system +/// * `level` - The targeted ZFS RAID level by the user. +/// * `disks` - List of disks designated as RAID targets. +fn check_zfs_raid_config( + runinfo: &RuntimeInfo, + level: ZfsRaidLevel, + disks: &[Disk], +) -> Result<(), String> { + // See also Proxmox/Install.pm:get_zfs_raid_setup() + + for disk in disks { + if runinfo.boot_type != BootType::Efi && disk.block_size == 4096 { + return Err("Booting from 4Kn drive in legacy BIOS mode is not supported.".to_owned()); + } + } + + let check_mirror_size = |disk1: &Disk, disk2: &Disk| { + if (disk1.size - disk2.size).abs() > disk1.size / 10. { + Err(format!( + "Mirrored disks must have same size:\n\n * {disk1}\n * {disk2}" + )) + } else { + Ok(()) + } + }; + + match level { + ZfsRaidLevel::Raid0 => check_raid_min_disks(disks, 1)?, + ZfsRaidLevel::Raid1 => { + check_raid_min_disks(disks, 2)?; + for disk in disks { + check_mirror_size(&disks[0], disk)?; + } + } + ZfsRaidLevel::Raid10 => { + check_raid_min_disks(disks, 4)?; + // Pairs need to have the same size + for i in (0..disks.len()).step_by(2) { + check_mirror_size(&disks[i], &disks[i + 1])?; + } + } + // For RAID-Z: minimum disks number is level + 2 + ZfsRaidLevel::RaidZ => { + check_raid_min_disks(disks, 3)?; + for disk in disks { + check_mirror_size(&disks[0], disk)?; + } + } + ZfsRaidLevel::RaidZ2 => { + check_raid_min_disks(disks, 4)?; + for disk in disks { + check_mirror_size(&disks[0], disk)?; + } + } + ZfsRaidLevel::RaidZ3 => { + check_raid_min_disks(disks, 5)?; + for disk in disks { + check_mirror_size(&disks[0], disk)?; + } + } + } + + Ok(()) +} + +/// Checks whether a user-supplied Btrfs RAID setup is valid or not, such as minimum +/// number of disks. +/// +/// # Arguments +/// +/// * `level` - The targeted Btrfs RAID level by the user. +/// * `disks` - List of disks designated as RAID targets. +fn check_btrfs_raid_config(level: BtrfsRaidLevel, disks: &[Disk]) -> Result<(), String> { + // See also Proxmox/Install.pm:get_btrfs_raid_setup() + + match level { + BtrfsRaidLevel::Raid0 => check_raid_min_disks(disks, 1)?, + BtrfsRaidLevel::Raid1 => check_raid_min_disks(disks, 2)?, + BtrfsRaidLevel::Raid10 => check_raid_min_disks(disks, 4)?, + } + + Ok(()) +} -- 2.41.0