]> git.proxmox.com Git - pve-installer.git/blobdiff - proxmox-tui-installer/src/options.rs
bump version to 8.2.6
[pve-installer.git] / proxmox-tui-installer / src / options.rs
index de2d97e670ddbbbc780c69eb59aa72ea6c3e4927..73fbf2ab1f50557338cfcd443a8b314ce64adb0e 100644 (file)
-use crate::{utils::CidrAddress, SummaryOption};
-use std::{
-    fmt,
-    net::{IpAddr, Ipv4Addr},
+use crate::SummaryOption;
+
+use proxmox_installer_common::{
+    options::{
+        BootdiskOptions, BtrfsRaidLevel, FsType, NetworkOptions, PasswordOptions, TimezoneOptions,
+        ZfsRaidLevel,
+    },
+    setup::LocaleInfo,
 };
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum ZfsRaidLevel {
-    Single,
-    Mirror,
-    Raid10,
-    RaidZ,
-    RaidZ2,
-    RaidZ3,
-}
-
-impl fmt::Display for ZfsRaidLevel {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use ZfsRaidLevel::*;
-        match self {
-            Single => write!(f, "single disk"),
-            Mirror => write!(f, "mirrored"),
-            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),
-}
-
-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})"),
-        }
-    }
-}
-
 pub const FS_TYPES: &[FsType] = {
     use FsType::*;
     &[
         Ext4,
         Xfs,
-        Zfs(ZfsRaidLevel::Single),
-        Zfs(ZfsRaidLevel::Mirror),
+        Zfs(ZfsRaidLevel::Raid0),
+        Zfs(ZfsRaidLevel::Raid1),
         Zfs(ZfsRaidLevel::Raid10),
         Zfs(ZfsRaidLevel::RaidZ),
         Zfs(ZfsRaidLevel::RaidZ2),
         Zfs(ZfsRaidLevel::RaidZ3),
+        Btrfs(BtrfsRaidLevel::Raid0),
+        Btrfs(BtrfsRaidLevel::Raid1),
+        Btrfs(BtrfsRaidLevel::Raid10),
     ]
 };
 
