[pve-devel] [PATCH 07/12] tui: switch to common crate
Aaron Lauterer
a.lauterer at proxmox.com
Wed Oct 25 18:00:06 CEST 2023
by switching dependencies and deleting doubled code to avoid ambiguities
within the same scope.
Signed-off-by: Aaron Lauterer <a.lauterer at 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 at 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
More information about the pve-devel
mailing list