-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(
.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(),
+ }
+ );
+ }
+}