From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 93D4297438 for ; Tue, 16 Apr 2024 17:38:18 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7473A30094 for ; Tue, 16 Apr 2024 17:37:48 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 16 Apr 2024 17:37:46 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 0459945B64 for ; Tue, 16 Apr 2024 17:33:34 +0200 (CEST) From: Aaron Lauterer To: pve-devel@lists.proxmox.com Date: Tue, 16 Apr 2024 17:33:10 +0200 Message-Id: <20240416153325.1154224-22-a.lauterer@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240416153325.1154224-1-a.lauterer@proxmox.com> References: <20240416153325.1154224-1-a.lauterer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.048 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 v5 21/36] auto-installer: fetch: add gathering of system identifiers and restructure code 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: , X-List-Received-Date: Tue, 16 Apr 2024 15:38:18 -0000 They will be used as payload when POSTing a request for an answer file. The idea is, that with this information, it should be possible to identify the system and generate a matching answer file on the fly. Many of these properties can also be found on the machine or packaging of the machine and could therefore be scanned into a database. Identifiers are the following properties from `dmidecode` sections 1, 2, and 3: * Asset Tag * Product Name * Serial Number * SKU Number * UUID As well as a list of the MAC addresses of all the NICs and the product type: pve, pmg, pbs. Since we now have more than a simple utils.rs module in the fetch plugins, it, and the additional fetch plugin utilities are placed in their own directory. Signed-off-by: Aaron Lauterer --- .../src/fetch_plugins/mod.rs | 2 +- .../fetch_plugins/{utils.rs => utils/mod.rs} | 43 ++++++++-- .../src/fetch_plugins/utils/sysinfo.rs | 81 +++++++++++++++++++ 3 files changed, 119 insertions(+), 7 deletions(-) rename proxmox-auto-installer/src/fetch_plugins/{utils.rs => utils/mod.rs} (72%) create mode 100644 proxmox-auto-installer/src/fetch_plugins/utils/sysinfo.rs diff --git a/proxmox-auto-installer/src/fetch_plugins/mod.rs b/proxmox-auto-installer/src/fetch_plugins/mod.rs index 11d6937..6f1e8a2 100644 --- a/proxmox-auto-installer/src/fetch_plugins/mod.rs +++ b/proxmox-auto-installer/src/fetch_plugins/mod.rs @@ -1,2 +1,2 @@ pub mod partition; -mod utils; +pub mod utils; diff --git a/proxmox-auto-installer/src/fetch_plugins/utils.rs b/proxmox-auto-installer/src/fetch_plugins/utils/mod.rs similarity index 72% rename from proxmox-auto-installer/src/fetch_plugins/utils.rs rename to proxmox-auto-installer/src/fetch_plugins/utils/mod.rs index f2a8e74..b3e9dad 100644 --- a/proxmox-auto-installer/src/fetch_plugins/utils.rs +++ b/proxmox-auto-installer/src/fetch_plugins/utils/mod.rs @@ -1,5 +1,7 @@ -use anyhow::{bail, Error, Result}; +use anyhow::{Error, Result}; use log::{info, warn}; +use serde::Deserialize; +use serde_json; use std::{ fs::{self, create_dir_all}, path::{Path, PathBuf}, @@ -10,6 +12,8 @@ static ANSWER_MP: &str = "/mnt/answer"; static PARTLABEL: &str = "proxmoxinst"; static SEARCH_PATH: &str = "/dev/disk/by-label"; +pub mod sysinfo; + /// Searches for upper and lower case existence of the partlabel in the search_path /// /// # Arguemnts @@ -20,10 +24,10 @@ pub fn scan_partlabels(partlabel_source: &str, search_path: &str) -> Result { - info!("Found partition with label '{partlabel}'"); + info!("Found partition with label '{}'", partlabel); return Ok(path); } - Ok(false) => info!("Did not detect partition with label '{partlabel}'"), + Ok(false) => info!("Did not detect partition with label '{}'", partlabel), Err(err) => info!("Encountered issue, accessing '{}': {}", path.display(), err), } @@ -31,13 +35,15 @@ pub fn scan_partlabels(partlabel_source: &str, search_path: &str) -> Result { - info!("Found partition with label '{partlabel}'"); + info!("Found partition with label '{}'", partlabel); return Ok(path); } - Ok(false) => info!("Did not detect partition with label '{partlabel}'"), + Ok(false) => info!("Did not detect partition with label '{}'", partlabel), Err(err) => info!("Encountered issue, accessing '{}': {}", path.display(), err), } - bail!("Could not detect upper or lower case labels for '{partlabel_source}'"); + Err(Error::msg(format!( + "Could not detect upper or lower case labels for '{partlabel_source}'" + ))) } /// Will search and mount a partition/FS labeled proxmoxinst in lower or uppercase to ANSWER_MP; @@ -79,3 +85,28 @@ fn check_if_mounted(target_path: &str) -> Result { } Ok(false) } + +#[derive(Deserialize, Debug)] +struct IpLinksUdevInfo { + ifname: String, +} + +/// Returns vec of usable NICs +pub fn get_nic_list() -> Result> { + let ip_output = Command::new("/usr/sbin/ip") + .arg("-j") + .arg("link") + .output()?; + let parsed_links: Vec = + serde_json::from_str(String::from_utf8(ip_output.stdout)?.as_str())?; + let mut links: Vec = Vec::new(); + + for link in parsed_links { + if link.ifname == *"lo" { + continue; + } + links.push(link.ifname); + } + + Ok(links) +} diff --git a/proxmox-auto-installer/src/fetch_plugins/utils/sysinfo.rs b/proxmox-auto-installer/src/fetch_plugins/utils/sysinfo.rs new file mode 100644 index 0000000..8c57283 --- /dev/null +++ b/proxmox-auto-installer/src/fetch_plugins/utils/sysinfo.rs @@ -0,0 +1,81 @@ +use anyhow::{bail, Result}; +use proxmox_installer_common::setup::SetupInfo; +use serde::Serialize; +use std::{collections::HashMap, fs, io, path::Path}; + +use super::get_nic_list; + +const DMI_PATH: &str = "/sys/devices/virtual/dmi/id"; + +pub fn get_sysinfo(pretty: bool) -> Result { + let system_files = vec![ + "product_serial", + "product_sku", + "product_uuid", + "product_name", + ]; + let baseboard_files = vec!["board_asset_tag", "board_serial", "board_name"]; + let chassis_files = vec!["chassis_serial", "chassis_sku", "chassis_asset_tag"]; + + let system = get_dmi_infos(system_files)?; + let baseboard = get_dmi_infos(baseboard_files)?; + let chassis = get_dmi_infos(chassis_files)?; + + let mut mac_addresses: Vec = Vec::new(); + let links = get_nic_list()?; + for link in links { + let address = fs::read_to_string(format!("/sys/class/net/{link}/address"))?; + let address = String::from(address.trim()); + mac_addresses.push(address); + } + + let iso_info = Path::new("/run/proxmox-installer/iso-info.json"); + let mut product = String::from("Not available. Would be one of the following: pve, pmg, pbs"); + if iso_info.exists() { + let file = fs::File::open("/run/proxmox-installer/iso-info.json")?; + let reader = io::BufReader::new(file); + let setup_info: SetupInfo = serde_json::from_reader(reader)?; + product = setup_info.config.product.to_string(); + } + + let sysinfo = SysInfo { + product, + system, + baseboard, + chassis, + mac_addresses, + }; + if pretty { + return Ok(serde_json::to_string_pretty(&sysinfo)?); + } + Ok(serde_json::to_string(&sysinfo)?) +} + +#[derive(Debug, Serialize)] +struct SysInfo { + product: String, + system: HashMap, + baseboard: HashMap, + chassis: HashMap, + mac_addresses: Vec, +} + +fn get_dmi_infos(files: Vec<&str>) -> Result> { + let mut res: HashMap = HashMap::new(); + + for file in files { + let path = format!("{DMI_PATH}/{file}"); + let content = match fs::read_to_string(&path) { + Err(ref err) if err.kind() == std::io::ErrorKind::NotFound => continue, + Err(ref err) if err.kind() == std::io::ErrorKind::PermissionDenied => { + bail!("Could not read data. Are you running as root or with sudo?") + } + Err(err) => bail!("Error: '{err}' on '{path}'"), + Ok(content) => content.trim().into(), + }; + let key = file.splitn(2, '_').last().unwrap(); + res.insert(key.into(), content); + } + + Ok(res) +} -- 2.39.2