public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christian Ebner <c.ebner@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [RFC proxmox-backup 5/8] server: implement sanity check job
Date: Wed, 13 Dec 2023 16:38:16 +0100	[thread overview]
Message-ID: <20231213153819.391392-6-c.ebner@proxmox.com> (raw)
In-Reply-To: <20231213153819.391392-1-c.ebner@proxmox.com>

Adds the sanity check job execution logic and implements a check for
the datastore usage levels exceeding the config values threshold
level.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
 src/server/mod.rs              |   3 +
 src/server/sanity_check_job.rs | 131 +++++++++++++++++++++++++++++++++
 2 files changed, 134 insertions(+)
 create mode 100644 src/server/sanity_check_job.rs

diff --git a/src/server/mod.rs b/src/server/mod.rs
index 4e3b68ac..b3fdc281 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -25,6 +25,9 @@ pub use gc_job::*;
 mod realm_sync_job;
 pub use realm_sync_job::*;
 
+mod sanity_check_job;
+pub use sanity_check_job::*;
+
 mod email_notifications;
 pub use email_notifications::*;
 
diff --git a/src/server/sanity_check_job.rs b/src/server/sanity_check_job.rs
new file mode 100644
index 00000000..a68b4bfd
--- /dev/null
+++ b/src/server/sanity_check_job.rs
@@ -0,0 +1,131 @@
+use std::sync::Arc;
+
+use anyhow::{format_err, Error};
+
+use proxmox_human_byte::HumanByte;
+use proxmox_sys::{task_error, task_log};
+
+use pbs_api_types::{
+    Authid, Operation, SanityCheckJobOptions, Userid, DATASTORE_USAGE_FULL_THRESHOLD_DEFAULT,
+};
+use pbs_datastore::DataStore;
+use proxmox_rest_server::WorkerTask;
+
+use crate::server::{jobstate::Job, lookup_user_email};
+
+pub fn check_datastore_usage_full_threshold(
+    worker: Arc<WorkerTask>,
+    sanity_check_options: SanityCheckJobOptions,
+) -> Result<Vec<String>, Error> {
+    let (config, _digest) = pbs_config::datastore::config()?;
+    let threshold = sanity_check_options
+        .datastore_usage_full_threshold
+        .unwrap_or(DATASTORE_USAGE_FULL_THRESHOLD_DEFAULT);
+    let mut errors = Vec::new();
+
+    task_log!(
+        worker,
+        "Checking datastore usage levels with {threshold}% threshold ..."
+    );
+    for (store, (_, _)) in &config.sections {
+        let datastore = match DataStore::lookup_datastore(store, Some(Operation::Read)) {
+            Ok(datastore) => datastore,
+            Err(err) => {
+                let msg = format!("failed to lookup datastore - {err}");
+                task_error!(worker, "{msg}");
+                errors.push(msg);
+                continue;
+            }
+        };
+
+        let status = match proxmox_sys::fs::fs_info(&datastore.base_path()) {
+            Ok(status) => status,
+            Err(err) => {
+                let msg = format!("failed to get datastore status - {err}");
+                task_error!(worker, "{msg}");
+                errors.push(msg);
+                continue;
+            }
+        };
+
+        let used = (status.used as f64 / status.total as f64 * 100f64).trunc() as u8;
+        if used >= threshold {
+            let msg = format!(
+                "Datastore '{store}' exceeded usage threshold!\n  used {} of {} ({used}%)",
+                HumanByte::from(status.used),
+                HumanByte::from(status.total),
+            );
+            task_error!(worker, "{msg}");
+            errors.push(msg);
+        } else {
+            task_log!(
+                worker,
+                "Datastore '{store}' below usage threshold, used {} of {} ({used}%)",
+                HumanByte::from(status.used),
+                HumanByte::from(status.total),
+            );
+        }
+    }
+
+    Ok(errors)
+}
+
+pub fn do_sanity_check_job(
+    mut job: Job,
+    sanity_check_options: SanityCheckJobOptions,
+    auth_id: &Authid,
+    schedule: Option<String>,
+) -> Result<String, Error> {
+    let worker_type = job.jobtype().to_string();
+    let auth_id = auth_id.clone();
+
+    let notify_user = sanity_check_options
+        .notify_user
+        .as_ref()
+        .unwrap_or_else(|| Userid::root_userid());
+    let email = lookup_user_email(notify_user);
+
+    let upid_str = WorkerTask::new_thread(
+        &worker_type,
+        Some(job.jobname().to_string()),
+        auth_id.to_string(),
+        false,
+        move |worker| {
+            job.start(&worker.upid().to_string())?;
+
+            task_log!(worker, "sanity check job '{}'", job.jobname());
+
+            if let Some(event_str) = schedule {
+                task_log!(worker, "task triggered by schedule '{event_str}'");
+            }
+
+            let result = check_datastore_usage_full_threshold(worker.clone(), sanity_check_options);
+            let job_result = match result {
+                Ok(ref errors) if errors.is_empty() => Ok(()),
+                Ok(_) => Err(format_err!(
+                    "sanity check failed - please check the log for details"
+                )),
+                Err(_) => Err(format_err!("sanity check failed - job aborted")),
+            };
+
+            let status = worker.create_state(&job_result);
+
+            if let Err(err) = job.finish(status) {
+                eprintln!("could not finish job state for {}: {err}", job.jobtype());
+            }
+
+            if let Some(email) = email {
+                task_log!(worker, "sending notification email to '{email}'");
+                if let Err(err) =
+                    crate::server::send_sanity_check_status(&email, None, job.jobname(), &result)
+                {
+                    log::error!("send sanity check notification failed: {err}");
+                }
+            }
+
+            job_result
+        },
+    )?;
+
+    Ok(upid_str)
+}
-- 
2.39.2





  parent reply	other threads:[~2023-12-13 15:38 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-13 15:38 [pbs-devel] [RFC proxmox-backup 0/8] implement sanity check jobs Christian Ebner
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 1/8] api-types: jobs: add sanity checks job types Christian Ebner
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 2/8] config: implement sanity check job configuration Christian Ebner
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 3/8] api: config: sanity check jobs api endpoints Christian Ebner
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 4/8] server: add sanity check job email notifications Christian Ebner
2023-12-13 15:38 ` Christian Ebner [this message]
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 6/8] api: admin: add sanity check job api endpoints Christian Ebner
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 7/8] manager: add sanity check jobs management cli commands Christian Ebner
2023-12-13 15:38 ` [pbs-devel] [RFC proxmox-backup 8/8] proxy: add sanity check task to scheduler Christian Ebner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231213153819.391392-6-c.ebner@proxmox.com \
    --to=c.ebner@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal