[pbs-devel] [PATCH v4 proxmox-backup] add versions API call

Thomas Lamprecht t.lamprecht at proxmox.com
Thu Nov 5 16:04:53 CET 2020


On 05.11.20 13:56, Mira Limbeck wrote:
> Adds an API call that returns all relevant packages, same as the
> versions API call in PVE.

this is really far from having anything to do with PVE...

1. It's on a different API path, PVE: `/nodes/<node>/apt/versions` this
   uses `/nodes/<node>/versions`

2. It returns a *complete* different result type, PVE uses the same as it's
   update call, which Stefan then adopted for PBS as APTUpdateInfo type, this
   patch invents some arbitrary new type, not compatible to existing
   pve-manager widgets.

3. The CLI commando does a connect to the API daemon, instead of just calling
   that code directly like PVE does, I mean, this is my least concern, but
   the version command is often required when there are issues, i.e., possibly
   no functional API daemon to connect to, so it IMO makes sense to avoid
   connecting locally for such a thing.
 
> In addition extends proxmox-backup-manager with the 'versions' command
> that prints the packages in a format similar to pveversion.

why not use the API result type, and the format CLI helpers to make it more
consistent with the remaining commands?

> 
> Signed-off-by: Mira Limbeck <m.limbeck at proxmox.com>
> ---
> v4:
>  - incorporated wolfgang's suggestions:
>    - is_some() && unwrap() replaced with match
>    - removed unnecessary map()
>    - renamed fd to filter_data
>    - changed the static str array to a slice of strs
> 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                  | 151 +++++++++++++++++++++++++++++-
>  src/api2/types/mod.rs             |  15 ++-
>  src/bin/proxmox-backup-manager.rs |  38 ++++++++
>  src/tools/apt.rs                  |   3 +
>  4 files changed, 205 insertions(+), 2 deletions(-)
> 
> diff --git a/src/api2/node.rs b/src/api2/node.rs
> index a19bea7e..0c66af55 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,154 @@ 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] = &[
> +        "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(
> +        |filter_data| {
> +            (filter_data.installed_version == Some(filter_data.active_version))
> +                && (filter_data.package.starts_with("pve-kernel-")
> +                    || PACKAGES.contains(&filter_data.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();
> +    // make sure the cache mutex gets dropped before the next call to list_installed_apt_packages
> +    {
> +        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() {
> +        packages.push(PackageInfo {
> +            name: pkg.package.clone(),
> +            version: pkg.old_version.clone(),
> +            comment: None,
> +        });
> +    }
> +
> +    // 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);
> +        match apt_pkg {
> +            Some(apt_pkg) => {
> +                packages.push(PackageInfo {
> +                    name: apt_pkg.package.clone(),
> +                    version: apt_pkg.old_version.clone(),
> +                    comment: None,
> +                });
> +            }
> +            None => {
> +                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 +468,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..d4ad73fe 100644
> --- a/src/bin/proxmox-backup-manager.rs
> +++ b/src/bin/proxmox-backup-manager.rs
> @@ -363,6 +363,41 @@ 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 {
> +        match pkg.comment {
> +            Some(comment) => println!("{}: {} ({})", pkg.name, pkg.version, comment),
> +            None => println!("{}: {}", pkg.name, pkg.version),
> +        }
> +    }
> +
> +    Ok(Value::Null)
> +}
> +
>  fn main() {
>  
>      proxmox_backup::tools::setup_safe_path_env();
> @@ -396,6 +431,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,
> 







More information about the pbs-devel mailing list