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 A114665E04 for ; Thu, 5 Nov 2020 10:49:12 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 8F7FD15F74 for ; Thu, 5 Nov 2020 10:48:42 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (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 id B2CAD15F6C for ; Thu, 5 Nov 2020 10:48:41 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 7A4E045E61 for ; Thu, 5 Nov 2020 10:48:41 +0100 (CET) From: Mira Limbeck To: pbs-devel@lists.proxmox.com Date: Thu, 5 Nov 2020 10:48:38 +0100 Message-Id: <20201105094838.965-1-m.limbeck@proxmox.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.252 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods NO_DNS_FOR_FROM 0.379 Envelope sender has no MX or A DNS records RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [node.rs, mod.rs, apt.rs, proxmox-backup-manager.rs] Subject: [pbs-devel] [PATCH v3 proxmox-backup] add versions API call X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Nov 2020 09:49:12 -0000 Adds an API call that returns all relevant packages, same as the versions API call in PVE. In addition extends proxmox-backup-manager with the 'versions' command that prints the packages in a format similar to pveversion. Signed-off-by: Mira Limbeck --- v3: - fixed the commit message to contain the right API call and command ('versions') v2: - renamed api call to 'versions' from 'version' - incorporated stefan's suggestions: - changed the filter to include all packages, but only installed versions - fixed looping over the cache multiple times - fixed use of wrong version - removed unnecessary version checks src/api2/node.rs | 147 +++++++++++++++++++++++++++++- src/api2/types/mod.rs | 15 ++- src/bin/proxmox-backup-manager.rs | 39 ++++++++ src/tools/apt.rs | 3 + 4 files changed, 202 insertions(+), 2 deletions(-) diff --git a/src/api2/node.rs b/src/api2/node.rs index a19bea7e..7888cd24 100644 --- a/src/api2/node.rs +++ b/src/api2/node.rs @@ -19,7 +19,7 @@ use proxmox::tools::websocket::WebSocket; use proxmox::{identity, sortable}; use crate::api2::types::*; -use crate::config::acl::PRIV_SYS_CONSOLE; +use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_CONSOLE}; use crate::server::WorkerTask; use crate::tools; use crate::tools::ticket::{self, Empty, Ticket}; @@ -305,6 +305,150 @@ fn upgrade_to_websocket( .boxed() } +#[api( + input: { + properties: { + node: { + schema: NODE_SCHEMA, + }, + verbose: { + description: "Enable verbose output.", + type: Boolean, + optional: true, + default: false, + }, + }, + }, + returns: { + description: "List of packages and their installed version.", + type: Array, + items: { + type: PackageInfo, + }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false), + }, +)] +/// Get package information for important Proxmox packages. +pub fn get_versions(verbose: bool) -> Result { + const PACKAGES: [&str; 11] = [ + "ifupdown2", + "libjs-extjs", + "proxmox-backup", + "proxmox-backup-docs", + "proxmox-backup-client", + "proxmox-backup-server", + "proxmox-mini-journalreader", + "proxmox-widget-toolkit", + "pve-xtermjs", + "smartmontools", + "zfsutils-linux", + ]; + + let running_kernel = nix::sys::utsname::uname().release().to_owned(); + let mut packages: Vec = Vec::new(); + let pbs_packages = tools::apt::list_installed_apt_packages( + |fd| { + (fd.installed_version.is_some() && fd.installed_version.unwrap() == fd.active_version) + && (fd.package.starts_with("pve-kernel-") || PACKAGES.contains(&fd.package)) + }, + None, + ); + if let Some(proxmox_backup) = pbs_packages + .iter() + .find(|pkg| pkg.package == "proxmox-backup") + { + packages.push(PackageInfo { + name: proxmox_backup.package.clone(), + version: proxmox_backup.old_version.clone(), + comment: Some(format!("running kernel: {}", &running_kernel)), + }); + } else { + packages.push(PackageInfo { + name: "proxmox-backup".to_owned(), + version: "not correctly installed".to_owned(), + comment: Some(format!("running kernel: {}", &running_kernel)), + }); + } + + if !verbose { + return Ok(json!(packages)); + } + + if let Some(proxmox_backup_server) = pbs_packages + .iter() + .find(|pkg| pkg.package == "proxmox-backup-server") + { + packages.push(PackageInfo { + name: proxmox_backup_server.package.clone(), + version: proxmox_backup_server.old_version.clone(), + comment: Some(format!( + "running version: {}.{}", + crate::api2::version::PROXMOX_PKG_VERSION, + crate::api2::version::PROXMOX_PKG_RELEASE + )), + }); + } else { + packages.push(PackageInfo { + name: "proxmox-backup-server".to_owned(), + version: "not correctly installed".to_owned(), + comment: Some(format!( + "running version: {}.{}", + crate::api2::version::PROXMOX_PKG_VERSION, + crate::api2::version::PROXMOX_PKG_RELEASE + )), + }); + } + + let mut pve_kernel_pkgs: Vec = pbs_packages + .iter() + .filter(|pkg| pkg.package.starts_with("pve-kernel-")) + .cloned() + .collect(); + { + let cache = apt_pkg_native::Cache::get_singleton(); + pve_kernel_pkgs.sort_by(|left, right| { + cache + .compare_versions(&left.old_version, &right.old_version) + .reverse() + }); + } + + for pkg in pve_kernel_pkgs.iter().map(|pkg| PackageInfo { + name: pkg.package.clone(), + version: pkg.old_version.clone(), + comment: None, + }) { + packages.push(pkg); + } + + // add packages we're interested in, but are not installed + // and the installed ones returned by list_installed_apt_packages + for pkg in PACKAGES.iter() { + if pkg == &"proxmox-backup" || pkg == &"proxmox-backup-server" { + continue; + } + let apt_pkg = pbs_packages.iter().find(|item| &item.package == pkg); + if apt_pkg.is_some() { + let apt_pkg = apt_pkg.unwrap(); + packages.push(PackageInfo { + name: apt_pkg.package.clone(), + version: apt_pkg.old_version.clone(), + comment: None, + }); + } else { + packages.push(PackageInfo { + name: pkg.to_string(), + version: "not installed".to_owned(), + comment: None, + }); + } + } + + Ok(json!(packages)) +} + pub const SUBDIRS: SubdirMap = &[ ("apt", &apt::ROUTER), ("disks", &disks::ROUTER), @@ -320,6 +464,7 @@ pub const SUBDIRS: SubdirMap = &[ ("tasks", &tasks::ROUTER), ("termproxy", &Router::new().post(&API_METHOD_TERMPROXY)), ("time", &time::ROUTER), + ("versions", &Router::new().get(&API_METHOD_GET_VERSIONS)), ( "vncwebsocket", &Router::new().upgrade(&API_METHOD_WEBSOCKET), diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs index 7ee89f57..707f1a9a 100644 --- a/src/api2/types/mod.rs +++ b/src/api2/types/mod.rs @@ -1129,7 +1129,7 @@ pub enum RRDTimeFrameResolution { } #[api()] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] /// Describes a package for which an update is available. pub struct APTUpdateInfo { @@ -1155,6 +1155,19 @@ pub struct APTUpdateInfo { pub change_log_url: String, } +#[api()] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +/// Pair of package name and version with optional comment. +pub struct PackageInfo { + /// Package name + pub name: String, + /// Package version + pub version: String, + /// Optional comment + pub comment: Option, +} + #[api()] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs index 7499446b..273bed81 100644 --- a/src/bin/proxmox-backup-manager.rs +++ b/src/bin/proxmox-backup-manager.rs @@ -363,6 +363,42 @@ async fn report() -> Result { Ok(Value::Null) } +#[api( + input: { + properties: { + verbose: { + description: "Enable verbose output.", + type: Boolean, + optional: true, + default: false, + }, + }, + }, +)] +/// Get package information for important Proxmox packages. +async fn get_versions(param: Value) -> Result { + let client = connect()?; + + let node = proxmox::tools::nodename(); + + let path = format!("api2/json/nodes/{}/versions", node); + + let mut result = client.get(&path, Some(param)).await?; + + let data = result["data"].take(); + let packages: Vec = serde_json::from_value(data)?; + + for pkg in packages { + if pkg.comment.is_some() { + println!("{}: {} ({})", pkg.name, pkg.version, pkg.comment.unwrap()); + } else { + println!("{}: {}", pkg.name, pkg.version); + } + } + + Ok(Value::Null) +} + fn main() { proxmox_backup::tools::setup_safe_path_env(); @@ -396,6 +432,9 @@ fn main() { ) .insert("report", CliCommand::new(&API_METHOD_REPORT) + ) + .insert("versions", + CliCommand::new(&API_METHOD_GET_VERSIONS) ); diff --git a/src/tools/apt.rs b/src/tools/apt.rs index 5800e0a2..fab8f998 100644 --- a/src/tools/apt.rs +++ b/src/tools/apt.rs @@ -136,6 +136,8 @@ fn get_changelog_url( } pub struct FilterData<'a> { + // package name + pub package: &'a str, // this is version info returned by APT pub installed_version: Option<&'a str>, pub candidate_version: &'a str, @@ -270,6 +272,7 @@ where let mut long_desc = "".to_owned(); let fd = FilterData { + package: package.as_str(), installed_version: current_version.as_deref(), candidate_version: &candidate_version, active_version: &version, -- 2.20.1