-#[derive(Clone, Debug)]
-pub struct LvmBootdiskOptions {
-    pub total_size: u64,
-    pub swap_size: u64,
-    pub max_root_size: u64,
-    pub max_data_size: u64,
-    pub min_lvm_free: u64,
-}
-
-impl LvmBootdiskOptions {
-    pub fn defaults_from(disk: &Disk) -> Self {
-        let min_lvm_free = if disk.size > 128 * 1024 * 1024 {
-            16 * 1024 * 1024
-        } else {
-            disk.size / 8
-        };
-
-        Self {
-            total_size: disk.size,
-            swap_size: 4 * 1024 * 1024, // TODO: value from installed memory
-            max_root_size: 0,
-            max_data_size: 0,
-            min_lvm_free,
-        }
-    }
-}
-
-#[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: u64,
-}
-
-impl ZfsBootdiskOptions {
-    pub fn defaults_from(disk: &Disk) -> Self {
-        Self {
-            ashift: 12,
-            compress: ZfsCompressOption::default(),
-            checksum: ZfsChecksumOption::default(),
-            copies: 1,
-            disk_size: disk.size,
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum AdvancedBootdiskOptions {
-    Lvm(LvmBootdiskOptions),
-    Zfs(ZfsBootdiskOptions),
-}
-
-#[derive(Clone, Debug)]
-pub struct Disk {
-    pub path: String,
-    pub size: u64,
-}
-
-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
-        write!(
-            f,
-            "{} ({} GiB)",
-            self.path,
-            (self.size as f64) / 1024. / 1024. / 1024.
-        )
-    }
-}
-
-impl From<&Disk> for String {
-    fn from(value: &Disk) -> Self {
-        value.to_string()
-    }
-}
-
-#[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 timezone: String,
-    pub kb_layout: String,
-}
-
-impl Default for TimezoneOptions {
-    fn default() -> Self {
-        Self {
-            timezone: "Europe/Vienna".to_owned(),
-            kb_layout: "en_US".to_owned(),
-        }
-    }
-}
-
-#[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_owned(),
-            root_password: String::new(),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct NetworkOptions {
-    pub ifname: String,
-    pub fqdn: String,
-    pub address: CidrAddress,
-    pub gateway: IpAddr,
-    pub dns_server: IpAddr,
-}
-
-impl Default for NetworkOptions {
-    fn default() -> Self {
-        // TODO: Retrieve automatically
-        Self {
-            ifname: String::new(),
-            fqdn: "pve.example.invalid".to_owned(),
-            // Safety: The provided mask will always be valid.
-            address: CidrAddress::new(Ipv4Addr::UNSPECIFIED, 0).unwrap(),
-            gateway: Ipv4Addr::UNSPECIFIED.into(),
-            dns_server: Ipv4Addr::UNSPECIFIED.into(),
-        }
-    }
-}
-
 #[derive(Clone, Debug)]
 pub struct InstallerOptions {
     pub bootdisk: BootdiskOptions,
     pub timezone: TimezoneOptions,
     pub password: PasswordOptions,
     pub network: NetworkOptions,
+    pub autoreboot: bool,
 }
 
 impl InstallerOptions {
-    pub fn to_summary(&self) -> Vec<SummaryOption> {
+    pub fn to_summary(&self, locales: &LocaleInfo) -> Vec<SummaryOption> {
+        let kb_layout = locales
+            .kmap
+            .get(&self.timezone.kb_layout)
+            .map(|l| &l.name)
+            .unwrap_or(&self.timezone.kb_layout);
+
         vec![
             SummaryOption::new("Bootdisk filesystem", self.bootdisk.fstype.to_string()),
             SummaryOption::new(
@@ -287,13 +54,126 @@ impl InstallerOptions {
                     .join(", "),
             ),
             SummaryOption::new("Timezone", &self.timezone.timezone),
-            SummaryOption::new("Keyboard layout", &self.timezone.kb_layout),
-            SummaryOption::new("Administator email", &self.password.email),
+            SummaryOption::new("Keyboard layout", kb_layout),
+            SummaryOption::new("Administrator email", &self.password.email),
             SummaryOption::new("Management interface", &self.network.ifname),
-            SummaryOption::new("Hostname", &self.network.fqdn),
+            SummaryOption::new("Hostname", self.network.fqdn.to_string()),
             SummaryOption::new("Host IP (CIDR)", self.network.address.to_string()),
             SummaryOption::new("Gateway", self.network.gateway.to_string()),
             SummaryOption::new("DNS", self.network.dns_server.to_string()),
         ]
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proxmox_installer_common::{
+        setup::{
+            Dns, Gateway, Interface, InterfaceState, IsoInfo, IsoLocations, NetworkInfo,
+            ProductConfig, ProxmoxProduct, Routes, SetupInfo,
+        },
+        utils::{CidrAddress, Fqdn},
+    };
+    use std::net::{IpAddr, Ipv4Addr};
+    use std::{collections::BTreeMap, path::PathBuf};
+
+    fn dummy_setup_info() -> SetupInfo {
+        SetupInfo {
+            config: ProductConfig {
+                fullname: "Proxmox VE".to_owned(),
+                product: ProxmoxProduct::PVE,
+                enable_btrfs: true,
+            },
+            iso_info: IsoInfo {
+                release: String::new(),
+                isorelease: String::new(),
+            },
+            locations: IsoLocations {
+                iso: PathBuf::new(),
+            },
+        }
+    }
+
+    #[test]
+    fn network_options_from_setup_network_info() {
+        let setup = dummy_setup_info();
+
+        let mut interfaces = BTreeMap::new();
+        interfaces.insert(
+            "eth0".to_owned(),
+            Interface {
+                name: "eth0".to_owned(),
+                index: 0,
+                state: InterfaceState::Up,
+                mac: "01:23:45:67:89:ab".to_owned(),
+                addresses: Some(vec![
+                    CidrAddress::new(Ipv4Addr::new(192, 168, 0, 2), 24).unwrap()
+                ]),
+            },
+        );
+
+        let mut info = NetworkInfo {
+            dns: Dns {
+                domain: Some("bar.com".to_owned()),
+                dns: Vec::new(),
+            },
+            routes: Some(Routes {
+                gateway4: Some(Gateway {
+                    dev: "eth0".to_owned(),
+                    gateway: IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
+                }),
+                gateway6: None,
+            }),
+            interfaces,
+            hostname: Some("foo".to_owned()),
+        };
+
+        assert_eq!(
+            NetworkOptions::defaults_from(&setup, &info),
+            NetworkOptions {
+                ifname: "eth0".to_owned(),
+                fqdn: Fqdn::from("foo.bar.com").unwrap(),
+                address: CidrAddress::new(Ipv4Addr::new(192, 168, 0, 2), 24).unwrap(),
+                gateway: IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
+                dns_server: Ipv4Addr::UNSPECIFIED.into(),
+            }
+        );
+
+        info.hostname = None;
+        assert_eq!(
+            NetworkOptions::defaults_from(&setup, &info),
+            NetworkOptions {
+                ifname: "eth0".to_owned(),
+                fqdn: Fqdn::from("pve.bar.com").unwrap(),
+                address: CidrAddress::new(Ipv4Addr::new(192, 168, 0, 2), 24).unwrap(),
+                gateway: IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
+                dns_server: Ipv4Addr::UNSPECIFIED.into(),
+            }
+        );
+
+        info.dns.domain = None;
+        assert_eq!(
+            NetworkOptions::defaults_from(&setup, &info),
+            NetworkOptions {
+                ifname: "eth0".to_owned(),
+                fqdn: Fqdn::from("pve.example.invalid").unwrap(),
+                address: CidrAddress::new(Ipv4Addr::new(192, 168, 0, 2), 24).unwrap(),
+                gateway: IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
+                dns_server: Ipv4Addr::UNSPECIFIED.into(),
+            }
+        );
+
+        info.hostname = Some("foo".to_owned());
+        assert_eq!(
+            NetworkOptions::defaults_from(&setup, &info),
+            NetworkOptions {
+                ifname: "eth0".to_owned(),
+                fqdn: Fqdn::from("foo.example.invalid").unwrap(),
+                address: CidrAddress::new(Ipv4Addr::new(192, 168, 0, 2), 24).unwrap(),
+                gateway: IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)),
+                dns_server: Ipv4Addr::UNSPECIFIED.into(),
+            }
+        );
+    }
+}