[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