all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Aaron Lauterer <a.lauterer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH 07/12] tui: switch to common crate
Date: Wed, 25 Oct 2023 18:00:06 +0200	[thread overview]
Message-ID: <20231025160011.3617524-8-a.lauterer@proxmox.com> (raw)
In-Reply-To: <20231025160011.3617524-1-a.lauterer@proxmox.com>

by switching dependencies and deleting doubled code to avoid ambiguities
within the same scope.

Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
---
 proxmox-tui-installer/src/main.rs           |  13 +-
 proxmox-tui-installer/src/options.rs        | 403 +-------------------
 proxmox-tui-installer/src/setup.rs          | 316 +--------------
 proxmox-tui-installer/src/system.rs         |   2 +-
 proxmox-tui-installer/src/views/bootdisk.rs | 248 +-----------
 proxmox-tui-installer/src/views/mod.rs      |   2 +-
 proxmox-tui-installer/src/views/timezone.rs |   4 +-
 7 files changed, 44 insertions(+), 944 deletions(-)

diff --git a/proxmox-tui-installer/src/main.rs b/proxmox-tui-installer/src/main.rs
index 81fe3ca..875a33a 100644
--- a/proxmox-tui-installer/src/main.rs
+++ b/proxmox-tui-installer/src/main.rs
@@ -28,16 +28,19 @@ use cursive::{
 use regex::Regex;
 
 mod options;
-use options::*;
+use options::InstallerOptions;
+
+use proxmox_installer_common::{
+    options::{BootdiskOptions, NetworkOptions, PasswordOptions, TimezoneOptions},
+    setup::{LocaleInfo, ProxmoxProduct, RuntimeInfo, SetupInfo},
+    utils::Fqdn,
+};
 
 mod setup;
-use setup::{InstallConfig, LocaleInfo, ProxmoxProduct, RuntimeInfo, SetupInfo};
+use setup::InstallConfig;
 
 mod system;
 
-mod utils;
-use utils::Fqdn;
-
 mod views;
 use views::{
     BootdiskOptionsView, CidrAddressEditView, FormView, TableView, TableViewItem,
diff --git a/proxmox-tui-installer/src/options.rs b/proxmox-tui-installer/src/options.rs
index 85b39b8..221ad01 100644
--- a/proxmox-tui-installer/src/options.rs
+++ b/proxmox-tui-installer/src/options.rs
@@ -1,77 +1,12 @@
-use std::net::{IpAddr, Ipv4Addr};
-use std::{cmp, fmt};
-
-use crate::setup::{LocaleInfo, NetworkInfo, RuntimeInfo, SetupInfo};
-use crate::utils::{CidrAddress, Fqdn};
 use crate::SummaryOption;
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum BtrfsRaidLevel {
-    Raid0,
-    Raid1,
-    Raid10,
-}
-
-impl fmt::Display for BtrfsRaidLevel {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use BtrfsRaidLevel::*;
-        match self {
-            Raid0 => write!(f, "RAID0"),
-            Raid1 => write!(f, "RAID1"),
-            Raid10 => write!(f, "RAID10"),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum ZfsRaidLevel {
-    Raid0,
-    Raid1,
-    Raid10,
-    RaidZ,
-    RaidZ2,
-    RaidZ3,
-}
-
-impl fmt::Display for ZfsRaidLevel {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use ZfsRaidLevel::*;
-        match self {
-            Raid0 => write!(f, "RAID0"),
-            Raid1 => write!(f, "RAID1"),
-            Raid10 => write!(f, "RAID10"),
-            RaidZ => write!(f, "RAIDZ-1"),
-            RaidZ2 => write!(f, "RAIDZ-2"),
-            RaidZ3 => write!(f, "RAIDZ-3"),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum FsType {
-    Ext4,
-    Xfs,
-    Zfs(ZfsRaidLevel),
-    Btrfs(BtrfsRaidLevel),
-}
-
-impl FsType {
-    pub fn is_btrfs(&self) -> bool {
-        matches!(self, FsType::Btrfs(_))
-    }
-}
-
-impl fmt::Display for FsType {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use FsType::*;
-        match self {
-            Ext4 => write!(f, "ext4"),
-            Xfs => write!(f, "XFS"),
-            Zfs(level) => write!(f, "ZFS ({level})"),
-            Btrfs(level) => write!(f, "Btrfs ({level})"),
-        }
-    }
-}
+use proxmox_installer_common::{
+    setup::LocaleInfo,
+    options::{
+        BootdiskOptions, BtrfsRaidLevel, FsType, NetworkOptions, TimezoneOptions,
+        PasswordOptions, ZfsRaidLevel,
+    },
+};
 
 pub const FS_TYPES: &[FsType] = {
     use FsType::*;
@@ -90,320 +25,6 @@ pub const FS_TYPES: &[FsType] = {
     ]
 };
 
-#[derive(Clone, Debug)]
-pub struct LvmBootdiskOptions {
-    pub total_size: f64,
-    pub swap_size: Option<f64>,
-    pub max_root_size: Option<f64>,
-    pub max_data_size: Option<f64>,
-    pub min_lvm_free: Option<f64>,
-}
-
-impl LvmBootdiskOptions {
-    pub fn defaults_from(disk: &Disk) -> Self {
-        Self {
-            total_size: disk.size,
-            swap_size: None,
-            max_root_size: None,
-            max_data_size: None,
-            min_lvm_free: None,
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct BtrfsBootdiskOptions {
-    pub disk_size: f64,
-    pub selected_disks: Vec<usize>,
-}
-
-impl BtrfsBootdiskOptions {
-    /// This panics if the provided slice is empty.
-    pub fn defaults_from(disks: &[Disk]) -> Self {
-        let disk = &disks[0];
-        Self {
-            disk_size: disk.size,
-            selected_disks: (0..disks.len()).collect(),
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub enum ZfsCompressOption {
-    #[default]
-    On,
-    Off,
-    Lzjb,
-    Lz4,
-    Zle,
-    Gzip,
-    Zstd,
-}
-
-impl fmt::Display for ZfsCompressOption {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", format!("{self:?}").to_lowercase())
-    }
-}
-
-impl From<&ZfsCompressOption> for String {
-    fn from(value: &ZfsCompressOption) -> Self {
-        value.to_string()
-    }
-}
-
-pub const ZFS_COMPRESS_OPTIONS: &[ZfsCompressOption] = {
-    use ZfsCompressOption::*;
-    &[On, Off, Lzjb, Lz4, Zle, Gzip, Zstd]
-};
-
-#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
-pub enum ZfsChecksumOption {
-    #[default]
-    On,
-    Off,
-    Fletcher2,
-    Fletcher4,
-    Sha256,
-}
-
-impl fmt::Display for ZfsChecksumOption {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", format!("{self:?}").to_lowercase())
-    }
-}
-
-impl From<&ZfsChecksumOption> for String {
-    fn from(value: &ZfsChecksumOption) -> Self {
-        value.to_string()
-    }
-}
-
-pub const ZFS_CHECKSUM_OPTIONS: &[ZfsChecksumOption] = {
-    use ZfsChecksumOption::*;
-    &[On, Off, Fletcher2, Fletcher4, Sha256]
-};
-
-#[derive(Clone, Debug)]
-pub struct ZfsBootdiskOptions {
-    pub ashift: usize,
-    pub compress: ZfsCompressOption,
-    pub checksum: ZfsChecksumOption,
-    pub copies: usize,
-    pub disk_size: f64,
-    pub selected_disks: Vec<usize>,
-}
-
-impl ZfsBootdiskOptions {
-    /// This panics if the provided slice is empty.
-    pub fn defaults_from(disks: &[Disk]) -> Self {
-        let disk = &disks[0];
-        Self {
-            ashift: 12,
-            compress: ZfsCompressOption::default(),
-            checksum: ZfsChecksumOption::default(),
-            copies: 1,
-            disk_size: disk.size,
-            selected_disks: (0..disks.len()).collect(),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum AdvancedBootdiskOptions {
-    Lvm(LvmBootdiskOptions),
-    Zfs(ZfsBootdiskOptions),
-    Btrfs(BtrfsBootdiskOptions),
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct Disk {
-    pub index: String,
-    pub path: String,
-    pub model: Option<String>,
-    pub size: f64,
-    pub block_size: Option<usize>,
-}
-
-impl fmt::Display for Disk {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // TODO: Format sizes properly with `proxmox-human-byte` once merged
-        // https://lists.proxmox.com/pipermail/pbs-devel/2023-May/006125.html
-        f.write_str(&self.path)?;
-        if let Some(model) = &self.model {
-            // FIXME: ellipsize too-long names?
-            write!(f, " ({model})")?;
-        }
-        write!(f, " ({:.2} GiB)", self.size)
-    }
-}
-
-impl From<&Disk> for String {
-    fn from(value: &Disk) -> Self {
-        value.to_string()
-    }
-}
-
-impl cmp::Eq for Disk {}
-
-impl cmp::PartialOrd for Disk {
-    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-        self.index.partial_cmp(&other.index)
-    }
-}
-
-impl cmp::Ord for Disk {
-    fn cmp(&self, other: &Self) -> cmp::Ordering {
-        self.index.cmp(&other.index)
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct BootdiskOptions {
-    pub disks: Vec<Disk>,
-    pub fstype: FsType,
-    pub advanced: AdvancedBootdiskOptions,
-}
-
-impl BootdiskOptions {
-    pub fn defaults_from(disk: &Disk) -> Self {
-        Self {
-            disks: vec![disk.clone()],
-            fstype: FsType::Ext4,
-            advanced: AdvancedBootdiskOptions::Lvm(LvmBootdiskOptions::defaults_from(disk)),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct TimezoneOptions {
-    pub country: String,
-    pub timezone: String,
-    pub kb_layout: String,
-}
-
-impl TimezoneOptions {
-    pub fn defaults_from(runtime: &RuntimeInfo, locales: &LocaleInfo) -> Self {
-        let country = runtime.country.clone().unwrap_or_else(|| "at".to_owned());
-
-        let timezone = locales
-            .cczones
-            .get(&country)
-            .and_then(|zones| zones.get(0))
-            .cloned()
-            .unwrap_or_else(|| "UTC".to_owned());
-
-        let kb_layout = locales
-            .countries
-            .get(&country)
-            .and_then(|c| {
-                if c.kmap.is_empty() {
-                    None
-                } else {
-                    Some(c.kmap.clone())
-                }
-            })
-            .unwrap_or_else(|| "en-us".to_owned());
-
-        Self {
-            country,
-            timezone,
-            kb_layout,
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct PasswordOptions {
-    pub email: String,
-    pub root_password: String,
-}
-
-impl Default for PasswordOptions {
-    fn default() -> Self {
-        Self {
-            email: "mail@example.invalid".to_string(),
-            root_password: String::new(),
-        }
-    }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub struct NetworkOptions {
-    pub ifname: String,
-    pub fqdn: Fqdn,
-    pub address: CidrAddress,
-    pub gateway: IpAddr,
-    pub dns_server: IpAddr,
-}
-
-impl NetworkOptions {
-    const DEFAULT_DOMAIN: &str = "example.invalid";
-
-    pub fn defaults_from(setup: &SetupInfo, network: &NetworkInfo) -> Self {
-        let mut this = Self {
-            ifname: String::new(),
-            fqdn: Self::construct_fqdn(network, setup.config.product.default_hostname()),
-            // Safety: The provided mask will always be valid.
-            address: CidrAddress::new(Ipv4Addr::UNSPECIFIED, 0).unwrap(),
-            gateway: Ipv4Addr::UNSPECIFIED.into(),
-            dns_server: Ipv4Addr::UNSPECIFIED.into(),
-        };
-
-        if let Some(ip) = network.dns.dns.first() {
-            this.dns_server = *ip;
-        }
-
-        if let Some(routes) = &network.routes {
-            let mut filled = false;
-            if let Some(gw) = &routes.gateway4 {
-                if let Some(iface) = network.interfaces.get(&gw.dev) {
-                    this.ifname = iface.name.clone();
-                    if let Some(addresses) = &iface.addresses {
-                        if let Some(addr) = addresses.iter().find(|addr| addr.is_ipv4()) {
-                            this.gateway = gw.gateway;
-                            this.address = addr.clone();
-                            filled = true;
-                        }
-                    }
-                }
-            }
-            if !filled {
-                if let Some(gw) = &routes.gateway6 {
-                    if let Some(iface) = network.interfaces.get(&gw.dev) {
-                        if let Some(addresses) = &iface.addresses {
-                            if let Some(addr) = addresses.iter().find(|addr| addr.is_ipv6()) {
-                                this.ifname = iface.name.clone();
-                                this.gateway = gw.gateway;
-                                this.address = addr.clone();
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        this
-    }
-
-    fn construct_fqdn(network: &NetworkInfo, default_hostname: &str) -> Fqdn {
-        let hostname = network.hostname.as_deref().unwrap_or(default_hostname);
-
-        let domain = network
-            .dns
-            .domain
-            .as_deref()
-            .unwrap_or(Self::DEFAULT_DOMAIN);
-
-        Fqdn::from(&format!("{hostname}.{domain}")).unwrap_or_else(|_| {
-            // Safety: This will always result in a valid FQDN, as we control & know
-            // the values of default_hostname (one of "pve", "pmg" or "pbs") and
-            // constant-defined DEFAULT_DOMAIN.
-            Fqdn::from(&format!("{}.{}", default_hostname, Self::DEFAULT_DOMAIN)).unwrap()
-        })
-    }
-}
-
 #[derive(Clone, Debug)]
 pub struct InstallerOptions {
     pub bootdisk: BootdiskOptions,
@@ -447,11 +68,15 @@ impl InstallerOptions {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::setup::{
-        Dns, Gateway, Interface, InterfaceState, IsoInfo, IsoLocations, NetworkInfo, ProductConfig,
-        ProxmoxProduct, Routes, SetupInfo,
+    use proxmox_installer_common::{
+        setup::{
+            Dns, Gateway, Interface, InterfaceState, IsoInfo, IsoLocations, NetworkInfo, ProductConfig,
+            ProxmoxProduct, Routes, SetupInfo,
+        },
+        utils::{CidrAddress, Fqdn},
     };
     use std::{collections::HashMap, path::PathBuf};
+    use std::net::{IpAddr, Ipv4Addr};
 
     fn dummy_setup_info() -> SetupInfo {
         SetupInfo {
diff --git a/proxmox-tui-installer/src/setup.rs b/proxmox-tui-installer/src/setup.rs
index 5575759..211a96b 100644
--- a/proxmox-tui-installer/src/setup.rs
+++ b/proxmox-tui-installer/src/setup.rs
@@ -1,131 +1,20 @@
 use std::{
-    cmp,
     collections::HashMap,
     fmt,
     fs::File,
     io::BufReader,
     net::IpAddr,
-    path::{Path, PathBuf},
+    path::Path,
 };
 
-use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+use serde::{Deserialize, Serialize, Serializer};
 
-use crate::{
-    options::{
-        AdvancedBootdiskOptions, BtrfsRaidLevel, Disk, FsType, InstallerOptions,
-        ZfsBootdiskOptions, ZfsChecksumOption, ZfsCompressOption, ZfsRaidLevel,
-    },
-    utils::CidrAddress,
-};
-
-#[allow(clippy::upper_case_acronyms)]
-#[derive(Clone, Copy, Deserialize, PartialEq)]
-#[serde(rename_all = "lowercase")]
-pub enum ProxmoxProduct {
-    PVE,
-    PBS,
-    PMG,
-}
-
-impl ProxmoxProduct {
-    pub fn default_hostname(self) -> &'static str {
-        match self {
-            Self::PVE => "pve",
-            Self::PMG => "pmg",
-            Self::PBS => "pbs",
-        }
-    }
-}
-
-#[derive(Clone, Deserialize)]
-pub struct ProductConfig {
-    pub fullname: String,
-    pub product: ProxmoxProduct,
-    #[serde(deserialize_with = "deserialize_bool_from_int")]
-    pub enable_btrfs: bool,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct IsoInfo {
-    pub release: String,
-    pub isorelease: String,
-}
-
-/// Paths in the ISO environment containing installer data.
-#[derive(Clone, Deserialize)]
-pub struct IsoLocations {
-    pub iso: PathBuf,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct SetupInfo {
-    #[serde(rename = "product-cfg")]
-    pub config: ProductConfig,
-    #[serde(rename = "iso-info")]
-    pub iso_info: IsoInfo,
-    pub locations: IsoLocations,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct CountryInfo {
-    pub name: String,
-    #[serde(default)]
-    pub zone: String,
-    pub kmap: String,
-}
-
-#[derive(Clone, Deserialize, Eq, PartialEq)]
-pub struct KeyboardMapping {
-    pub name: String,
-    #[serde(rename = "kvm")]
-    pub id: String,
-    #[serde(rename = "x11")]
-    pub xkb_layout: String,
-    #[serde(rename = "x11var")]
-    pub xkb_variant: String,
-}
-
-impl cmp::PartialOrd for KeyboardMapping {
-    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-        self.name.partial_cmp(&other.name)
-    }
-}
-
-impl cmp::Ord for KeyboardMapping {
-    fn cmp(&self, other: &Self) -> cmp::Ordering {
-        self.name.cmp(&other.name)
-    }
-}
-
-#[derive(Clone, Deserialize)]
-pub struct LocaleInfo {
-    #[serde(deserialize_with = "deserialize_cczones_map")]
-    pub cczones: HashMap<String, Vec<String>>,
-    #[serde(rename = "country")]
-    pub countries: HashMap<String, CountryInfo>,
-    pub kmap: HashMap<String, KeyboardMapping>,
-}
-
-#[derive(Serialize)]
-struct InstallZfsOption {
-    ashift: usize,
-    #[serde(serialize_with = "serialize_as_display")]
-    compress: ZfsCompressOption,
-    #[serde(serialize_with = "serialize_as_display")]
-    checksum: ZfsChecksumOption,
-    copies: usize,
-}
-
-impl From<ZfsBootdiskOptions> for InstallZfsOption {
-    fn from(opts: ZfsBootdiskOptions) -> Self {
-        InstallZfsOption {
-            ashift: opts.ashift,
-            compress: opts.compress,
-            checksum: opts.checksum,
-            copies: opts.copies,
-        }
-    }
-}
+use crate::options::InstallerOptions;
+use proxmox_installer_common::{
+        options::{AdvancedBootdiskOptions, BtrfsRaidLevel, Disk, FsType, ZfsRaidLevel},
+        setup::InstallZfsOption,
+        utils::CidrAddress,
+    };
 
 /// See Proxmox::Install::Config
 #[derive(Serialize)]
@@ -247,82 +136,6 @@ pub fn read_json<T: for<'de> Deserialize<'de>, P: AsRef<Path>>(path: P) -> Resul
     serde_json::from_reader(reader).map_err(|err| format!("failed to parse JSON: {err}"))
 }
 
-fn deserialize_bool_from_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
-where
-    D: Deserializer<'de>,
-{
-    let val: u32 = Deserialize::deserialize(deserializer)?;
-    Ok(val != 0)
-}
-
-fn deserialize_cczones_map<'de, D>(
-    deserializer: D,
-) -> Result<HashMap<String, Vec<String>>, D::Error>
-where
-    D: Deserializer<'de>,
-{
-    let map: HashMap<String, HashMap<String, u32>> = Deserialize::deserialize(deserializer)?;
-
-    let mut result = HashMap::new();
-    for (cc, list) in map.into_iter() {
-        result.insert(cc, list.into_keys().collect());
-    }
-
-    Ok(result)
-}
-
-fn deserialize_disks_map<'de, D>(deserializer: D) -> Result<Vec<Disk>, D::Error>
-where
-    D: Deserializer<'de>,
-{
-    let disks =
-        <Vec<(usize, String, f64, String, Option<usize>, String)>>::deserialize(deserializer)?;
-    Ok(disks
-        .into_iter()
-        .map(
-            |(index, device, size_mb, model, logical_bsize, _syspath)| Disk {
-                index: index.to_string(),
-                // Linux always reports the size of block devices in sectors, where one sector is
-                // defined as being 2^9 = 512 bytes in size.
-                // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/blk_types.h?h=v6.4#n30
-                size: (size_mb * 512.) / 1024. / 1024. / 1024.,
-                block_size: logical_bsize,
-                path: device,
-                model: (!model.is_empty()).then_some(model),
-            },
-        )
-        .collect())
-}
-
-fn deserialize_cidr_list<'de, D>(deserializer: D) -> Result<Option<Vec<CidrAddress>>, D::Error>
-where
-    D: Deserializer<'de>,
-{
-    #[derive(Deserialize)]
-    struct CidrDescriptor {
-        address: String,
-        prefix: usize,
-        // family is implied anyway by parsing the address
-    }
-
-    let list: Vec<CidrDescriptor> = Deserialize::deserialize(deserializer)?;
-
-    let mut result = Vec::with_capacity(list.len());
-    for desc in list {
-        let ip_addr = desc
-            .address
-            .parse::<IpAddr>()
-            .map_err(|err| de::Error::custom(format!("{:?}", err)))?;
-
-        result.push(
-            CidrAddress::new(ip_addr, desc.prefix)
-                .map_err(|err| de::Error::custom(format!("{:?}", err)))?,
-        );
-    }
-
-    Ok(Some(result))
-}
-
 fn serialize_disk_opt<S>(value: &Option<Disk>, serializer: S) -> Result<S::Ok, S::Error>
 where
     S: Serializer,
@@ -366,116 +179,3 @@ where
 {
     serializer.collect_str(value)
 }
-
-#[derive(Clone, Deserialize)]
-pub struct RuntimeInfo {
-    /// Whether is system was booted in (legacy) BIOS or UEFI mode.
-    pub boot_type: BootType,
-
-    /// Detected country if available.
-    pub country: Option<String>,
-
-    /// Maps devices to their information.
-    #[serde(deserialize_with = "deserialize_disks_map")]
-    pub disks: Vec<Disk>,
-
-    /// Network addresses, gateways and DNS info.
-    pub network: NetworkInfo,
-
-    /// Total memory of the system in MiB.
-    pub total_memory: usize,
-
-    /// Whether the CPU supports hardware-accelerated virtualization
-    #[serde(deserialize_with = "deserialize_bool_from_int")]
-    pub hvm_supported: bool,
-}
-
-#[derive(Copy, Clone, Eq, Deserialize, PartialEq)]
-#[serde(rename_all = "lowercase")]
-pub enum BootType {
-    Bios,
-    Efi,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct NetworkInfo {
-    pub dns: Dns,
-    pub routes: Option<Routes>,
-
-    /// Maps devices to their configuration, if it has a usable configuration.
-    /// (Contains no entries for devices with only link-local addresses.)
-    #[serde(default)]
-    pub interfaces: HashMap<String, Interface>,
-
-    /// The hostname of this machine, if set by the DHCP server.
-    pub hostname: Option<String>,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct Dns {
-    pub domain: Option<String>,
-
-    /// List of stringified IP addresses.
-    #[serde(default)]
-    pub dns: Vec<IpAddr>,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct Routes {
-    /// Ipv4 gateway.
-    pub gateway4: Option<Gateway>,
-
-    /// Ipv6 gateway.
-    pub gateway6: Option<Gateway>,
-}
-
-#[derive(Clone, Deserialize)]
-pub struct Gateway {
-    /// Outgoing network device.
-    pub dev: String,
-
-    /// Stringified gateway IP address.
-    pub gateway: IpAddr,
-}
-
-#[derive(Clone, Deserialize)]
-#[serde(rename_all = "UPPERCASE")]
-pub enum InterfaceState {
-    Up,
-    Down,
-    #[serde(other)]
-    Unknown,
-}
-
-impl InterfaceState {
-    // avoid display trait as this is not the string representation for a serializer
-    pub fn render(&self) -> String {
-        match self {
-            Self::Up => "\u{25CF}",
-            Self::Down | Self::Unknown => " ",
-        }
-        .into()
-    }
-}
-
-#[derive(Clone, Deserialize)]
-pub struct Interface {
-    pub name: String,
-
-    pub index: usize,
-
-    pub mac: String,
-
-    pub state: InterfaceState,
-
-    #[serde(default)]
-    #[serde(deserialize_with = "deserialize_cidr_list")]
-    pub addresses: Option<Vec<CidrAddress>>,
-}
-
-impl Interface {
-    // avoid display trait as this is not the string representation for a serializer
-    pub fn render(&self) -> String {
-        format!("{} {}", self.state.render(), self.name)
-    }
-}
diff --git a/proxmox-tui-installer/src/system.rs b/proxmox-tui-installer/src/system.rs
index bbf13b8..d1675a9 100644
--- a/proxmox-tui-installer/src/system.rs
+++ b/proxmox-tui-installer/src/system.rs
@@ -1,6 +1,6 @@
 use std::{fs::OpenOptions, io::Write, process::Command};
 
-use crate::setup::KeyboardMapping;
+use proxmox_installer_common::setup::KeyboardMapping;
 
 pub fn set_keyboard_layout(kmap: &KeyboardMapping) -> Result<(), String> {
     Command::new("setxkbmap")
diff --git a/proxmox-tui-installer/src/views/bootdisk.rs b/proxmox-tui-installer/src/views/bootdisk.rs
index ba08c8b..38a6521 100644
--- a/proxmox-tui-installer/src/views/bootdisk.rs
+++ b/proxmox-tui-installer/src/views/bootdisk.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, collections::HashSet, marker::PhantomData, rc::Rc};
+use std::{cell::RefCell, marker::PhantomData, rc::Rc};
 
 use cursive::{
     view::{Nameable, Resizable, ViewWrapper},
@@ -10,15 +10,18 @@ use cursive::{
 };
 
 use super::{DiskSizeEditView, FormView, IntegerEditView};
-use crate::{
+use crate::options::FS_TYPES;
+use crate::InstallerState;
+
+use proxmox_installer_common::{
     options::{
-        AdvancedBootdiskOptions, BootdiskOptions, BtrfsBootdiskOptions, BtrfsRaidLevel, Disk,
-        FsType, LvmBootdiskOptions, ZfsBootdiskOptions, ZfsRaidLevel, FS_TYPES,
+        AdvancedBootdiskOptions, BootdiskOptions, BtrfsBootdiskOptions, Disk,
+        FsType, LvmBootdiskOptions, ZfsBootdiskOptions,
         ZFS_CHECKSUM_OPTIONS, ZFS_COMPRESS_OPTIONS,
     },
-    setup::{BootType, ProductConfig},
+    setup::{BootType, ProductConfig, ProxmoxProduct},
+    disk_checks::{check_btrfs_raid_config, check_for_duplicate_disks, check_disks_4kn_legacy_boot, check_zfs_raid_config},
 };
-use crate::{setup::ProxmoxProduct, InstallerState};
 
 pub struct BootdiskOptionsView {
     view: LinearLayout,
@@ -619,236 +622,3 @@ fn advanced_options_view(
     .with_name("advanced-bootdisk-options-dialog")
     .max_size((120, 40))
 }
-
-/// Checks a list of disks for duplicate entries, using their index as key.
-///
-/// # Arguments
-///
-/// * `disks` - A list of disks to check for duplicates.
-fn check_for_duplicate_disks(disks: &[Disk]) -> Result<(), &Disk> {
-    let mut set = HashSet::new();
-
-    for disk in disks {
-        if !set.insert(&disk.index) {
-            return Err(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 all disks for legacy BIOS boot compatibility and reports an error as appropriate. 4Kn
-/// disks are generally broken with legacy BIOS and cannot be booted from.
-///
-/// # Arguments
-///
-/// * `runinfo` - `RuntimeInfo` instance of currently running system
-/// * `disks` - List of disks designated as bootdisk targets.
-fn check_disks_4kn_legacy_boot(boot_type: BootType, disks: &[Disk]) -> Result<(), &str> {
-    let is_blocksize_4096 = |disk: &Disk| disk.block_size.map(|s| s == 4096).unwrap_or(false);
-
-    if boot_type == BootType::Bios && disks.iter().any(is_blocksize_4096) {
-        return Err("Booting from 4Kn drive in legacy BIOS mode is not supported.");
-    }
-
-    Ok(())
-}
-
-/// Checks whether a user-supplied ZFS RAID setup is valid or not, such as disk sizes andminimum
-/// number of disks.
-///
-/// # Arguments
-///
-/// * `level` - The targeted ZFS RAID level by the user.
-/// * `disks` - List of disks designated as RAID targets.
-fn check_zfs_raid_config(level: ZfsRaidLevel, disks: &[Disk]) -> Result<(), String> {
-    // See also Proxmox/Install.pm:get_zfs_raid_setup()
-
-    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(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    fn dummy_disk(index: usize) -> Disk {
-        Disk {
-            index: index.to_string(),
-            path: format!("/dev/dummy{index}"),
-            model: Some("Dummy disk".to_owned()),
-            size: 1024. * 1024. * 1024. * 8.,
-            block_size: Some(512),
-        }
-    }
-
-    fn dummy_disks(num: usize) -> Vec<Disk> {
-        (0..num).map(dummy_disk).collect()
-    }
-
-    #[test]
-    fn duplicate_disks() {
-        assert!(check_for_duplicate_disks(&dummy_disks(2)).is_ok());
-        assert_eq!(
-            check_for_duplicate_disks(&[
-                dummy_disk(0),
-                dummy_disk(1),
-                dummy_disk(2),
-                dummy_disk(2),
-                dummy_disk(3),
-            ]),
-            Err(&dummy_disk(2)),
-        );
-    }
-
-    #[test]
-    fn raid_min_disks() {
-        let disks = dummy_disks(10);
-
-        assert!(check_raid_min_disks(&disks[..1], 2).is_err());
-        assert!(check_raid_min_disks(&disks[..1], 1).is_ok());
-        assert!(check_raid_min_disks(&disks, 1).is_ok());
-    }
-
-    #[test]
-    fn bios_boot_compat_4kn() {
-        for i in 0..10 {
-            let mut disks = dummy_disks(10);
-            disks[i].block_size = Some(4096);
-
-            // Must fail if /any/ of the disks are 4Kn
-            assert!(check_disks_4kn_legacy_boot(BootType::Bios, &disks).is_err());
-            // For UEFI, we allow it for every configuration
-            assert!(check_disks_4kn_legacy_boot(BootType::Efi, &disks).is_ok());
-        }
-    }
-
-    #[test]
-    fn btrfs_raid() {
-        let disks = dummy_disks(10);
-
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid0, &[]).is_err());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid0, &disks[..1]).is_ok());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid0, &disks).is_ok());
-
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid1, &[]).is_err());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid1, &disks[..1]).is_err());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid1, &disks[..2]).is_ok());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid1, &disks).is_ok());
-
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid10, &[]).is_err());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid10, &disks[..3]).is_err());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid10, &disks[..4]).is_ok());
-        assert!(check_btrfs_raid_config(BtrfsRaidLevel::Raid10, &disks).is_ok());
-    }
-
-    #[test]
-    fn zfs_raid() {
-        let disks = dummy_disks(10);
-
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid0, &[]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid0, &disks[..1]).is_ok());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid0, &disks).is_ok());
-
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid1, &[]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid1, &disks[..2]).is_ok());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid1, &disks).is_ok());
-
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid10, &[]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid10, &dummy_disks(4)).is_ok());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::Raid10, &disks).is_ok());
-
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ, &[]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ, &disks[..2]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ, &disks[..3]).is_ok());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ, &disks).is_ok());
-
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ2, &[]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ2, &disks[..3]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ2, &disks[..4]).is_ok());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ2, &disks).is_ok());
-
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ3, &[]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ3, &disks[..4]).is_err());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ3, &disks[..5]).is_ok());
-        assert!(check_zfs_raid_config(ZfsRaidLevel::RaidZ3, &disks).is_ok());
-    }
-}
diff --git a/proxmox-tui-installer/src/views/mod.rs b/proxmox-tui-installer/src/views/mod.rs
index aa24fa4..aabae0e 100644
--- a/proxmox-tui-installer/src/views/mod.rs
+++ b/proxmox-tui-installer/src/views/mod.rs
@@ -7,7 +7,7 @@ use cursive::{
     Rect, Vec2, View,
 };
 
