* [pbs-devel] [PATCH v3 proxmox-backup] add versions API call
@ 2020-11-05 9:48 Mira Limbeck
2020-11-05 11:15 ` Wolfgang Bumiller
0 siblings, 1 reply; 2+ messages in thread
From: Mira Limbeck @ 2020-11-05 9:48 UTC (permalink / raw)
To: pbs-devel
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 <m.limbeck@proxmox.com>
---
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<Value, Error> {
+ 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<PackageInfo> = 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<APTUpdateInfo> = 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<String>,
+}
+
#[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<Value, Error> {
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<Value, Error> {
+ 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<PackageInfo> = 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
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [pbs-devel] [PATCH v3 proxmox-backup] add versions API call
2020-11-05 9:48 [pbs-devel] [PATCH v3 proxmox-backup] add versions API call Mira Limbeck
@ 2020-11-05 11:15 ` Wolfgang Bumiller
0 siblings, 0 replies; 2+ messages in thread
From: Wolfgang Bumiller @ 2020-11-05 11:15 UTC (permalink / raw)
To: Mira Limbeck; +Cc: pbs-devel
On Thu, Nov 05, 2020 at 10:48:38AM +0100, Mira Limbeck wrote:
> 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 <m.limbeck@proxmox.com>
> ---
> 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<Value, Error> {
> + const PACKAGES: [&str; 11] = [
No need to count them, you can just make this an `&[&str]`.
> + "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<PackageInfo> = Vec::new();
> + let pbs_packages = tools::apt::list_installed_apt_packages(
> + |fd| {
makes me think of file descriptors :-\
> + (fd.installed_version.is_some() && fd.installed_version.unwrap() == fd.active_version)
You can just use
(fd.installed_version == Some(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<APTUpdateInfo> = 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);
> + }
Can we build the `PackageInfo` inside the loop body please?
packages.push(PackageInfo {
...
});
> +
> + // 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() {
Please don't use `if (x.is_some()) { use(x.unwrap() }`, use `match`
instead.
> + 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<String>,
> +}
> +
> #[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<Value, Error> {
> 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<Value, Error> {
> + 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<PackageInfo> = serde_json::from_value(data)?;
> +
> + for pkg in packages {
> + if pkg.comment.is_some() {
Same, use `match` instead of `is_some() => unwrap()`
> + 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
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2020-11-05 11:15 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-05 9:48 [pbs-devel] [PATCH v3 proxmox-backup] add versions API call Mira Limbeck
2020-11-05 11:15 ` Wolfgang Bumiller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox