[pve-devel] [PATCH installer v2 16/17] fix #5536: post-hook: add utility for sending notifications after auto-install
Aaron Lauterer
a.lauterer at proxmox.com
Wed Jul 24 13:21:11 CEST 2024
Two few small things inline
On 2024-07-18 15:49, Christoph Heiss wrote:
> This utility can be called with the low-level install config after a
> successful installation to send a notification via a HTTP POST request,
> if the user has configured an endpoint for that in the answer file.
>
> Signed-off-by: Christoph Heiss <c.heiss at proxmox.com>
> ---
> Changes v1 -> v2:
> * squash implementation and unit tests into one patch
> * simplify udev property retrieving by introducing proper helpers on
> `UdevInfo` itself
> * rename Answer::from_reader() -> Answer::try_from_reader to better
> reflect it returns a Result<>
> * improved error message in some places
> * added new fields; now includes ISO version, SecureBoot state, CPU
> and DMI info
> * product information was split into separate fields
> * boot mode information was split into separate fields
> * product version is now retrieved from the package using dpkg-query
> directly
> * kernel version was split into separate fields, retrieving version
> string from the image directly
> * all disks and NICs are now included, a field indicates whether they
> are boot disk or management interface, respectively
> * move with_chroot() invocation out of PostHookInfo::gather()
>
> Signed-off-by: Christoph Heiss <c.heiss at proxmox.com>
> ---
> Cargo.toml | 1 +
> Makefile | 8 +-
> debian/install | 1 +
> proxmox-auto-installer/src/answer.rs | 16 +-
> .../src/bin/proxmox-auto-installer.rs | 13 +-
> proxmox-auto-installer/src/udevinfo.rs | 8 +-
> .../src/fetch_plugins/http.rs | 2 +-
> proxmox-installer-common/src/http.rs | 6 +-
> proxmox-installer-common/src/options.rs | 5 +
> proxmox-installer-common/src/setup.rs | 2 +-
> proxmox-installer-common/src/utils.rs | 2 +
> proxmox-post-hook/Cargo.toml | 18 +
> proxmox-post-hook/src/main.rs | 784 ++++++++++++++++++
> 13 files changed, 843 insertions(+), 23 deletions(-)
> create mode 100644 proxmox-post-hook/Cargo.toml
> create mode 100644 proxmox-post-hook/src/main.rs
>
[…]
> diff --git a/proxmox-post-hook/Cargo.toml b/proxmox-post-hook/Cargo.toml
> new file mode 100644
> index 0000000..3acea6c
> --- /dev/null
> +++ b/proxmox-post-hook/Cargo.toml
> @@ -0,0 +1,18 @@
> +[package]
> +name = "proxmox-post-hook"
> +version = "0.1.0"
> +edition = "2021"
> +authors = [
> + "Christoph Heiss <c.heiss at proxmox.com>",
> + "Proxmox Support Team <support at proxmox.com>",
> +]
> +license = "AGPL-3"
> +exclude = [ "build", "debian" ]
> +homepage = "https://www.proxmox.com"
> +
> +[dependencies]
> +anyhow.workspace = true
> +proxmox-auto-installer.workspace = true
> +proxmox-installer-common = { workspace = true, features = ["http"] }
> +serde.workspace = true
> +serde_json.workspace = true
> diff --git a/proxmox-post-hook/src/main.rs b/proxmox-post-hook/src/main.rs
> new file mode 100644
> index 0000000..d3e5b5c
> --- /dev/null
> +++ b/proxmox-post-hook/src/main.rs
> @@ -0,0 +1,784 @@
> +//! Post installation hook for the Proxmox installer, mainly for combination
> +//! with the auto-installer.
> +//!
> +//! If a `[posthook]` section is specified in the given answer file, it will
> +//! send a HTTP POST request to that URL, with an optional certificate fingerprint
> +//! for usage with (self-signed) TLS certificates.
> +//! In the body of the request, information about the newly installed system is sent.
> +//!
> +//! Relies on `proxmox-chroot` as an external dependency to (bind-)mount the
> +//! previously installed system.
> +
> +use std::{
> + collections::HashSet,
> + ffi::CStr,
> + fs::{self, File},
> + io::BufReader,
> + os::unix::fs::FileExt,
> + path::PathBuf,
> + process::{Command, ExitCode},
> +};
> +
> +use anyhow::{anyhow, bail, Context, Result};
> +use proxmox_auto_installer::{
> + answer::{Answer, PostNotificationHookInfo},
> + udevinfo::{UdevInfo, UdevProperties},
> +};
> +use proxmox_installer_common::{
> + options::{Disk, FsType},
> + setup::{
> + load_installer_setup_files, BootType, InstallConfig, IsoInfo, ProxmoxProduct, RuntimeInfo,
> + SetupInfo,
> + },
> + sysinfo::SystemDMI,
> + utils::CidrAddress,
> +};
> +use serde::Serialize;
> +
> +/// Information about the system boot status.
> +#[derive(Serialize)]
> +struct BootInfo {
> + /// Whether the system is booted using UEFI or legacy BIOS.
> + mode: BootType,
> + /// Whether SecureBoot is enabled for the installation.
> + #[serde(skip_serializing_if = "Option::is_none")]
> + secureboot: Option<bool>,
> +}
> +
> +/// Holds all the public keys for the different algorithms available.
> +#[derive(Serialize)]
> +struct SshPublicHostKeys {
> + // ECDSA-based public host key
> + ecdsa: String,
> + // ED25519-based public host key
> + ed25519: String,
> + // RSA-based public host key
> + rsa: String,
> +}
> +
> +/// A single disk configured as boot disk.
Is this comment valid this way? AFAIU there was a dedicated struct for
boot disks in the previous version, but now this struct is for all
disks, optionally marking it as boot disk.
> +#[derive(Serialize)]
> +#[serde(rename_all = "kebab-case")]
> +struct DiskInfo {
> + /// Size in bytes
> + size: usize,
> + /// Set to true if the disk is used for booting.
> + #[serde(skip_serializing_if = "Option::is_none")]
> + is_bootdisk: Option<bool>,
> + /// Properties about the device as given by udev.
> + udev_properties: UdevProperties,
> +}
> +
> +/// Holds information about the management network interface.
> +#[derive(Serialize)]
> +#[serde(rename_all = "kebab-case")]
> +struct NetworkInterfaceInfo {
> + /// MAC address of the interface
> + mac: String,
> + /// (Designated) IP address of the interface
> + #[serde(skip_serializing_if = "Option::is_none")]
> + address: Option<CidrAddress>,
> + /// Set to true if the interface is the chosen management interface during
> + /// installation.
> + #[serde(skip_serializing_if = "Option::is_none")]
> + is_management: Option<bool>,
> + /// Properties about the device as given by udev.
> + udev_properties: UdevProperties,
> +}
> +
> +/// Information about the installed product itself.
> +#[derive(Serialize)]
> +#[serde(rename_all = "kebab-case")]
> +struct ProductInfo {
> + /// Full name of the product
> + fullname: String,
> + /// Product abbreviation
> + short: ProxmoxProduct,
> + /// Version of the installed product
> + version: String,
> +}
> +
> +/// The current kernel version.
> +/// Aligns with the format as used by the /nodes/<node>/status API of each product.
> +#[derive(Serialize)]
> +struct KernelVersionInformation {
> + /// The systemname/nodename
> + pub sysname: String,
> + /// The kernel release number
> + pub release: String,
> + /// The kernel version
> + pub version: String,
> + /// The machine architecture
> + pub machine: String,
> +}
> +
> +/// Information about the CPU(s) installed in the system
> +#[derive(Serialize)]
> +struct CpuInfo {
> + /// Number of physical CPU cores.
> + cores: usize,
> + /// Number of logical CPU cores aka. threads.
> + cpus: usize,
> + /// CPU feature flag set as a space-delimited list.
> + flags: String,
> + /// Whether hardware-accelerated virtualization is supported.
> + hvm: bool,
> + /// Reported model of the CPU(s)
> + model: String,
> + /// Number of physical CPU sockets
> + sockets: usize,
> +}
> +
> +/// All data sent as request payload with the post-hook POST request.
> +#[derive(Serialize)]
> +#[serde(rename_all = "kebab-case")]
> +struct PostHookInfo {
> + /// major.minor version of Debian as installed, retrieved from /etc/debian_version
> + debian_version: String,
> + /// PVE/PMG/PBS version as reported by `pveversion`, `pmgversion` or
> + /// `proxmox-backup-manager version`, respectively.
> + product: ProductInfo,
> + /// Release information for the ISO used for the installation.
> + iso: IsoInfo,
> + /// Installed kernel version
> + kernel_version: KernelVersionInformation,
> + /// Describes the boot mode of the machine and the SecureBoot status.
> + boot_info: BootInfo,
> + /// Information about the installed CPU(s)
> + cpu_info: CpuInfo,
> + /// DMI information about the system
> + dmi: SystemDMI,
> + /// Filesystem used for boot disk(s)
> + filesystem: FsType,
> + /// Fully qualified domain name of the installed system
> + fqdn: String,
> + /// Unique systemd-id128 identifier of the installed system (128-bit, 16 bytes)
> + machine_id: String,
> + /// All disks detected on the system.
> + disks: Vec<DiskInfo>,
> + /// All network interfaces detected on the system.
> + network_interfaces: Vec<NetworkInterfaceInfo>,
> + /// Public parts of SSH host keys of the installed system
> + ssh_public_host_keys: SshPublicHostKeys,
> +}
> +
> +/// Defines the size of a gibibyte in bytes.
> +const SIZE_GIB: usize = 1024 * 1024 * 1024;
> +
> +impl PostHookInfo {
> + /// Gathers all needed information about the newly installed system for sending
> + /// it to a specified server.
> + ///
> + /// # Arguments
> + ///
> + /// * `target_path` - Path to where the chroot environment root is mounted
> + /// * `answer` - Answer file as provided by the user
> + fn gather(target_path: &str, answer: &Answer) -> Result<Self> {
> + println!("Gathering installed system data ..");
The three dots at the end can most likely be either 3 (or UTF-8 3-dots)
or just one?
> +
> + let config: InstallConfig =
> + serde_json::from_reader(BufReader::new(File::open("/tmp/low-level-config.json")?))?;
> +
[…]
More information about the pve-devel
mailing list