-use crate::utils::CidrAddress;
+use proxmox_installer_common::utils::CidrAddress;
 
 mod bootdisk;
 pub use bootdisk::*;
diff --git a/proxmox-tui-installer/src/views/timezone.rs b/proxmox-tui-installer/src/views/timezone.rs
index 6732286..77fbb10 100644
--- a/proxmox-tui-installer/src/views/timezone.rs
+++ b/proxmox-tui-installer/src/views/timezone.rs
@@ -6,9 +6,11 @@ use cursive::{
 
 use super::FormView;
 use crate::{
+    system, InstallerState,
+};
+use proxmox_installer_common::{
     options::TimezoneOptions,
     setup::{KeyboardMapping, LocaleInfo},
-    system, InstallerState,
 };
 
 pub struct TimezoneOptionsView {
-- 
2.39.2





  parent reply	other threads:[~2023-10-25 16:00 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-25 15:59 [pve-devel] [PATCH 00/12] installer: add crate for common code Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 01/12] add proxmox-installer-common crate Aaron Lauterer
2023-10-27 10:59   ` Christoph Heiss
2023-10-25 16:00 ` [pve-devel] [PATCH 02/12] common: copy common code from tui-installer Aaron Lauterer
2023-10-27 11:14   ` Christoph Heiss
2023-10-25 16:00 ` [pve-devel] [PATCH 03/12] common: utils: add dependency for doc test Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 04/12] common: make InstallZfsOption public Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 05/12] common: disk_checks: make functions public Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 06/12] tui-installer: add dependency for new common crate Aaron Lauterer
2023-10-25 16:00 ` Aaron Lauterer [this message]
2023-10-25 16:00 ` [pve-devel] [PATCH 08/12] tui: remove now unused utils.rs Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 09/12] common: add installer_setup method Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 10/12] common: document " Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 11/12] tui: use installer_setup from common cate Aaron Lauterer
2023-10-25 16:00 ` [pve-devel] [PATCH 12/12] tui: remove unused read_json function Aaron Lauterer
2023-10-27 11:06   ` Christoph Heiss
2023-10-27 11:39 ` [pve-devel] [PATCH installer] buildsys: copy over `proxmox-installer-common` crate to build directory Christoph Heiss
2023-10-27 11:41 ` [pve-devel] [PATCH 00/12] installer: add crate for common code Christoph Heiss
2023-10-30  9:45   ` Aaron Lauterer
2023-11-02 19:05 ` [pve-devel] applied-series: " Thomas Lamprecht

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=20231025160011.3617524-8-a.lauterer@proxmox.com \
    --to=a.lauterer@proxmox.com \
    --cc=pve-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.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal