]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/api2/node/apt.rs
typo fixes all over the place
[proxmox-backup.git] / src / api2 / node / apt.rs
index 9beaf024d5c0034f38a1b515ebcae0806c0a0b1a..e77b89fa4c2642c89f7df4b2bc729330cfd4c1a6 100644 (file)
@@ -1,12 +1,13 @@
 use anyhow::{Error, bail, format_err};
 use serde_json::{json, Value};
+use std::collections::HashMap;
 
 use proxmox::list_subdirs_api_method;
 use proxmox::api::{api, RpcEnvironment, RpcEnvironmentType, Permission};
 use proxmox::api::router::{Router, SubdirMap};
 
 use crate::server::WorkerTask;
-use crate::tools::{apt, http};
+use crate::tools::{apt, http, subscription};
 
 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
 use crate::api2::types::{Authid, APTUpdateInfo, NODE_SCHEMA, UPID_SCHEMA};
@@ -34,18 +35,15 @@ use crate::api2::types::{Authid, APTUpdateInfo, NODE_SCHEMA, UPID_SCHEMA};
 /// List available APT updates
 fn apt_update_available(_param: Value) -> Result<Value, Error> {
 
-    match apt::pkg_cache_expired() {
-        Ok(false) => {
-            if let Ok(Some(cache)) = apt::read_pkg_state() {
-                return Ok(json!(cache.package_status));
-            }
-        },
-        _ => (),
+    if let Ok(false) = apt::pkg_cache_expired() {
+        if let Ok(Some(cache)) = apt::read_pkg_state() {
+            return Ok(json!(cache.package_status));
+        }
     }
 
     let cache = apt::update_cache()?;
 
-    return Ok(json!(cache.package_status));
+    Ok(json!(cache.package_status))
 }
 
 fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
@@ -87,10 +85,10 @@ fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
             },
             notify: {
                 type: bool,
-                description: r#"Send notification mail about new package updates availanle to the
+                description: r#"Send notification mail about new package updates available to the
                     email address configured for 'root@pam')."#,
-                optional: true,
                 default: false,
+                optional: true,
             },
             quiet: {
                 description: "Only produces output suitable for logging, omitting progress indicators.",
@@ -109,16 +107,13 @@ fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
 )]
 /// Update the APT database
 pub fn apt_update_database(
-    notify: Option<bool>,
-    quiet: Option<bool>,
+    notify: bool,
+    quiet: bool,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<String, Error> {
 
     let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-    let to_stdout = if rpcenv.env_type() == RpcEnvironmentType::CLI { true } else { false };
-    // FIXME: change to non-option in signature and drop below once we have proxmox-api-macro 0.2.3
-    let quiet = quiet.unwrap_or(API_METHOD_APT_UPDATE_DATABASE_PARAM_DEFAULT_QUIET);
-    let notify = notify.unwrap_or(API_METHOD_APT_UPDATE_DATABASE_PARAM_DEFAULT_NOTIFY);
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
 
     let upid_str = WorkerTask::new_thread("aptupdate", None, auth_id, to_stdout, move |worker| {
         do_apt_update(&worker, quiet)?;
@@ -143,6 +138,7 @@ pub fn apt_update_database(
                 }
             }
             if !to_notify.is_empty() {
+                to_notify.sort_unstable_by_key(|k| &k.package);
                 crate::server::send_updates_available(&to_notify)?;
             }
             cache.notified = Some(notified);
@@ -194,24 +190,164 @@ fn apt_get_changelog(
         }
     }, Some(&name));
 
-    if pkg_info.len() == 0 {
+    if pkg_info.is_empty() {
         bail!("Package '{}' not found", name);
     }
 
     let changelog_url = &pkg_info[0].change_log_url;
     // FIXME: use 'apt-get changelog' for proxmox packages as well, once repo supports it
     if changelog_url.starts_with("http://download.proxmox.com/") {
-        let changelog = crate::tools::runtime::block_on(http::get_string(changelog_url))
+        let changelog = crate::tools::runtime::block_on(http::get_string(changelog_url, None))
             .map_err(|err| format_err!("Error downloading changelog from '{}': {}", changelog_url, err))?;
-        return Ok(json!(changelog));
+        Ok(json!(changelog))
+
+    } else if changelog_url.starts_with("https://enterprise.proxmox.com/") {
+        let sub = match subscription::read_subscription()? {
+            Some(sub) => sub,
+            None => bail!("cannot retrieve changelog from enterprise repo: no subscription info found")
+        };
+        let (key, id) = match sub.key {
+            Some(key) => {
+                match sub.serverid {
+                    Some(id) => (key, id),
+                    None =>
+                        bail!("cannot retrieve changelog from enterprise repo: no server id found")
+                }
+            },
+            None => bail!("cannot retrieve changelog from enterprise repo: no subscription key found")
+        };
+
+        let mut auth_header = HashMap::new();
+        auth_header.insert("Authorization".to_owned(),
+            format!("Basic {}", base64::encode(format!("{}:{}", key, id))));
+
+        let changelog = crate::tools::runtime::block_on(http::get_string(changelog_url, Some(&auth_header)))
+            .map_err(|err| format_err!("Error downloading changelog from '{}': {}", changelog_url, err))?;
+        Ok(json!(changelog))
+
     } else {
         let mut command = std::process::Command::new("apt-get");
         command.arg("changelog");
         command.arg("-qq"); // don't display download progress
         command.arg(name);
         let output = crate::tools::run_command(command, None)?;
-        return Ok(json!(output));
+        Ok(json!(output))
+    }
+}
+
+#[api(
+    input: {
+        properties: {
+            node: {
+                schema: NODE_SCHEMA,
+            },
+        },
+    },
+    returns: {
+        description: "List of more relevant packages.",
+        type: Array,
+        items: {
+            type: APTUpdateInfo,
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// Get package information for important Proxmox Backup Server packages.
+pub fn get_versions() -> Result<Vec<APTUpdateInfo>, 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",
+    ];
+
+    fn unknown_package(package: String, extra_info: Option<String>) -> APTUpdateInfo {
+        APTUpdateInfo {
+            package,
+            title: "unknown".into(),
+            arch: "unknown".into(),
+            description: "unknown".into(),
+            version: "unknown".into(),
+            old_version: "unknown".into(),
+            origin: "unknown".into(),
+            priority: "unknown".into(),
+            section: "unknown".into(),
+            change_log_url: "unknown".into(),
+            extra_info,
+        }
     }
+
+    let is_kernel = |name: &str| name.starts_with("pve-kernel-");
+
+    let mut packages: Vec<APTUpdateInfo> = Vec::new();
+    let pbs_packages = apt::list_installed_apt_packages(
+        |filter| {
+            filter.installed_version == Some(filter.active_version)
+                && (is_kernel(filter.package) || PACKAGES.contains(&filter.package))
+        },
+        None,
+    );
+
+    let running_kernel = format!(
+        "running kernel: {}",
+        nix::sys::utsname::uname().release().to_owned()
+    );
+    if let Some(proxmox_backup) = pbs_packages.iter().find(|pkg| pkg.package == "proxmox-backup") {
+        let mut proxmox_backup = proxmox_backup.clone();
+        proxmox_backup.extra_info = Some(running_kernel);
+        packages.push(proxmox_backup);
+    } else {
+        packages.push(unknown_package("proxmox-backup".into(), Some(running_kernel)));
+    }
+
+    let version = crate::api2::version::PROXMOX_PKG_VERSION;
+    let release = crate::api2::version::PROXMOX_PKG_RELEASE;
+    let daemon_version_info = Some(format!("running version: {}.{}", version, release));
+    if let Some(pkg) = pbs_packages.iter().find(|pkg| pkg.package == "proxmox-backup-server") {
+        let mut pkg = pkg.clone();
+        pkg.extra_info = daemon_version_info;
+        packages.push(pkg);
+    } else {
+        packages.push(unknown_package("proxmox-backup".into(), daemon_version_info));
+    }
+
+    let mut kernel_pkgs: Vec<APTUpdateInfo> = pbs_packages
+        .iter()
+        .filter(|pkg| is_kernel(&pkg.package))
+        .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();
+        kernel_pkgs.sort_by(|left, right| {
+            cache
+                .compare_versions(&left.old_version, &right.old_version)
+                .reverse()
+        });
+    }
+    packages.append(&mut kernel_pkgs);
+
+    // add entry for all packages we're interested in, even if not installed
+    for pkg in PACKAGES.iter() {
+        if pkg == &"proxmox-backup" || pkg == &"proxmox-backup-server" {
+            continue;
+        }
+        match pbs_packages.iter().find(|item| &item.package == pkg) {
+            Some(apt_pkg) => packages.push(apt_pkg.to_owned()),
+            None => packages.push(unknown_package(pkg.to_string(), None)),
+        }
+    }
+
+    Ok(packages)
 }
 
 const SUBDIRS: SubdirMap = &[
@@ -220,6 +356,7 @@ const SUBDIRS: SubdirMap = &[
         .get(&API_METHOD_APT_UPDATE_AVAILABLE)
         .post(&API_METHOD_APT_UPDATE_DATABASE)
     ),
+    ("versions", &Router::new().get(&API_METHOD_GET_VERSIONS)),
 ];
 
 pub const ROUTER: Router = Router::new()