From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 7CE051FF16F for ; Tue, 14 Oct 2025 15:23:21 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B6DD3418B; Tue, 14 Oct 2025 15:22:55 +0200 (CEST) From: Christoph Heiss To: pve-devel@lists.proxmox.com Date: Tue, 14 Oct 2025 15:21:56 +0200 Message-ID: <20251014132207.1171073-12-c.heiss@proxmox.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251014132207.1171073-1-c.heiss@proxmox.com> References: <20251014132207.1171073-1-c.heiss@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1760448096058 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.038 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 Subject: [pve-devel] [PATCH installer 11/14] tui: views: move network options view to own module 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: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" In preparation for adding network interface name pinning, which will introduce quite a bit more functionality. No functional changes. Signed-off-by: Christoph Heiss --- proxmox-tui-installer/src/main.rs | 90 +--------------- proxmox-tui-installer/src/views/mod.rs | 3 + proxmox-tui-installer/src/views/network.rs | 113 +++++++++++++++++++++ 3 files changed, 121 insertions(+), 85 deletions(-) create mode 100644 proxmox-tui-installer/src/views/network.rs diff --git a/proxmox-tui-installer/src/main.rs b/proxmox-tui-installer/src/main.rs index 15ee5d3..b24f90b 100644 --- a/proxmox-tui-installer/src/main.rs +++ b/proxmox-tui-installer/src/main.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_code)] -use std::{collections::HashMap, env, net::IpAddr}; +use std::{collections::HashMap, env}; use cursive::{ Cursive, CursiveRunnable, ScreenId, View, XY, @@ -9,7 +9,7 @@ use cursive::{ view::{Nameable, Offset, Resizable, ViewWrapper}, views::{ Button, Checkbox, Dialog, DummyView, EditView, Layer, LinearLayout, PaddedView, Panel, - ResizedView, ScrollView, SelectView, StackView, TextView, + ResizedView, ScrollView, StackView, TextView, }, }; @@ -20,7 +20,6 @@ use proxmox_installer_common::{ ROOT_PASSWORD_MIN_LENGTH, options::{BootdiskOptions, NetworkOptions, TimezoneOptions, email_validate}, setup::{LocaleInfo, ProxmoxProduct, RuntimeInfo, SetupInfo, installer_setup}, - utils::{CidrAddress, Fqdn}, }; mod setup; @@ -28,7 +27,7 @@ mod system; mod views; use views::{ - BootdiskOptionsView, CidrAddressEditView, FormView, InstallProgressView, TableView, + BootdiskOptionsView, FormView, InstallProgressView, NetworkOptionsView, TableView, TableViewItem, TimezoneOptionsView, }; @@ -483,91 +482,12 @@ fn password_dialog(siv: &mut Cursive) -> InstallerView { fn network_dialog(siv: &mut Cursive) -> InstallerView { let state = siv.user_data::().unwrap(); let options = &state.options.network; - let ifaces = state.runtime_info.network.interfaces.values(); - let ifnames = ifaces - .clone() - .map(|iface| (iface.render(), iface.name.clone())); - let mut ifaces_selection = SelectView::new().popup().with_all(ifnames.clone()); - - // sort first to always have stable view - ifaces_selection.sort(); - let selected = ifaces_selection - .iter() - .position(|(_label, iface)| *iface == options.ifname) - .unwrap_or(ifaces.len() - 1); - - ifaces_selection.set_selection(selected); - - let inner = FormView::new() - .child("Management interface", ifaces_selection) - .child( - "Hostname (FQDN)", - EditView::new().content(options.fqdn.to_string()), - ) - .child( - "IP address (CIDR)", - CidrAddressEditView::new().content(options.address.clone()), - ) - .child( - "Gateway address", - EditView::new().content(options.gateway.to_string()), - ) - .child( - "DNS server address", - EditView::new().content(options.dns_server.to_string()), - ) - .with_name("network-options"); InstallerView::new( state, - inner, + NetworkOptionsView::new(options, &state.runtime_info.network).with_name("network-options"), Box::new(|siv| { - let options = siv.call_on_name("network-options", |view: &mut FormView| { - let ifname = view - .get_value::(0) - .ok_or("failed to retrieve management interface name")?; - - let fqdn = view - .get_value::(1) - .ok_or("failed to retrieve host FQDN")? - .parse::() - .map_err(|err| format!("hostname does not look valid:\n\n{err}"))?; - - let address = view - .get_value::(2) - .ok_or("failed to retrieve host address".to_string()) - .and_then(|(ip_addr, mask)| { - CidrAddress::new(ip_addr, mask).map_err(|err| err.to_string()) - })?; - - let gateway = view - .get_value::(3) - .ok_or("failed to retrieve gateway address")? - .parse::() - .map_err(|err| err.to_string())?; - - let dns_server = view - .get_value::(4) - .ok_or("failed to retrieve DNS server address")? - .parse::() - .map_err(|err| err.to_string())?; - - if address.addr().is_ipv4() != gateway.is_ipv4() { - Err("host and gateway IP address version must not differ".to_owned()) - } else if address.addr().is_ipv4() != dns_server.is_ipv4() { - Err("host and DNS IP address version must not differ".to_owned()) - } else if fqdn.to_string().ends_with(".invalid") { - Err("hostname does not look valid".to_owned()) - } else { - Ok(NetworkOptions { - ifname, - fqdn, - address, - gateway, - dns_server, - }) - } - }); + let options = siv.call_on_name("network-options", NetworkOptionsView::get_values); match options { Some(Ok(options)) => { diff --git a/proxmox-tui-installer/src/views/mod.rs b/proxmox-tui-installer/src/views/mod.rs index 537e3ed..43ca999 100644 --- a/proxmox-tui-installer/src/views/mod.rs +++ b/proxmox-tui-installer/src/views/mod.rs @@ -16,6 +16,9 @@ pub use bootdisk::*; mod install_progress; pub use install_progress::*; +mod network; +pub use network::*; + mod tabbed_view; pub use tabbed_view::*; diff --git a/proxmox-tui-installer/src/views/network.rs b/proxmox-tui-installer/src/views/network.rs new file mode 100644 index 0000000..5e3e258 --- /dev/null +++ b/proxmox-tui-installer/src/views/network.rs @@ -0,0 +1,113 @@ +use std::net::IpAddr; + +use cursive::{ + view::ViewWrapper, + views::{EditView, SelectView}, +}; + +use super::{CidrAddressEditView, FormView}; +use proxmox_installer_common::{ + options::NetworkOptions, + setup::NetworkInfo, + utils::{CidrAddress, Fqdn}, +}; + +pub struct NetworkOptionsView { + view: FormView, +} + +impl NetworkOptionsView { + pub fn new(options: &NetworkOptions, network_info: &NetworkInfo) -> Self { + let ifaces = network_info.interfaces.values(); + let ifnames = ifaces + .clone() + .map(|iface| (iface.render(), iface.name.clone())); + let mut ifaces_selection = SelectView::new().popup().with_all(ifnames.clone()); + + // sort first to always have stable view + ifaces_selection.sort(); + let selected = ifaces_selection + .iter() + .position(|(_label, iface)| *iface == options.ifname) + .unwrap_or(ifaces.len() - 1); + + ifaces_selection.set_selection(selected); + + let view = FormView::new() + .child("Management interface", ifaces_selection) + .child( + "Hostname (FQDN)", + EditView::new().content(options.fqdn.to_string()), + ) + .child( + "IP address (CIDR)", + CidrAddressEditView::new().content(options.address.clone()), + ) + .child( + "Gateway address", + EditView::new().content(options.gateway.to_string()), + ) + .child( + "DNS server address", + EditView::new().content(options.dns_server.to_string()), + ); + + Self { view } + } + + pub fn get_values(&mut self) -> Result { + let ifname = self + .view + .get_value::(0) + .ok_or("failed to retrieve management interface name")?; + + let fqdn = self + .view + .get_value::(1) + .ok_or("failed to retrieve host FQDN")? + .parse::() + .map_err(|err| format!("hostname does not look valid:\n\n{err}"))?; + + let address = self + .view + .get_value::(2) + .ok_or("failed to retrieve host address".to_string()) + .and_then(|(ip_addr, mask)| { + CidrAddress::new(ip_addr, mask).map_err(|err| err.to_string()) + })?; + + let gateway = self + .view + .get_value::(3) + .ok_or("failed to retrieve gateway address")? + .parse::() + .map_err(|err| err.to_string())?; + + let dns_server = self + .view + .get_value::(4) + .ok_or("failed to retrieve DNS server address")? + .parse::() + .map_err(|err| err.to_string())?; + + if address.addr().is_ipv4() != gateway.is_ipv4() { + Err("host and gateway IP address version must not differ".to_owned()) + } else if address.addr().is_ipv4() != dns_server.is_ipv4() { + Err("host and DNS IP address version must not differ".to_owned()) + } else if fqdn.to_string().ends_with(".invalid") { + Err("hostname does not look valid".to_owned()) + } else { + Ok(NetworkOptions { + ifname, + fqdn, + address, + gateway, + dns_server, + }) + } + } +} + +impl ViewWrapper for NetworkOptionsView { + cursive::wrap_impl!(self.view: FormView); +} -- 2.51.0 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel