public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
@ 2024-04-12 10:05 Lukas Wagner
  2024-04-12 10:05 ` [pbs-devel] [PATCH proxmox-backup 01/33] pbs-config: add module for loading notification config Lukas Wagner
                   ` (35 more replies)
  0 siblings, 36 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:05 UTC (permalink / raw)
  To: pbs-devel

These patches integrate the notification system which was introduced in Proxmox VE
in 8.1 into Proxmox Backup Server.

Some highlights/noteworthy details from the series:
  - notification template files are installed in /usr/share/proxmox-backup/templates/default
    and are rendered when a notification is sent
  - since sending notifications needs to be done from a privileged context
    (to read protected passwords/tokens from the priv config file),
    we queue notifications to be sent in /var/lib/proxmox-backup/notifications
    and periodically send any queued notifications via a worker in the
    privileged process
  - The API endpoint paths are prefixed with /config/notifications
  - API endpoints which read/modify notification system config require
    Sys.Audit (read) or Sys.Modify (modify) permissions on /system/notifications

  - tape-{backup,restore} settings and datastore options now have a
    'notification-mode' parameter, which allows to choose between the
    'legacy' behavior (sendmail to a selected user's email address) and
    the event-based notification system. If the parameter is not set, we
    default to the legacy behavior in order to keep existing behavior
    as is for now. For new datastores/tape backup jobs created
    interactively from teh UI, we set the parameter to 'notification-system'
    and opt in into the new system by default.

Still missing/rough edges:
  - documentation (will follow soon)
  - Datastore option view in UI could be improved. When 'notification-mode' is
    set to 'notification-system', we should indicate that 'mailto-user' and the
    other notification settings have no effect. Already did that in the edit
    window, but in the grid panel I did not find a quick way to do that.

  - still needs a bit more testing (e.g. ACME notifications) - would greatly
    appreciate people helping here!

Open questions:
  - Right now the 'default-matcher' matches every notification, same as in PVE.
    Up until now, datastore prune notifications were only sent on errors, so
    maybe we should also have the default-matcher work the same?
    The default matcher would then probably be:
      - !All
          - severity: error
          - match-field: exact:type=prune

This patch series requires the patches for 'proxmox' from
https://lists.proxmox.com/pipermail/pve-devel/2024-April/062708.html
to be applied.

The dependency for proxmox-widget-toolkit should be bumped to at least
4.1.4 (we need: "utils: add mechanism to add and override translatable notification event
descriptions in the product specific UIs")
Also, while not strictly needed, the patches for widget-toolkit from
https://lists.proxmox.com/pipermail/pve-devel/2024-February/061992.html
make creating matchers much easier - the required API endpoints were
already implemented by this series.

proxmox-backup:

Lukas Wagner (33):
  pbs-config: add module for loading notification config
  server: rename email_notifications module to notifications
  notifications: allow sending notifications via proxmox_notify
  buildsys: install templates for test notifications
  pbs-config: acl: add /system/notifications as known ACL path
  api: add endpoints for querying/testing notification targets
  api: add endpoints for notification matchers
  api: add endpoints for sendmail targets
  api: add endpoints for smtp targets
  api: add endpoints for gotify targets
  api: add endpoints for querying known notification values/fields
  api-types: api: datatore: add notification-mode parameter
  api-types: api: tape: add notification-mode parameter
  server: notifications: send GC notifications via notification system
  server: notifications: send prune notifications via notification
    system
  server: notifications: send verify notifications via notification
    system
  server: notifications: send sync notifications via notification system
  server: notifications: send update notifications via notification
    system
  server: notifications: send acme notifications via notification system
  server: notifications: send tape notifications via notification system
  ui: add notification config panel
  ui: tape backup job: add selector for notification-mode
  ui: tape backup: add selector for 'notification-mode'
  ui: tape restore: add 'notification-mode' parameter
  ui: datastore options: add 'notification-mode' parameter
  ui: utils: add overrides for known notification metadata fields/values
  ui: datastore edit: make new stores use notification system by default
  ui: permissions paths: add /system/notifications to combobox
  proxmox-backup-manager: add CLI for notification targets
  proxmox-backup-manager: add CLI for notification matchers
  proxmox-backup-manager: add CLI for gotify endpoints
  proxmox-backup-manager: add CLI for sendmail endpoints
  proxmox-backup-manager: add CLI for SMTP endpoints

 Cargo.toml                                    |   3 +
 Makefile                                      |   5 +-
 debian/proxmox-backup-server.install          |  29 +
 pbs-api-types/src/datastore.rs                |  22 +
 pbs-api-types/src/jobs.rs                     |   8 +-
 pbs-config/Cargo.toml                         |   1 +
 pbs-config/src/acl.rs                         |   3 +-
 pbs-config/src/lib.rs                         |   1 +
 pbs-config/src/notifications.rs               |  41 +
 src/api2/config/datastore.rs                  |   9 +
 src/api2/config/mod.rs                        |   2 +
 src/api2/config/notifications/gotify.rs       | 190 +++++
 src/api2/config/notifications/matchers.rs     | 170 ++++
 src/api2/config/notifications/mod.rs          | 175 ++++
 src/api2/config/notifications/sendmail.rs     | 178 ++++
 src/api2/config/notifications/smtp.rs         | 191 +++++
 src/api2/config/notifications/targets.rs      |  63 ++
 src/api2/config/tape_backup_job.rs            |   8 +
 src/api2/pull.rs                              |  10 +-
 src/api2/tape/backup.rs                       |  62 +-
 src/api2/tape/restore.rs                      |  46 +-
 src/bin/proxmox-backup-api.rs                 |  11 +
 src/bin/proxmox-backup-manager.rs             |   2 +
 src/bin/proxmox-backup-proxy.rs               |   1 +
 src/bin/proxmox_backup_manager/mod.rs         |   2 +
 .../notifications/gotify.rs                   |  93 +++
 .../notifications/matchers.rs                 |  93 +++
 .../notifications/mod.rs                      |  21 +
 .../notifications/sendmail.rs                 |  94 +++
 .../notifications/smtp.rs                     |  96 +++
 .../notifications/targets.rs                  |  51 ++
 src/server/email_notifications.rs             | 763 ------------------
 src/server/gc_job.rs                          |  10 +-
 src/server/mod.rs                             |   4 +-
 src/server/notifications.rs                   | 544 +++++++++++++
 src/server/verify_job.rs                      |  10 +-
 src/tape/drive/mod.rs                         |  22 +-
 src/tape/mod.rs                               |  27 +
 src/tape/pool_writer/mod.rs                   |  11 +-
 templates/Makefile                            |  41 +
 templates/default/acme-err-body.txt.hbs       |   7 +
 templates/default/acme-err-subject.txt.hbs    |   1 +
 templates/default/gc-err-body.txt.hbs         |   8 +
 templates/default/gc-err-subject.txt.hbs      |   1 +
 templates/default/gc-ok-body.txt.hbs          |  23 +
 templates/default/gc-ok-subject.txt.hbs       |   1 +
 .../default/package-updates-body.txt.hbs      |   8 +
 .../default/package-updates-subject.txt.hbs   |   1 +
 templates/default/prune-err-body.txt.hbs      |  10 +
 templates/default/prune-err-subject.txt.hbs   |   1 +
 templates/default/prune-ok-body.txt.hbs       |  10 +
 templates/default/prune-ok-subject.txt.hbs    |   1 +
 templates/default/sync-err-body.txt.hbs       |  14 +
 templates/default/sync-err-subject.txt.hbs    |   5 +
 templates/default/sync-ok-body.txt.hbs        |  14 +
 templates/default/sync-ok-subject.txt.hbs     |   5 +
 .../default/tape-backup-err-body.txt.hbs      |  26 +
 .../default/tape-backup-err-subject.txt.hbs   |   5 +
 templates/default/tape-backup-ok-body.txt.hbs |  27 +
 .../default/tape-backup-ok-subject.txt.hbs    |   5 +
 templates/default/tape-load-body.txt.hbs      |  15 +
 templates/default/tape-load-subject.txt.hbs   |   1 +
 templates/default/test-body.html.hbs          |   1 +
 templates/default/test-body.txt.hbs           |   1 +
 templates/default/test-subject.txt.hbs        |   1 +
 templates/default/verify-err-body.txt.hbs     |  14 +
 templates/default/verify-err-subject.txt.hbs  |   1 +
 templates/default/verify-ok-body.txt.hbs      |  10 +
 templates/default/verify-ok-subject.txt.hbs   |   1 +
 www/Makefile                                  |   1 +
 www/NavigationTree.js                         |   6 +
 www/Utils.js                                  |  16 +
 www/config/NotificationConfigView.js          |  11 +
 www/datastore/OptionView.js                   |  15 +
 www/form/PermissionPathSelector.js            |   1 +
 www/tape/window/TapeBackup.js                 |  25 +
 www/tape/window/TapeBackupJob.js              |  24 +
 www/tape/window/TapeRestore.js                |  17 +
 www/window/DataStoreEdit.js                   |  13 +
 www/window/NotifyOptions.js                   |  38 +
 80 files changed, 2630 insertions(+), 868 deletions(-)
 create mode 100644 pbs-config/src/notifications.rs
 create mode 100644 src/api2/config/notifications/gotify.rs
 create mode 100644 src/api2/config/notifications/matchers.rs
 create mode 100644 src/api2/config/notifications/mod.rs
 create mode 100644 src/api2/config/notifications/sendmail.rs
 create mode 100644 src/api2/config/notifications/smtp.rs
 create mode 100644 src/api2/config/notifications/targets.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/gotify.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/matchers.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/mod.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/sendmail.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/smtp.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/targets.rs
 delete mode 100644 src/server/email_notifications.rs
 create mode 100644 src/server/notifications.rs
 create mode 100644 templates/Makefile
 create mode 100644 templates/default/acme-err-body.txt.hbs
 create mode 100644 templates/default/acme-err-subject.txt.hbs
 create mode 100644 templates/default/gc-err-body.txt.hbs
 create mode 100644 templates/default/gc-err-subject.txt.hbs
 create mode 100644 templates/default/gc-ok-body.txt.hbs
 create mode 100644 templates/default/gc-ok-subject.txt.hbs
 create mode 100644 templates/default/package-updates-body.txt.hbs
 create mode 100644 templates/default/package-updates-subject.txt.hbs
 create mode 100644 templates/default/prune-err-body.txt.hbs
 create mode 100644 templates/default/prune-err-subject.txt.hbs
 create mode 100644 templates/default/prune-ok-body.txt.hbs
 create mode 100644 templates/default/prune-ok-subject.txt.hbs
 create mode 100644 templates/default/sync-err-body.txt.hbs
 create mode 100644 templates/default/sync-err-subject.txt.hbs
 create mode 100644 templates/default/sync-ok-body.txt.hbs
 create mode 100644 templates/default/sync-ok-subject.txt.hbs
 create mode 100644 templates/default/tape-backup-err-body.txt.hbs
 create mode 100644 templates/default/tape-backup-err-subject.txt.hbs
 create mode 100644 templates/default/tape-backup-ok-body.txt.hbs
 create mode 100644 templates/default/tape-backup-ok-subject.txt.hbs
 create mode 100644 templates/default/tape-load-body.txt.hbs
 create mode 100644 templates/default/tape-load-subject.txt.hbs
 create mode 100644 templates/default/test-body.html.hbs
 create mode 100644 templates/default/test-body.txt.hbs
 create mode 100644 templates/default/test-subject.txt.hbs
 create mode 100644 templates/default/verify-err-body.txt.hbs
 create mode 100644 templates/default/verify-err-subject.txt.hbs
 create mode 100644 templates/default/verify-ok-body.txt.hbs
 create mode 100644 templates/default/verify-ok-subject.txt.hbs
 create mode 100644 www/config/NotificationConfigView.js


Summary over all repositories:
  80 files changed, 2630 insertions(+), 868 deletions(-)

-- 
Generated by git-murpp 0.7.1




^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 01/33] pbs-config: add module for loading notification config
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
@ 2024-04-12 10:05 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 02/33] server: rename email_notifications module to notifications Lukas Wagner
                   ` (34 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:05 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 Cargo.toml                      |  3 +++
 pbs-config/Cargo.toml           |  1 +
 pbs-config/src/lib.rs           |  1 +
 pbs-config/src/notifications.rs | 41 +++++++++++++++++++++++++++++++++
 4 files changed, 46 insertions(+)
 create mode 100644 pbs-config/src/notifications.rs

diff --git a/Cargo.toml b/Cargo.toml
index 4616e476..f06aeba3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,6 +65,7 @@ proxmox-io = "1.0.1" # tools and client use "tokio" feature
 proxmox-lang = "1.1"
 proxmox-ldap = "0.2.1"
 proxmox-metrics = "0.3"
+proxmox-notify = "0.3"
 proxmox-openid = "0.10.0"
 proxmox-rest-server = { version = "0.5.1", features = [ "templates" ] }
 # some use "cli", some use "cli" and "server", pbs-config uses nothing
@@ -211,6 +212,7 @@ proxmox-io.workspace = true
 proxmox-lang.workspace = true
 proxmox-ldap.workspace = true
 proxmox-metrics.workspace = true
+proxmox-notify = { workspace = true, features = [ "pbs-context" ] }
 proxmox-openid.workspace = true
 proxmox-rest-server = { workspace = true, features = [ "rate-limited-stream" ] }
 proxmox-router = { workspace = true, features = [ "cli", "server"] }
@@ -257,6 +259,7 @@ proxmox-rrd.workspace = true
 #proxmox-lang = { path = "../proxmox/proxmox-lang" }
 #proxmox-ldap = { path = "../proxmox/proxmox-ldap" }
 #proxmox-metrics = { path = "../proxmox/proxmox-metrics" }
+#proxmox-notify = { path = "../proxmox/proxmox-notify" }
 #proxmox-openid = { path = "../proxmox/proxmox-openid" }
 #proxmox-rest-server = { path = "../proxmox/proxmox-rest-server" }
 #proxmox-router = { path = "../proxmox/proxmox-router" }
diff --git a/pbs-config/Cargo.toml b/pbs-config/Cargo.toml
index d11cd41e..ac639a8e 100644
--- a/pbs-config/Cargo.toml
+++ b/pbs-config/Cargo.toml
@@ -17,6 +17,7 @@ regex.workspace = true
 serde.workspace = true
 serde_json.workspace = true
 
+proxmox-notify.workspace = true
 proxmox-router = { workspace = true, default-features = false }
 proxmox-schema.workspace = true
 proxmox-section-config.workspace = true
diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs
index 3cfdb2a1..20a8238d 100644
--- a/pbs-config/src/lib.rs
+++ b/pbs-config/src/lib.rs
@@ -7,6 +7,7 @@ pub mod drive;
 pub mod media_pool;
 pub mod metrics;
 pub mod network;
+pub mod notifications;
 pub mod prune;
 pub mod remote;
 pub mod sync;
diff --git a/pbs-config/src/notifications.rs b/pbs-config/src/notifications.rs
new file mode 100644
index 00000000..df327346
--- /dev/null
+++ b/pbs-config/src/notifications.rs
@@ -0,0 +1,41 @@
+use anyhow::Error;
+
+use proxmox_notify::Config;
+
+use pbs_buildcfg::configdir;
+
+use crate::{open_backup_lockfile, BackupLockGuard};
+
+/// Configuration file location for notification targets/matchers.
+pub const NOTIFICATION_CONFIG_PATH: &str = configdir!("/notifications.cfg");
+
+/// Private configuration file location for secrets - only readable by `root`.
+pub const NOTIFICATION_PRIV_CONFIG_PATH: &str = configdir!("/notifications-priv.cfg");
+
+/// Lockfile to prevent concurrent write access.
+pub const NOTIFICATION_LOCK_FILE: &str = configdir!("/.notifications.lck");
+
+/// Get exclusive lock for `notifications.cfg`
+pub fn lock_config() -> Result<BackupLockGuard, Error> {
+    open_backup_lockfile(NOTIFICATION_LOCK_FILE, None, true)
+}
+
+/// Load notification config.
+pub fn config() -> Result<Config, Error> {
+    let content =
+        proxmox_sys::fs::file_read_optional_string(NOTIFICATION_CONFIG_PATH)?.unwrap_or_default();
+
+    let priv_content = proxmox_sys::fs::file_read_optional_string(NOTIFICATION_PRIV_CONFIG_PATH)?
+        .unwrap_or_default();
+
+    Ok(Config::new(&content, &priv_content)?)
+}
+
+/// Save notification config.
+pub fn save_config(config: Config) -> Result<(), Error> {
+    let (cfg, priv_cfg) = config.write()?;
+    crate::replace_backup_config(NOTIFICATION_CONFIG_PATH, cfg.as_bytes())?;
+    crate::replace_secret_config(NOTIFICATION_PRIV_CONFIG_PATH, priv_cfg.as_bytes())?;
+
+    Ok(())
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 02/33] server: rename email_notifications module to notifications
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
  2024-04-12 10:05 ` [pbs-devel] [PATCH proxmox-backup 01/33] pbs-config: add module for loading notification config Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 03/33] notifications: allow sending notifications via proxmox_notify Lukas Wagner
                   ` (33 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

The module will be extended to interact with the proxmox_notify crate,
hence the name change seems to be in order.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/server/mod.rs                                       | 4 ++--
 src/server/{email_notifications.rs => notifications.rs} | 0
 2 files changed, 2 insertions(+), 2 deletions(-)
 rename src/server/{email_notifications.rs => notifications.rs} (100%)

diff --git a/src/server/mod.rs b/src/server/mod.rs
index 4e3b68ac..d2cbc931 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -25,8 +25,8 @@ pub use gc_job::*;
 mod realm_sync_job;
 pub use realm_sync_job::*;
 
-mod email_notifications;
-pub use email_notifications::*;
+pub mod notifications;
+pub use notifications::*;
 
 mod report;
 pub use report::*;
diff --git a/src/server/email_notifications.rs b/src/server/notifications.rs
similarity index 100%
rename from src/server/email_notifications.rs
rename to src/server/notifications.rs
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 03/33] notifications: allow sending notifications via proxmox_notify
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
  2024-04-12 10:05 ` [pbs-devel] [PATCH proxmox-backup 01/33] pbs-config: add module for loading notification config Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 02/33] server: rename email_notifications module to notifications Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 04/33] buildsys: install templates for test notifications Lukas Wagner
                   ` (32 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

  - Set the context in proxmox_notify
  - Add helper function which queues notifications to a spool
    directory
  - Set up a worker task, running in the privileged process, which
    periodically checks the spool directory for queued notifications

The queuing is needed because on PBS we send most if not all
notifications from the proxy-process running as the `backup` user.
However, to have access to the protected passwords/tokens for various
notification endpoints, we need to read the notification config as
root.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/bin/proxmox-backup-api.rs   | 11 ++++
 src/bin/proxmox-backup-proxy.rs |  1 +
 src/server/notifications.rs     | 96 ++++++++++++++++++++++++++++++++-
 3 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/src/bin/proxmox-backup-api.rs b/src/bin/proxmox-backup-api.rs
index e46557a0..d5ce9f3b 100644
--- a/src/bin/proxmox-backup-api.rs
+++ b/src/bin/proxmox-backup-api.rs
@@ -56,6 +56,7 @@ async fn run() -> Result<(), Error> {
     proxmox_backup::server::create_state_dir()?;
     proxmox_backup::server::create_active_operations_dir()?;
     proxmox_backup::server::jobstate::create_jobstate_dir()?;
+    proxmox_backup::server::notifications::create_spool_dir()?;
     proxmox_backup::tape::create_tape_status_dir()?;
     proxmox_backup::tape::create_drive_state_dir()?;
     proxmox_backup::tape::create_changer_state_dir()?;
@@ -72,6 +73,7 @@ async fn run() -> Result<(), Error> {
     let _ = csrf_secret(); // load with lazy_static
 
     proxmox_backup::auth_helpers::setup_auth_context(true);
+    proxmox_backup::server::notifications::init()?;
 
     let backup_user = pbs_config::backup_user()?;
     let mut command_sock = proxmox_rest_server::CommandSocket::new(
@@ -153,6 +155,8 @@ async fn run() -> Result<(), Error> {
         std::thread::sleep(std::time::Duration::from_secs(3));
     });
 
+    start_notification_worker();
+
     server.await?;
     log::info!("server shutting down, waiting for active workers to complete");
     proxmox_rest_server::last_worker_future().await?;
@@ -161,3 +165,10 @@ async fn run() -> Result<(), Error> {
 
     Ok(())
 }
+
+fn start_notification_worker() {
+    let abort_future = proxmox_rest_server::shutdown_future();
+    let future = Box::pin(proxmox_backup::server::notifications::notification_worker());
+    let task = futures::future::select(future, abort_future);
+    tokio::spawn(task);
+}
diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
index f79ec2f5..15444685 100644
--- a/src/bin/proxmox-backup-proxy.rs
+++ b/src/bin/proxmox-backup-proxy.rs
@@ -198,6 +198,7 @@ async fn run() -> Result<(), Error> {
     }
 
     proxmox_backup::auth_helpers::setup_auth_context(false);
+    proxmox_backup::server::notifications::init()?;
 
     let rrd_cache = initialize_rrd_cache()?;
     rrd_cache.apply_journal()?;
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 43b55656..8dde2eea 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -1,20 +1,29 @@
 use anyhow::Error;
-use serde_json::json;
+use const_format::concatcp;
+use serde_json::{json, Value};
+use std::collections::HashMap;
+use std::path::Path;
+use std::time::{Duration, Instant};
 
 use handlebars::{
     Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError, TemplateError,
 };
+use nix::unistd::Uid;
 
 use proxmox_human_byte::HumanByte;
 use proxmox_lang::try_block;
+use proxmox_notify::context::pbs::PBS_CONTEXT;
 use proxmox_schema::ApiType;
 use proxmox_sys::email::sendmail;
+use proxmox_sys::fs::{create_path, CreateOptions};
 
 use pbs_api_types::{
     APTUpdateInfo, DataStoreConfig, DatastoreNotify, GarbageCollectionStatus, Notify,
     SyncJobConfig, TapeBackupJobSetup, User, Userid, VerificationJobConfig,
 };
+use proxmox_notify::{Notification, Severity};
 
+const SPOOL_DIR: &str = concatcp!(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR, "/notifications");
 const GC_OK_TEMPLATE: &str = r###"
 
 Datastore:            {{datastore}}
@@ -283,6 +292,91 @@ lazy_static::lazy_static! {
     };
 }
 
+/// Initialize the notification system by setting context in proxmox_notify
+pub fn init() -> Result<(), Error> {
+    proxmox_notify::context::set_context(&PBS_CONTEXT);
+    Ok(())
+}
+
+/// Create the directory which will be used to temporarily store notifications
+/// which were sent from an unprivileged process.
+pub fn create_spool_dir() -> Result<(), Error> {
+    let backup_user = pbs_config::backup_user()?;
+    let opts = CreateOptions::new()
+        .owner(backup_user.uid)
+        .group(backup_user.gid);
+
+    create_path(SPOOL_DIR, None, Some(opts))?;
+    Ok(())
+}
+
+async fn send_queued_notifications() -> Result<(), Error> {
+    let mut read_dir = tokio::fs::read_dir(SPOOL_DIR).await?;
+
+    while let Some(entry) = read_dir.next_entry().await? {
+        let path = entry.path();
+
+        if let Some(ext) = path.extension() {
+            if ext == "json" {
+                let p = path.clone();
+
+                let res = tokio::task::spawn_blocking(move || {
+                    let config = pbs_config::notifications::config()?;
+                    let bytes = std::fs::read(p)?;
+                    let notification: Notification = serde_json::from_slice(&bytes)?;
+                    proxmox_notify::api::common::send(&config, &notification)?;
+
+                    Ok::<(), Error>(())
+                })
+                .await?;
+
+                if let Err(err) = res {
+                    log::error!("failed to send notification: {err}");
+                }
+
+                // Currently, there is no retry-mechanism in case of failure...
+                // For retries, we'd have to keep track of which targets succeeded/failed
+                // to send, so we do not retry notifying a target which succeeded before.
+                tokio::fs::remove_file(path).await?;
+            }
+        }
+    }
+
+    Ok::<(), Error>(())
+}
+
+/// Worker task to periodically send any queued notifications.
+pub async fn notification_worker() {
+    loop {
+        let delay_target = Instant::now() + Duration::from_secs(5);
+
+        if let Err(err) = send_queued_notifications().await {
+            log::error!("notification worker task error: {err}");
+        }
+
+        tokio::time::sleep_until(tokio::time::Instant::from_std(delay_target)).await;
+    }
+}
+
+fn send_notification(notification: Notification) -> Result<(), Error> {
+    if nix::unistd::ROOT == Uid::current() {
+        let config = pbs_config::notifications::config()?;
+        proxmox_notify::api::common::send(&config, &notification)?;
+    } else {
+        let ser = serde_json::to_vec(&notification)?;
+        let path = Path::new(SPOOL_DIR).join(format!("{id}.json", id = notification.id()));
+
+        let backup_user = pbs_config::backup_user()?;
+        let opts = CreateOptions::new()
+            .owner(backup_user.uid)
+            .group(backup_user.gid);
+        proxmox_sys::fs::replace_file(path, &ser, opts, true)?;
+        log::info!("queued notification (id={id})", id = notification.id())
+    }
+
+    Ok(())
+}
+
 /// Summary of a successful Tape Job
 #[derive(Default)]
 pub struct TapeBackupJobSummary {
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 04/33] buildsys: install templates for test notifications
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (2 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 03/33] notifications: allow sending notifications via proxmox_notify Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 05/33] pbs-config: acl: add /system/notifications as known ACL path Lukas Wagner
                   ` (31 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

The notification stack loads handlebar templates for notifications
from /usr/share/proxmox-backup-server/templates/default/. This commit
modifies the build system to install template files from the
'templates' directory at that location. First, we only have templates
for test notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 Makefile                               |  5 +++--
 debian/proxmox-backup-server.install   |  3 +++
 templates/Makefile                     | 15 +++++++++++++++
 templates/default/test-body.html.hbs   |  1 +
 templates/default/test-body.txt.hbs    |  1 +
 templates/default/test-subject.txt.hbs |  1 +
 6 files changed, 24 insertions(+), 2 deletions(-)
 create mode 100644 templates/Makefile
 create mode 100644 templates/default/test-body.html.hbs
 create mode 100644 templates/default/test-body.txt.hbs
 create mode 100644 templates/default/test-subject.txt.hbs

diff --git a/Makefile b/Makefile
index 0317dd5e..03e93876 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ include defines.mk
 PACKAGE := proxmox-backup
 ARCH := $(DEB_BUILD_ARCH)
 
-SUBDIRS := etc www docs
+SUBDIRS := etc www docs templates
 
 # Binaries usable by users
 USR_BIN := \
@@ -93,7 +93,7 @@ build:
 	cp -a debian \
 	  Cargo.toml src \
 	  $(SUBCRATES) \
-	  docs etc examples tests www zsh-completions \
+	  docs etc examples tests www zsh-completions templates \
 	  defines.mk Makefile \
 	  ./build/
 	rm -f build/Cargo.lock
@@ -211,6 +211,7 @@ install: $(COMPILED_BINS)
 	    install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(LIBEXECDIR)/proxmox-backup/ ;)
 	$(MAKE) -C www install
 	$(MAKE) -C docs install
+	$(MAKE) -C templates install
 
 .PHONY: upload
 upload: UPLOAD_DIST ?= $(DEB_DISTRIBUTION)
diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index ee114ea3..6aff594d 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -41,3 +41,6 @@ usr/share/zsh/vendor-completions/_pmtx
 usr/share/zsh/vendor-completions/_proxmox-backup-debug
 usr/share/zsh/vendor-completions/_proxmox-backup-manager
 usr/share/zsh/vendor-completions/_proxmox-tape
+usr/share/proxmox-backup/templates/default/test-body.txt.hbs
+usr/share/proxmox-backup/templates/default/test-body.html.hbs
+usr/share/proxmox-backup/templates/default/test-subject.txt.hbs
diff --git a/templates/Makefile b/templates/Makefile
new file mode 100644
index 00000000..b35a8bb3
--- /dev/null
+++ b/templates/Makefile
@@ -0,0 +1,15 @@
+include ../defines.mk
+
+NOTIFICATION_TEMPLATES=					\
+	default/test-body.txt.hbs			\
+	default/test-body.html.hbs			\
+	default/test-subject.txt.hbs		\
+
+all:
+
+clean:
+
+install:
+	install -dm755 $(DESTDIR)$(DATAROOTDIR)/proxmox-backup/templates/default
+	$(foreach i,$(NOTIFICATION_TEMPLATES), \
+	    install -m644 $(i) $(DESTDIR)$(DATAROOTDIR)/proxmox-backup/templates/$(i) ;)
diff --git a/templates/default/test-body.html.hbs b/templates/default/test-body.html.hbs
new file mode 100644
index 00000000..26a43dde
--- /dev/null
+++ b/templates/default/test-body.html.hbs
@@ -0,0 +1 @@
+This is a test of the notification target '{{ target }}'.
diff --git a/templates/default/test-body.txt.hbs b/templates/default/test-body.txt.hbs
new file mode 100644
index 00000000..26a43dde
--- /dev/null
+++ b/templates/default/test-body.txt.hbs
@@ -0,0 +1 @@
+This is a test of the notification target '{{ target }}'.
diff --git a/templates/default/test-subject.txt.hbs b/templates/default/test-subject.txt.hbs
new file mode 100644
index 00000000..cb8e1320
--- /dev/null
+++ b/templates/default/test-subject.txt.hbs
@@ -0,0 +1 @@
+Test notification
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 05/33] pbs-config: acl: add /system/notifications as known ACL path
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (3 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 04/33] buildsys: install templates for test notifications Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 06/33] api: add endpoints for querying/testing notification targets Lukas Wagner
                   ` (30 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

This one will be used for configuring the new notification stack.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 pbs-config/src/acl.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pbs-config/src/acl.rs b/pbs-config/src/acl.rs
index a0354a05..f78d69cd 100644
--- a/pbs-config/src/acl.rs
+++ b/pbs-config/src/acl.rs
@@ -100,7 +100,8 @@ pub fn check_acl_path(path: &str) -> Result<(), Error> {
                 return Ok(());
             }
             match components[1] {
-                "certificates" | "disks" | "log" | "status" | "tasks" | "time" => {
+                "certificates" | "disks" | "log" | "notifications" | "status" | "tasks"
+                | "time" => {
                     if components_len == 2 {
                         return Ok(());
                     }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 06/33] api: add endpoints for querying/testing notification targets
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (4 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 05/33] pbs-config: acl: add /system/notifications as known ACL path Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 07/33] api: add endpoints for notification matchers Lukas Wagner
                   ` (29 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

These endpoints require Sys.Audit permissions on
/system/notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/mod.rs                   |  2 +
 src/api2/config/notifications/mod.rs     | 12 +++++
 src/api2/config/notifications/targets.rs | 63 ++++++++++++++++++++++++
 3 files changed, 77 insertions(+)
 create mode 100644 src/api2/config/notifications/mod.rs
 create mode 100644 src/api2/config/notifications/targets.rs

diff --git a/src/api2/config/mod.rs b/src/api2/config/mod.rs
index 6cfeaea1..15dc5db9 100644
--- a/src/api2/config/mod.rs
+++ b/src/api2/config/mod.rs
@@ -11,6 +11,7 @@ pub mod datastore;
 pub mod drive;
 pub mod media_pool;
 pub mod metrics;
+pub mod notifications;
 pub mod prune;
 pub mod remote;
 pub mod sync;
@@ -28,6 +29,7 @@ const SUBDIRS: SubdirMap = &sorted!([
     ("drive", &drive::ROUTER),
     ("media-pool", &media_pool::ROUTER),
     ("metrics", &metrics::ROUTER),
+    ("notifications", &notifications::ROUTER),
     ("prune", &prune::ROUTER),
     ("remote", &remote::ROUTER),
     ("sync", &sync::ROUTER),
diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
new file mode 100644
index 00000000..f75e3910
--- /dev/null
+++ b/src/api2/config/notifications/mod.rs
@@ -0,0 +1,12 @@
+use proxmox_router::list_subdirs_api_method;
+use proxmox_router::{Router, SubdirMap};
+use proxmox_sortable_macro::sortable;
+
+mod targets;
+
+#[sortable]
+const SUBDIRS: SubdirMap = &sorted!([("targets", &targets::ROUTER),]);
+
+pub const ROUTER: Router = Router::new()
+    .get(&list_subdirs_api_method!(SUBDIRS))
+    .subdirs(SUBDIRS);
diff --git a/src/api2/config/notifications/targets.rs b/src/api2/config/notifications/targets.rs
new file mode 100644
index 00000000..c14fe151
--- /dev/null
+++ b/src/api2/config/notifications/targets.rs
@@ -0,0 +1,63 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox_notify::api::Target;
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use proxmox_router::{list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap};
+use proxmox_schema::api;
+use proxmox_sortable_macro::sortable;
+
+use pbs_api_types::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
+
+#[api(
+    protected: true,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of all entities which can be used as notification targets.",
+        type: Array,
+        items: { type: Target },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List all notification targets
+pub fn list_targets(_param: Value, _rpcenv: &mut dyn RpcEnvironment) -> Result<Vec<Target>, Error> {
+    let config = pbs_config::notifications::config()?;
+    let targets = proxmox_notify::api::get_targets(&config)?;
+
+    Ok(targets)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+        }
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Test a given notification target.
+pub fn test_target(name: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+    let config = pbs_config::notifications::config()?;
+    proxmox_notify::api::common::test_target(&config, &name)?;
+    Ok(())
+}
+
+#[sortable]
+const SUBDIRS: SubdirMap = &sorted!([("test", &TEST_ROUTER),]);
+const TEST_ROUTER: Router = Router::new().post(&API_METHOD_TEST_TARGET);
+const ITEM_ROUTER: Router = Router::new()
+    .get(&list_subdirs_api_method!(SUBDIRS))
+    .subdirs(SUBDIRS);
+
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_LIST_TARGETS)
+    .match_all("name", &ITEM_ROUTER);
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 07/33] api: add endpoints for notification matchers
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (5 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 06/33] api: add endpoints for querying/testing notification targets Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 08/33] api: add endpoints for sendmail targets Lukas Wagner
                   ` (28 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

These endpoints require Sys.Audit/Sys.Modify permissions on
/system/notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/notifications/matchers.rs | 170 ++++++++++++++++++++++
 src/api2/config/notifications/mod.rs      |   6 +-
 2 files changed, 175 insertions(+), 1 deletion(-)
 create mode 100644 src/api2/config/notifications/matchers.rs

diff --git a/src/api2/config/notifications/matchers.rs b/src/api2/config/notifications/matchers.rs
new file mode 100644
index 00000000..1091b8fb
--- /dev/null
+++ b/src/api2/config/notifications/matchers.rs
@@ -0,0 +1,170 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox_notify::matcher::{DeleteableMatcherProperty, MatcherConfig, MatcherConfigUpdater};
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use proxmox_router::{Permission, Router, RpcEnvironment};
+use proxmox_schema::api;
+
+use pbs_api_types::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA};
+
+#[api(
+    protected: true,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of matchers.",
+        type: Array,
+        items: { type: MatcherConfig },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List all notification matchers
+pub fn list_matchers(
+    _param: Value,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<MatcherConfig>, Error> {
+    let config = pbs_config::notifications::config()?;
+
+    let matchers = proxmox_notify::api::matcher::get_matchers(&config)?;
+
+    Ok(matchers)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    returns: { type: MatcherConfig },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// Get a notification matcher.
+pub fn get_matcher(name: String, rpcenv: &mut dyn RpcEnvironment) -> Result<MatcherConfig, Error> {
+    let config = pbs_config::notifications::config()?;
+    let matcher = proxmox_notify::api::matcher::get_matcher(&config, &name)?;
+
+    rpcenv["digest"] = hex::encode(config.digest()).into();
+
+    Ok(matcher)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            matcher: {
+                type: MatcherConfig,
+                flatten: true,
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Add a new notification matcher
+pub fn add_matcher(matcher: MatcherConfig, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+
+    proxmox_notify::api::matcher::add_matcher(&mut config, matcher)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            updater: {
+                type: MatcherConfigUpdater,
+                flatten: true,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
+                optional: true,
+                items: {
+                    type: DeleteableMatcherProperty,
+                }
+            },
+            digest: {
+                optional: true,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Update notification matcher
+pub fn update_matcher(
+    name: String,
+    updater: MatcherConfigUpdater,
+    delete: Option<Vec<DeleteableMatcherProperty>>,
+    digest: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    let digest = digest.map(hex::decode).transpose()?;
+
+    proxmox_notify::api::matcher::update_matcher(
+        &mut config,
+        &name,
+        updater,
+        delete.as_deref(),
+        digest.as_deref(),
+    )?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Delete notification matcher
+pub fn delete_matcher(name: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    proxmox_notify::api::matcher::delete_matcher(&mut config, &name)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+const ITEM_ROUTER: Router = Router::new()
+    .get(&API_METHOD_GET_MATCHER)
+    .put(&API_METHOD_UPDATE_MATCHER)
+    .delete(&API_METHOD_DELETE_MATCHER);
+
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_LIST_MATCHERS)
+    .post(&API_METHOD_ADD_MATCHER)
+    .match_all("name", &ITEM_ROUTER);
diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index f75e3910..361dc593 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -2,10 +2,14 @@ use proxmox_router::list_subdirs_api_method;
 use proxmox_router::{Router, SubdirMap};
 use proxmox_sortable_macro::sortable;
 
+mod matchers;
 mod targets;
 
 #[sortable]
-const SUBDIRS: SubdirMap = &sorted!([("targets", &targets::ROUTER),]);
+const SUBDIRS: SubdirMap = &sorted!([
+    ("targets", &targets::ROUTER),
+    ("matchers", &matchers::ROUTER),
+]);
 
 pub const ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(SUBDIRS))
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 08/33] api: add endpoints for sendmail targets
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (6 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 07/33] api: add endpoints for notification matchers Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 09/33] api: add endpoints for smtp targets Lukas Wagner
                   ` (27 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

These endpoints require Sys.Audit/Sys.Modify permissions on
/system/notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/notifications/mod.rs      |   9 ++
 src/api2/config/notifications/sendmail.rs | 178 ++++++++++++++++++++++
 2 files changed, 187 insertions(+)
 create mode 100644 src/api2/config/notifications/sendmail.rs

diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index 361dc593..76233b6c 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -3,10 +3,12 @@ use proxmox_router::{Router, SubdirMap};
 use proxmox_sortable_macro::sortable;
 
 mod matchers;
+mod sendmail;
 mod targets;
 
 #[sortable]
 const SUBDIRS: SubdirMap = &sorted!([
+    ("endpoints", &ENDPOINT_ROUTER),
     ("targets", &targets::ROUTER),
     ("matchers", &matchers::ROUTER),
 ]);
@@ -14,3 +16,10 @@ const SUBDIRS: SubdirMap = &sorted!([
 pub const ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(SUBDIRS))
     .subdirs(SUBDIRS);
+
+#[sortable]
+const ENDPOINT_SUBDIRS: SubdirMap = &sorted!([("sendmail", &sendmail::ROUTER),]);
+
+const ENDPOINT_ROUTER: Router = Router::new()
+    .get(&list_subdirs_api_method!(ENDPOINT_SUBDIRS))
+    .subdirs(ENDPOINT_SUBDIRS);
diff --git a/src/api2/config/notifications/sendmail.rs b/src/api2/config/notifications/sendmail.rs
new file mode 100644
index 00000000..d13877cb
--- /dev/null
+++ b/src/api2/config/notifications/sendmail.rs
@@ -0,0 +1,178 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox_notify::endpoints::sendmail::{
+    DeleteableSendmailProperty, SendmailConfig, SendmailConfigUpdater,
+};
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use proxmox_router::{Permission, Router, RpcEnvironment};
+use proxmox_schema::api;
+
+use pbs_api_types::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA};
+
+#[api(
+    protected: true,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of sendmail endpoints.",
+        type: Array,
+        items: { type: SendmailConfig },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List all sendmail endpoints
+pub fn list_endpoints(
+    _param: Value,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<SendmailConfig>, Error> {
+    let config = pbs_config::notifications::config()?;
+
+    let endpoints = proxmox_notify::api::sendmail::get_endpoints(&config)?;
+
+    Ok(endpoints)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    returns: { type: SendmailConfig },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// Get a sendmail endpoint.
+pub fn get_endpoint(
+    name: String,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<SendmailConfig, Error> {
+    let config = pbs_config::notifications::config()?;
+    let endpoint = proxmox_notify::api::sendmail::get_endpoint(&config, &name)?;
+
+    rpcenv["digest"] = hex::encode(config.digest()).into();
+
+    Ok(endpoint)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            endpoint: {
+                type: SendmailConfig,
+                flatten: true,
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Add a new sendmail endpoint.
+pub fn add_endpoint(
+    endpoint: SendmailConfig,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+
+    proxmox_notify::api::sendmail::add_endpoint(&mut config, endpoint)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            updater: {
+                type: SendmailConfigUpdater,
+                flatten: true,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
+                optional: true,
+                items: {
+                    type: DeleteableSendmailProperty,
+                }
+            },
+            digest: {
+                optional: true,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Update sendmail endpoint.
+pub fn update_endpoint(
+    name: String,
+    updater: SendmailConfigUpdater,
+    delete: Option<Vec<DeleteableSendmailProperty>>,
+    digest: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    let digest = digest.map(hex::decode).transpose()?;
+
+    proxmox_notify::api::sendmail::update_endpoint(
+        &mut config,
+        &name,
+        updater,
+        delete.as_deref(),
+        digest.as_deref(),
+    )?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Delete sendmail endpoint.
+pub fn delete_endpoint(name: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    proxmox_notify::api::sendmail::delete_endpoint(&mut config, &name)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+const ITEM_ROUTER: Router = Router::new()
+    .get(&API_METHOD_GET_ENDPOINT)
+    .put(&API_METHOD_UPDATE_ENDPOINT)
+    .delete(&API_METHOD_DELETE_ENDPOINT);
+
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_LIST_ENDPOINTS)
+    .post(&API_METHOD_ADD_ENDPOINT)
+    .match_all("name", &ITEM_ROUTER);
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 09/33] api: add endpoints for smtp targets
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (7 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 08/33] api: add endpoints for sendmail targets Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 10/33] api: add endpoints for gotify targets Lukas Wagner
                   ` (26 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

These endpoints require Sys.Audit/Sys.Modify permissions on
/system/notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/notifications/mod.rs  |   4 +-
 src/api2/config/notifications/smtp.rs | 191 ++++++++++++++++++++++++++
 2 files changed, 194 insertions(+), 1 deletion(-)
 create mode 100644 src/api2/config/notifications/smtp.rs

diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index 76233b6c..75b96ce1 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -4,6 +4,7 @@ use proxmox_sortable_macro::sortable;
 
 mod matchers;
 mod sendmail;
+mod smtp;
 mod targets;
 
 #[sortable]
@@ -18,7 +19,8 @@ pub const ROUTER: Router = Router::new()
     .subdirs(SUBDIRS);
 
 #[sortable]
-const ENDPOINT_SUBDIRS: SubdirMap = &sorted!([("sendmail", &sendmail::ROUTER),]);
+const ENDPOINT_SUBDIRS: SubdirMap =
+    &sorted!([("sendmail", &sendmail::ROUTER), ("smtp", &smtp::ROUTER),]);
 
 const ENDPOINT_ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(ENDPOINT_SUBDIRS))
diff --git a/src/api2/config/notifications/smtp.rs b/src/api2/config/notifications/smtp.rs
new file mode 100644
index 00000000..d73e33e8
--- /dev/null
+++ b/src/api2/config/notifications/smtp.rs
@@ -0,0 +1,191 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox_notify::endpoints::smtp::{
+    DeleteableSmtpProperty, SmtpConfig, SmtpConfigUpdater, SmtpPrivateConfig,
+    SmtpPrivateConfigUpdater,
+};
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use proxmox_router::{Permission, Router, RpcEnvironment};
+use proxmox_schema::api;
+
+use pbs_api_types::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA};
+
+#[api(
+    protected: true,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of smtp endpoints.",
+        type: Array,
+        items: { type: SmtpConfig },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List all smtp endpoints
+pub fn list_endpoints(
+    _param: Value,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<SmtpConfig>, Error> {
+    let config = pbs_config::notifications::config()?;
+
+    let endpoints = proxmox_notify::api::smtp::get_endpoints(&config)?;
+
+    Ok(endpoints)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    returns: { type: SmtpConfig },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// Get a smtp endpoint.
+pub fn get_endpoint(name: String, rpcenv: &mut dyn RpcEnvironment) -> Result<SmtpConfig, Error> {
+    let config = pbs_config::notifications::config()?;
+    let endpoint = proxmox_notify::api::smtp::get_endpoint(&config, &name)?;
+
+    rpcenv["digest"] = hex::encode(config.digest()).into();
+
+    Ok(endpoint)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            endpoint: {
+                type: SmtpConfig,
+                flatten: true,
+            },
+            password: {
+                optional: true,
+                description: "SMTP authentication password"
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Add a new smtp endpoint.
+pub fn add_endpoint(
+    endpoint: SmtpConfig,
+    password: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    let private_endpoint_config = SmtpPrivateConfig {
+        name: endpoint.name.clone(),
+        password,
+    };
+
+    proxmox_notify::api::smtp::add_endpoint(&mut config, endpoint, private_endpoint_config)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            updater: {
+                type: SmtpConfigUpdater,
+                flatten: true,
+            },
+            password: {
+                description: "SMTP authentication password",
+                optional: true,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
+                optional: true,
+                items: {
+                    type: DeleteableSmtpProperty,
+                }
+            },
+            digest: {
+                optional: true,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Update smtp endpoint.
+pub fn update_endpoint(
+    name: String,
+    updater: SmtpConfigUpdater,
+    password: Option<String>,
+    delete: Option<Vec<DeleteableSmtpProperty>>,
+    digest: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    let digest = digest.map(hex::decode).transpose()?;
+
+    proxmox_notify::api::smtp::update_endpoint(
+        &mut config,
+        &name,
+        updater,
+        SmtpPrivateConfigUpdater { password },
+        delete.as_deref(),
+        digest.as_deref(),
+    )?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Delete smtp endpoint.
+pub fn delete_endpoint(name: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    proxmox_notify::api::smtp::delete_endpoint(&mut config, &name)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+const ITEM_ROUTER: Router = Router::new()
+    .get(&API_METHOD_GET_ENDPOINT)
+    .put(&API_METHOD_UPDATE_ENDPOINT)
+    .delete(&API_METHOD_DELETE_ENDPOINT);
+
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_LIST_ENDPOINTS)
+    .post(&API_METHOD_ADD_ENDPOINT)
+    .match_all("name", &ITEM_ROUTER);
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 10/33] api: add endpoints for gotify targets
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (8 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 09/33] api: add endpoints for smtp targets Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 11/33] api: add endpoints for querying known notification values/fields Lukas Wagner
                   ` (25 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

These endpoints require Sys.Audit/Sys.Modify permissions on
/system/notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/notifications/gotify.rs | 190 ++++++++++++++++++++++++
 src/api2/config/notifications/mod.rs    |   8 +-
 2 files changed, 196 insertions(+), 2 deletions(-)
 create mode 100644 src/api2/config/notifications/gotify.rs

diff --git a/src/api2/config/notifications/gotify.rs b/src/api2/config/notifications/gotify.rs
new file mode 100644
index 00000000..ed49f037
--- /dev/null
+++ b/src/api2/config/notifications/gotify.rs
@@ -0,0 +1,190 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox_notify::endpoints::gotify::{
+    DeleteableGotifyProperty, GotifyConfig, GotifyConfigUpdater, GotifyPrivateConfig,
+    GotifyPrivateConfigUpdater,
+};
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use proxmox_router::{Permission, Router, RpcEnvironment};
+use proxmox_schema::api;
+
+use pbs_api_types::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA};
+
+#[api(
+    protected: true,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of gotify endpoints.",
+        type: Array,
+        items: { type: GotifyConfig },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List all gotify endpoints
+pub fn list_endpoints(
+    _param: Value,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<GotifyConfig>, Error> {
+    let config = pbs_config::notifications::config()?;
+
+    let endpoints = proxmox_notify::api::gotify::get_endpoints(&config)?;
+
+    Ok(endpoints)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    returns: { type: GotifyConfig },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// Get a gotify endpoint.
+pub fn get_endpoint(name: String, rpcenv: &mut dyn RpcEnvironment) -> Result<GotifyConfig, Error> {
+    let config = pbs_config::notifications::config()?;
+    let endpoint = proxmox_notify::api::gotify::get_endpoint(&config, &name)?;
+
+    rpcenv["digest"] = hex::encode(config.digest()).into();
+
+    Ok(endpoint)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            endpoint: {
+                type: GotifyConfig,
+                flatten: true,
+            },
+            token: {
+                description: "Authentication token",
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Add a new gotify endpoint.
+pub fn add_endpoint(
+    endpoint: GotifyConfig,
+    token: String,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    let private_endpoint_config = GotifyPrivateConfig {
+        name: endpoint.name.clone(),
+        token,
+    };
+
+    proxmox_notify::api::gotify::add_endpoint(&mut config, endpoint, private_endpoint_config)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            updater: {
+                type: GotifyConfigUpdater,
+                flatten: true,
+            },
+            token: {
+                description: "Authentication token",
+                optional: true,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
+                optional: true,
+                items: {
+                    type: DeleteableGotifyProperty,
+                }
+            },
+            digest: {
+                optional: true,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Update gotify endpoint.
+pub fn update_endpoint(
+    name: String,
+    updater: GotifyConfigUpdater,
+    token: Option<String>,
+    delete: Option<Vec<DeleteableGotifyProperty>>,
+    digest: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    let digest = digest.map(hex::decode).transpose()?;
+
+    proxmox_notify::api::gotify::update_endpoint(
+        &mut config,
+        &name,
+        updater,
+        GotifyPrivateConfigUpdater { token },
+        delete.as_deref(),
+        digest.as_deref(),
+    )?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            }
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_MODIFY, false),
+    },
+)]
+/// Delete gotify endpoint.
+pub fn delete_endpoint(name: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+    let _lock = pbs_config::notifications::lock_config()?;
+    let mut config = pbs_config::notifications::config()?;
+    proxmox_notify::api::gotify::delete_gotify_endpoint(&mut config, &name)?;
+
+    pbs_config::notifications::save_config(config)?;
+    Ok(())
+}
+
+const ITEM_ROUTER: Router = Router::new()
+    .get(&API_METHOD_GET_ENDPOINT)
+    .put(&API_METHOD_UPDATE_ENDPOINT)
+    .delete(&API_METHOD_DELETE_ENDPOINT);
+
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_LIST_ENDPOINTS)
+    .post(&API_METHOD_ADD_ENDPOINT)
+    .match_all("name", &ITEM_ROUTER);
diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index 75b96ce1..9c4a0cd9 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -2,6 +2,7 @@ use proxmox_router::list_subdirs_api_method;
 use proxmox_router::{Router, SubdirMap};
 use proxmox_sortable_macro::sortable;
 
+mod gotify;
 mod matchers;
 mod sendmail;
 mod smtp;
@@ -19,8 +20,11 @@ pub const ROUTER: Router = Router::new()
     .subdirs(SUBDIRS);
 
 #[sortable]
-const ENDPOINT_SUBDIRS: SubdirMap =
-    &sorted!([("sendmail", &sendmail::ROUTER), ("smtp", &smtp::ROUTER),]);
+const ENDPOINT_SUBDIRS: SubdirMap = &sorted!([
+    ("gotify", &gotify::ROUTER),
+    ("sendmail", &sendmail::ROUTER),
+    ("smtp", &smtp::ROUTER),
+]);
 
 const ENDPOINT_ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(ENDPOINT_SUBDIRS))
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 11/33] api: add endpoints for querying known notification values/fields
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (9 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 10/33] api: add endpoints for gotify targets Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 12/33] api-types: api: datatore: add notification-mode parameter Lukas Wagner
                   ` (24 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

These endpoints require Sys.Audit/Sys.Modify permissions on
/system/notifications.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/notifications/mod.rs | 146 ++++++++++++++++++++++++++-
 1 file changed, 145 insertions(+), 1 deletion(-)

diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index 9c4a0cd9..2c8b780e 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -1,7 +1,16 @@
-use proxmox_router::list_subdirs_api_method;
+use anyhow::Error;
+use serde::Serialize;
+use serde_json::Value;
+
+use proxmox_router::{list_subdirs_api_method, Permission, RpcEnvironment};
 use proxmox_router::{Router, SubdirMap};
+use proxmox_schema::api;
 use proxmox_sortable_macro::sortable;
 
+use crate::api2::config::datastore::list_datastores;
+use crate::api2::config::media_pool::list_pools;
+use pbs_api_types::PRIV_SYS_AUDIT;
+
 mod gotify;
 mod matchers;
 mod sendmail;
@@ -11,6 +20,8 @@ mod targets;
 #[sortable]
 const SUBDIRS: SubdirMap = &sorted!([
     ("endpoints", &ENDPOINT_ROUTER),
+    ("fields", &FIELD_ROUTER),
+    ("values", &VALUE_ROUTER),
     ("targets", &targets::ROUTER),
     ("matchers", &matchers::ROUTER),
 ]);
@@ -29,3 +40,136 @@ const ENDPOINT_SUBDIRS: SubdirMap = &sorted!([
 const ENDPOINT_ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(ENDPOINT_SUBDIRS))
     .subdirs(ENDPOINT_SUBDIRS);
+
+const FIELD_ROUTER: Router = Router::new().get(&API_METHOD_GET_FIELDS);
+const VALUE_ROUTER: Router = Router::new().get(&API_METHOD_GET_VALUES);
+
+#[api]
+#[derive(Serialize)]
+/// A matchable field.
+pub struct MatchableField {
+    /// Name of the field
+    name: String,
+}
+
+#[api]
+#[derive(Serialize)]
+/// A matchable metadata field value
+pub struct MatchableValue {
+    /// Field this value belongs to.
+    field: String,
+    /// Notification metadata value known by the system
+    value: String,
+    ///  Additional comment for this value.
+    comment: Option<String>,
+    /// Set if `value` was generated by the system and can safely be used
+    /// as a base for translations.
+    internal: Option<bool>,
+}
+
+#[api(
+    protected: false,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of known metadata fields.",
+        type: Array,
+        items: { type: MatchableField },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// Get all known metadata fields.
+pub fn get_fields(
+    _param: Value,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<MatchableField>, Error> {
+    let fields = vec![
+        MatchableField {
+            name: "type".into(),
+        },
+        MatchableField {
+            name: "hostname".into(),
+        },
+        MatchableField {
+            name: "datastore".into(),
+        },
+        MatchableField {
+            name: "media-pool".into(),
+        },
+    ];
+
+    Ok(fields)
+}
+
+#[api(
+    protected: false,
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of known metadata field values.",
+        type: Array,
+        items: { type: MatchableValue },
+    },
+    access: {
+        permission: &Permission::Privilege(&["system", "notifications"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List all known, matchable metadata field values.
+pub fn get_values(
+    param: Value,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<MatchableValue>, Error> {
+    let mut values = Vec::new();
+
+    let datastores = list_datastores(param, rpcenv)?;
+
+    for datastore in datastores {
+        values.push(MatchableValue {
+            field: "datastore".into(),
+            value: datastore.name.clone(),
+            comment: datastore.comment.clone(),
+            internal: Some(false),
+        });
+    }
+
+    let pools = list_pools(rpcenv)?;
+    for pool in pools {
+        values.push(MatchableValue {
+            field: "media-pool".into(),
+            value: pool.name.clone(),
+            comment: None,
+            internal: Some(false),
+        });
+    }
+
+    values.push(MatchableValue {
+        field: "hostname".into(),
+        value: proxmox_sys::nodename().into(),
+        comment: None,
+        internal: Some(false),
+    });
+
+    for ty in [
+        "prune",
+        "gc",
+        "sync",
+        "verify",
+        "tape-backup",
+        "tape-load",
+        "package-updates",
+        "acme",
+    ] {
+        values.push(MatchableValue {
+            field: "type".into(),
+            value: ty.into(),
+            comment: None,
+            internal: Some(true),
+        });
+    }
+
+    Ok(values)
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 12/33] api-types: api: datatore: add notification-mode parameter
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (10 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 11/33] api: add endpoints for querying known notification values/fields Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 13/33] api-types: api: tape: " Lukas Wagner
                   ` (23 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

This one lets the user choose between the old notification behavior
(selecting an email address/user and always/error/never behavior per
datastore) and the new one (emit notification events to the
notification system)

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 pbs-api-types/src/datastore.rs | 22 ++++++++++++++++++++++
 src/api2/config/datastore.rs   |  9 +++++++++
 2 files changed, 31 insertions(+)

diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 5e13c157..a5da3e94 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -309,6 +309,10 @@ pub struct DataStoreConfig {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub notify: Option<String>,
 
+    /// Opt-in into the new notification system
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub notification_mode: Option<NotificationMode>,
+
     /// Datastore tuning options
     #[serde(skip_serializing_if = "Option::is_none")]
     pub tuning: Option<String>,
@@ -318,6 +322,23 @@ pub struct DataStoreConfig {
     pub maintenance_mode: Option<String>,
 }
 
+#[api]
+#[derive(Serialize, Deserialize, Updater, Clone, PartialEq, Default)]
+#[serde(rename_all = "kebab-case")]
+/// Configure how notifications for this datastore should be sent.
+/// `legacy-sendmail` sends email notifications to the user configured
+/// in `notify-user` via the system's `sendmail` executable.
+/// `notification-system` emits matchable notification events to the
+/// notification system.
+pub enum NotificationMode {
+    /// Send notifications via the system's sendmail command to the user
+    /// configured in `notify-user`
+    #[default]
+    LegacySendmail,
+    /// Emit notification events to the notification system
+    NotificationSystem,
+}
+
 impl DataStoreConfig {
     pub fn new(name: String, path: String) -> Self {
         Self {
@@ -330,6 +351,7 @@ impl DataStoreConfig {
             verify_new: None,
             notify_user: None,
             notify: None,
+            notification_mode: None,
             tuning: None,
             maintenance_mode: None,
         }
diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
index 3081e1f4..9a64c6a6 100644
--- a/src/api2/config/datastore.rs
+++ b/src/api2/config/datastore.rs
@@ -222,6 +222,8 @@ pub enum DeletableProperty {
     NotifyUser,
     /// Delete the notify property
     Notify,
+    /// Delete the notification-mode property
+    NotificationMode,
     /// Delete the tuning property
     Tuning,
     /// Delete the maintenance-mode property
@@ -315,6 +317,9 @@ pub fn update_datastore(
                 DeletableProperty::NotifyUser => {
                     data.notify_user = None;
                 }
+                DeletableProperty::NotificationMode => {
+                    data.notification_mode = None;
+                }
                 DeletableProperty::Tuning => {
                     data.tuning = None;
                 }
@@ -385,6 +390,10 @@ pub fn update_datastore(
         data.notify_user = update.notify_user;
     }
 
+    if update.notification_mode.is_some() {
+        data.notification_mode = update.notification_mode;
+    }
+
     if update.tuning.is_some() {
         data.tuning = update.tuning;
     }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 13/33] api-types: api: tape: add notification-mode parameter
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (11 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 12/33] api-types: api: datatore: add notification-mode parameter Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system Lukas Wagner
                   ` (22 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Same as with datastores, this option determines whether we send
notifications the old way (send email via sendmail to a user's email
address) or the new way (emit matchable notification events to the
notification stack).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 pbs-api-types/src/jobs.rs          | 8 +++++---
 src/api2/config/tape_backup_job.rs | 8 ++++++++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/pbs-api-types/src/jobs.rs b/pbs-api-types/src/jobs.rs
index 6fb9b187..868702bc 100644
--- a/pbs-api-types/src/jobs.rs
+++ b/pbs-api-types/src/jobs.rs
@@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize};
 use proxmox_schema::*;
 
 use crate::{
-    Authid, BackupNamespace, BackupType, RateLimitConfig, Userid, BACKUP_GROUP_SCHEMA,
-    BACKUP_NAMESPACE_SCHEMA, BACKUP_NS_RE, DATASTORE_SCHEMA, DRIVE_NAME_SCHEMA,
-    MEDIA_POOL_NAME_SCHEMA, NS_MAX_DEPTH_REDUCED_SCHEMA, PROXMOX_SAFE_ID_FORMAT,
+    Authid, BackupNamespace, BackupType, NotificationMode, RateLimitConfig, Userid,
+    BACKUP_GROUP_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_NS_RE, DATASTORE_SCHEMA,
+    DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA, NS_MAX_DEPTH_REDUCED_SCHEMA, PROXMOX_SAFE_ID_FORMAT,
     PROXMOX_SAFE_ID_REGEX_STR, REMOTE_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
 };
 
@@ -325,6 +325,8 @@ pub struct TapeBackupJobSetup {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub notify_user: Option<Userid>,
     #[serde(skip_serializing_if = "Option::is_none")]
+    pub notification_mode: Option<NotificationMode>,
+    #[serde(skip_serializing_if = "Option::is_none")]
     pub group_filter: Option<Vec<GroupFilter>>,
     #[serde(skip_serializing_if = "Option::is_none", default)]
     pub ns: Option<BackupNamespace>,
diff --git a/src/api2/config/tape_backup_job.rs b/src/api2/config/tape_backup_job.rs
index 386ff530..e425cbb5 100644
--- a/src/api2/config/tape_backup_job.rs
+++ b/src/api2/config/tape_backup_job.rs
@@ -132,6 +132,8 @@ pub enum DeletableProperty {
     LatestOnly,
     /// Delete the 'notify-user' property
     NotifyUser,
+    /// Delete the 'notification-mode' property
+    NotificationMode,
     /// Delete the 'group_filter' property
     GroupFilter,
     /// Delete the 'max-depth' property
@@ -202,6 +204,9 @@ pub fn update_tape_backup_job(
                 DeletableProperty::NotifyUser => {
                     data.setup.notify_user = None;
                 }
+                DeletableProperty::NotificationMode => {
+                    data.setup.notification_mode = None;
+                }
                 DeletableProperty::Schedule => {
                     data.schedule = None;
                 }
@@ -243,6 +248,9 @@ pub fn update_tape_backup_job(
     if update.setup.notify_user.is_some() {
         data.setup.notify_user = update.setup.notify_user;
     }
+    if update.setup.notification_mode.is_some() {
+        data.setup.notification_mode = update.setup.notification_mode;
+    }
     if update.setup.group_filter.is_some() {
         data.setup.group_filter = update.setup.group_filter;
     }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (12 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 13/33] api-types: api: tape: " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-15  9:41   ` Gabriel Goller
  2024-04-16  9:37   ` Gabriel Goller
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 15/33] server: notifications: send prune " Lukas Wagner
                   ` (21 subsequent siblings)
  35 siblings, 2 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

If the `notification-mode` parameter is set to `legacy-sendmail`, then
we still use the new infrastructure, but don't consider the
notification config and use a hard-coded sendmail endpoint directly.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install     |   4 +
 src/api2/pull.rs                         |   2 +-
 src/server/gc_job.rs                     |  10 +-
 src/server/notifications.rs              | 184 +++++++----------------
 src/server/verify_job.rs                 |   3 +-
 templates/Makefile                       |   4 +
 templates/default/gc-err-body.txt.hbs    |   8 +
 templates/default/gc-err-subject.txt.hbs |   1 +
 templates/default/gc-ok-body.txt.hbs     |  23 +++
 templates/default/gc-ok-subject.txt.hbs  |   1 +
 10 files changed, 102 insertions(+), 138 deletions(-)
 create mode 100644 templates/default/gc-err-body.txt.hbs
 create mode 100644 templates/default/gc-err-subject.txt.hbs
 create mode 100644 templates/default/gc-ok-body.txt.hbs
 create mode 100644 templates/default/gc-ok-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 6aff594d..197c070f 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -41,6 +41,10 @@ usr/share/zsh/vendor-completions/_pmtx
 usr/share/zsh/vendor-completions/_proxmox-backup-debug
 usr/share/zsh/vendor-completions/_proxmox-backup-manager
 usr/share/zsh/vendor-completions/_proxmox-tape
+usr/share/proxmox-backup/templates/default/gc-err-body.txt.hbs
+usr/share/proxmox-backup/templates/default/gc-ok-body.txt.hbs
+usr/share/proxmox-backup/templates/default/gc-err-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/gc-ok-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.html.hbs
 usr/share/proxmox-backup/templates/default/test-subject.txt.hbs
diff --git a/src/api2/pull.rs b/src/api2/pull.rs
index 59db3660..7fe2267a 100644
--- a/src/api2/pull.rs
+++ b/src/api2/pull.rs
@@ -114,7 +114,7 @@ pub fn do_sync_job(
         bail!("can't sync to same datastore");
     }
 
-    let (email, notify) = crate::server::lookup_datastore_notify_settings(&sync_job.store);
+    let (email, notify, _) = crate::server::lookup_datastore_notify_settings(&sync_job.store);
 
     let upid_str = WorkerTask::spawn(
         &worker_type,
diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
index 41375d72..ff5bdccf 100644
--- a/src/server/gc_job.rs
+++ b/src/server/gc_job.rs
@@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
 ) -> Result<String, Error> {
     let store = datastore.name().to_string();
 
-    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
-
     let worker_type = job.jobtype().to_string();
     let upid_str = WorkerTask::new_thread(
         &worker_type,
@@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
                 eprintln!("could not finish job state for {}: {err}", job.jobtype());
             }
 
-            if let Some(email) = email {
-                let gc_status = datastore.last_gc_status();
-                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
-                    eprintln!("send gc notification failed: {err}");
-                }
+            let gc_status = datastore.last_gc_status();
+            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
+                eprintln!("send gc notification failed: {err}");
             }
 
             result
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 8dde2eea..720ae5ff 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -1,16 +1,13 @@
 use anyhow::Error;
 use const_format::concatcp;
-use serde_json::{json, Value};
+use serde_json::json;
 use std::collections::HashMap;
 use std::path::Path;
 use std::time::{Duration, Instant};
 
-use handlebars::{
-    Context, Handlebars, Helper, HelperResult, Output, RenderContext, RenderError, TemplateError,
-};
+use handlebars::{Handlebars, TemplateError};
 use nix::unistd::Uid;
 
-use proxmox_human_byte::HumanByte;
 use proxmox_lang::try_block;
 use proxmox_notify::context::pbs::PBS_CONTEXT;
 use proxmox_schema::ApiType;
@@ -18,52 +15,13 @@ use proxmox_sys::email::sendmail;
 use proxmox_sys::fs::{create_path, CreateOptions};
 
 use pbs_api_types::{
-    APTUpdateInfo, DataStoreConfig, DatastoreNotify, GarbageCollectionStatus, Notify,
-    SyncJobConfig, TapeBackupJobSetup, User, Userid, VerificationJobConfig,
+    APTUpdateInfo, DataStoreConfig, DatastoreNotify, GarbageCollectionStatus, NotificationMode,
+    Notify, SyncJobConfig, TapeBackupJobSetup, User, Userid, VerificationJobConfig,
 };
-use proxmox_notify::{Notification, Severity};
+use proxmox_notify::endpoints::sendmail::{SendmailConfig, SendmailEndpoint};
+use proxmox_notify::{Endpoint, Notification, Severity};
 
 const SPOOL_DIR: &str = concatcp!(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR, "/notifications");
-const GC_OK_TEMPLATE: &str = r###"
-
-Datastore:            {{datastore}}
-Task ID:              {{status.upid}}
-Index file count:     {{status.index-file-count}}
-
-Removed garbage:      {{human-bytes status.removed-bytes}}
-Removed chunks:       {{status.removed-chunks}}
-Removed bad chunks:   {{status.removed-bad}}
-
-Leftover bad chunks:  {{status.still-bad}}
-Pending removals:     {{human-bytes status.pending-bytes}} (in {{status.pending-chunks}} chunks)
-
-Original Data usage:  {{human-bytes status.index-data-bytes}}
-On-Disk usage:        {{human-bytes status.disk-bytes}} ({{relative-percentage status.disk-bytes status.index-data-bytes}})
-On-Disk chunks:       {{status.disk-chunks}}
-
-Deduplication Factor: {{deduplication-factor}}
-
-Garbage collection successful.
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#DataStore-{{datastore}}>
-
-"###;
-
-const GC_ERR_TEMPLATE: &str = r###"
-
-Datastore: {{datastore}}
-
-Garbage collection failed: {{error}}
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
-
-"###;
 
 const VERIFY_OK_TEMPLATE: &str = r###"
 
@@ -259,12 +217,6 @@ lazy_static::lazy_static! {
             hb.set_strict_mode(true);
             hb.register_escape_fn(handlebars::no_escape);
 
-            hb.register_helper("human-bytes", Box::new(handlebars_humam_bytes_helper));
-            hb.register_helper("relative-percentage", Box::new(handlebars_relative_percentage_helper));
-
-            hb.register_template_string("gc_ok_template", GC_OK_TEMPLATE)?;
-            hb.register_template_string("gc_err_template", GC_ERR_TEMPLATE)?;
-
             hb.register_template_string("verify_ok_template", VERIFY_OK_TEMPLATE)?;
             hb.register_template_string("verify_err_template", VERIFY_ERR_TEMPLATE)?;
 
@@ -377,6 +329,19 @@ fn send_notification(notification: Notification) -> Result<(), Error> {
     Ok(())
 }
 
+fn send_sendmail_legacy_notification(notification: Notification, email: &str) -> Result<(), Error> {
+    let endpoint = SendmailEndpoint {
+        config: SendmailConfig {
+            mailto: vec![email.into()],
+            ..Default::default()
+        },
+    };
+
+    endpoint.send(&notification)?;
+
+    Ok(())
+}
+
 /// Summary of a successful Tape Job
 #[derive(Default)]
 pub struct TapeBackupJobSummary {
@@ -413,21 +378,10 @@ fn send_job_status_mail(email: &str, subject: &str, text: &str) -> Result<(), Er
 }
 
 pub fn send_gc_status(
-    email: &str,
-    notify: DatastoreNotify,
     datastore: &str,
     status: &GarbageCollectionStatus,
     result: &Result<(), Error>,
 ) -> Result<(), Error> {
-    match notify.gc {
-        None => { /* send notifications by default */ }
-        Some(notify) => {
-            if notify == Notify::Never || (result.is_ok() && notify == Notify::Error) {
-                return Ok(());
-            }
-        }
-    }
-
     let (fqdn, port) = get_server_url();
     let mut data = json!({
         "datastore": datastore,
@@ -435,7 +389,7 @@ pub fn send_gc_status(
         "port": port,
     });
 
-    let text = match result {
+    let (severity, template) = match result {
         Ok(()) => {
             let deduplication_factor = if status.disk_bytes > 0 {
                 (status.index_data_bytes as f64) / (status.disk_bytes as f64)
@@ -446,20 +400,38 @@ pub fn send_gc_status(
             data["status"] = json!(status);
             data["deduplication-factor"] = format!("{:.2}", deduplication_factor).into();
 
-            HANDLEBARS.render("gc_ok_template", &data)?
+            (Severity::Info, "gc-ok")
         }
         Err(err) => {
             data["error"] = err.to_string().into();
-            HANDLEBARS.render("gc_err_template", &data)?
+            (Severity::Error, "gc-err")
         }
     };
+    let metadata = HashMap::from([
+        ("datastore".into(), datastore.into()),
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "gc".into()),
+    ]);
 
-    let subject = match result {
-        Ok(()) => format!("Garbage Collect Datastore '{datastore}' successful"),
-        Err(_) => format!("Garbage Collect Datastore '{datastore}' failed"),
-    };
+    let notification = Notification::from_template(severity, template, data, metadata);
 
-    send_job_status_mail(email, &subject, &text)?;
+    let (email, notify, mode) = lookup_datastore_notify_settings(datastore);
+    match mode {
+        NotificationMode::LegacySendmail => {
+            let notify = notify.gc.unwrap_or(Notify::Always);
+
+            if notify == Notify::Never || (result.is_ok() && notify == Notify::Error) {
+                return Ok(());
+            }
+
+            if let Some(email) = email {
+                send_sendmail_legacy_notification(notification, &email)?;
+            }
+        }
+        NotificationMode::NotificationSystem => {
+            send_notification(notification)?;
+        }
+    }
 
     Ok(())
 }
@@ -519,8 +491,8 @@ pub fn send_prune_status(
     result: &Result<(), Error>,
 ) -> Result<(), Error> {
     let (email, notify) = match lookup_datastore_notify_settings(store) {
-        (Some(email), notify) => (email, notify),
-        (None, _) => return Ok(()),
+        (Some(email), notify, _) => (email, notify),
+        (None, _, _) => return Ok(()),
     };
 
     let notify_prune = notify.prune.unwrap_or(Notify::Error);
@@ -755,7 +727,9 @@ pub fn lookup_user_email(userid: &Userid) -> Option<String> {
 }
 
 /// Lookup Datastore notify settings
-pub fn lookup_datastore_notify_settings(store: &str) -> (Option<String>, DatastoreNotify) {
+pub fn lookup_datastore_notify_settings(
+    store: &str,
+) -> (Option<String>, DatastoreNotify, NotificationMode) {
     let mut email = None;
 
     let notify = DatastoreNotify {
@@ -767,12 +741,12 @@ pub fn lookup_datastore_notify_settings(store: &str) -> (Option<String>, Datasto
 
     let (config, _digest) = match pbs_config::datastore::config() {
         Ok(result) => result,
-        Err(_) => return (email, notify),
+        Err(_) => return (email, notify, NotificationMode::default()),
     };
 
     let config: DataStoreConfig = match config.lookup("datastore", store) {
         Ok(result) => result,
-        Err(_) => return (email, notify),
+        Err(_) => return (email, notify, NotificationMode::default()),
     };
 
     email = match config.notify_user {
@@ -780,68 +754,20 @@ pub fn lookup_datastore_notify_settings(store: &str) -> (Option<String>, Datasto
         None => lookup_user_email(Userid::root_userid()),
     };
 
+    let notification_mode = config.notification_mode.unwrap_or_default();
     let notify_str = config.notify.unwrap_or_default();
 
     if let Ok(value) = DatastoreNotify::API_SCHEMA.parse_property_string(&notify_str) {
         if let Ok(notify) = serde_json::from_value(value) {
-            return (email, notify);
+            return (email, notify, notification_mode);
         }
     }
 
-    (email, notify)
-}
-
-// Handlerbar helper functions
-
-fn handlebars_humam_bytes_helper(
-    h: &Helper,
-    _: &Handlebars,
-    _: &Context,
-    _rc: &mut RenderContext,
-    out: &mut dyn Output,
-) -> HelperResult {
-    let param = h
-        .param(0)
-        .and_then(|v| v.value().as_u64())
-        .ok_or_else(|| RenderError::new("human-bytes: param not found"))?;
-
-    out.write(&HumanByte::from(param).to_string())?;
-
-    Ok(())
-}
-
-fn handlebars_relative_percentage_helper(
-    h: &Helper,
-    _: &Handlebars,
-    _: &Context,
-    _rc: &mut RenderContext,
-    out: &mut dyn Output,
-) -> HelperResult {
-    let param0 = h
-        .param(0)
-        .and_then(|v| v.value().as_f64())
-        .ok_or_else(|| RenderError::new("relative-percentage: param0 not found"))?;
-    let param1 = h
-        .param(1)
-        .and_then(|v| v.value().as_f64())
-        .ok_or_else(|| RenderError::new("relative-percentage: param1 not found"))?;
-
-    if param1 == 0.0 {
-        out.write("-")?;
-    } else {
-        out.write(&format!("{:.2}%", (param0 * 100.0) / param1))?;
-    }
-    Ok(())
+    (email, notify, notification_mode)
 }
 
 #[test]
 fn test_template_register() {
-    HANDLEBARS.get_helper("human-bytes").unwrap();
-    HANDLEBARS.get_helper("relative-percentage").unwrap();
-
-    assert!(HANDLEBARS.has_template("gc_ok_template"));
-    assert!(HANDLEBARS.has_template("gc_err_template"));
-
     assert!(HANDLEBARS.has_template("verify_ok_template"));
     assert!(HANDLEBARS.has_template("verify_err_template"));
 
diff --git a/src/server/verify_job.rs b/src/server/verify_job.rs
index 8bf2a0c9..fed80e5c 100644
--- a/src/server/verify_job.rs
+++ b/src/server/verify_job.rs
@@ -23,7 +23,8 @@ pub fn do_verification_job(
     let outdated_after = verification_job.outdated_after;
     let ignore_verified_snapshots = verification_job.ignore_verified.unwrap_or(true);
 
-    let (email, notify) = crate::server::lookup_datastore_notify_settings(&verification_job.store);
+    let (email, notify, _) =
+        crate::server::lookup_datastore_notify_settings(&verification_job.store);
 
     // FIXME encode namespace here for filter/ACL check?
     let job_id = format!("{}:{}", &verification_job.store, job.jobname());
diff --git a/templates/Makefile b/templates/Makefile
index b35a8bb3..7d4cb19f 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -1,6 +1,10 @@
 include ../defines.mk
 
 NOTIFICATION_TEMPLATES=					\
+	default/gc-err-body.txt.hbs			\
+	default/gc-ok-body.txt.hbs			\
+	default/gc-err-subject.txt.hbs		\
+	default/gc-ok-subject.txt.hbs		\
 	default/test-body.txt.hbs			\
 	default/test-body.html.hbs			\
 	default/test-subject.txt.hbs		\
diff --git a/templates/default/gc-err-body.txt.hbs b/templates/default/gc-err-body.txt.hbs
new file mode 100644
index 00000000..d6c2d0bc
--- /dev/null
+++ b/templates/default/gc-err-body.txt.hbs
@@ -0,0 +1,8 @@
+Datastore: {{datastore}}
+
+Garbage collection failed: {{error}}
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
diff --git a/templates/default/gc-err-subject.txt.hbs b/templates/default/gc-err-subject.txt.hbs
new file mode 100644
index 00000000..ebf49f3b
--- /dev/null
+++ b/templates/default/gc-err-subject.txt.hbs
@@ -0,0 +1 @@
+Garbage Collect Datastore '{{ datastore }}' failed
diff --git a/templates/default/gc-ok-body.txt.hbs b/templates/default/gc-ok-body.txt.hbs
new file mode 100644
index 00000000..d2f7cd81
--- /dev/null
+++ b/templates/default/gc-ok-body.txt.hbs
@@ -0,0 +1,23 @@
+Datastore:            {{datastore}}
+Task ID:              {{status.upid}}
+Index file count:     {{status.index-file-count}}
+
+Removed garbage:      {{human-bytes status.removed-bytes}}
+Removed chunks:       {{status.removed-chunks}}
+Removed bad chunks:   {{status.removed-bad}}
+
+Leftover bad chunks:  {{status.still-bad}}
+Pending removals:     {{human-bytes status.pending-bytes}} (in {{status.pending-chunks}} chunks)
+
+Original Data usage:  {{human-bytes status.index-data-bytes}}
+On-Disk usage:        {{human-bytes status.disk-bytes}} ({{relative-percentage status.disk-bytes status.index-data-bytes}})
+On-Disk chunks:       {{status.disk-chunks}}
+
+Deduplication Factor: {{deduplication-factor}}
+
+Garbage collection successful.
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#DataStore-{{datastore}}>
diff --git a/templates/default/gc-ok-subject.txt.hbs b/templates/default/gc-ok-subject.txt.hbs
new file mode 100644
index 00000000..538e3700
--- /dev/null
+++ b/templates/default/gc-ok-subject.txt.hbs
@@ -0,0 +1 @@
+Garbage Collect Datastore '{{ datastore }}' successful
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 15/33] server: notifications: send prune notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (13 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 16/33] server: notifications: send verify " Lukas Wagner
                   ` (20 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

If the `notification-mode` parameter is set to `legacy-sendmail`, then
we still use the new infrastructure, but don't consider the
notification config and use a hard-coded sendmail endpoint directly.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install        |  4 ++
 src/server/notifications.rs                 | 76 ++++++++-------------
 templates/Makefile                          |  4 ++
 templates/default/prune-err-body.txt.hbs    | 10 +++
 templates/default/prune-err-subject.txt.hbs |  1 +
 templates/default/prune-ok-body.txt.hbs     | 10 +++
 templates/default/prune-ok-subject.txt.hbs  |  1 +
 7 files changed, 57 insertions(+), 49 deletions(-)
 create mode 100644 templates/default/prune-err-body.txt.hbs
 create mode 100644 templates/default/prune-err-subject.txt.hbs
 create mode 100644 templates/default/prune-ok-body.txt.hbs
 create mode 100644 templates/default/prune-ok-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 197c070f..7b8183dd 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -45,6 +45,10 @@ usr/share/proxmox-backup/templates/default/gc-err-body.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-ok-body.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-err-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-ok-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/prune-err-body.txt.hbs
+usr/share/proxmox-backup/templates/default/prune-ok-body.txt.hbs
+usr/share/proxmox-backup/templates/default/prune-err-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/prune-ok-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.html.hbs
 usr/share/proxmox-backup/templates/default/test-subject.txt.hbs
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 720ae5ff..73de961e 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -87,34 +87,6 @@ Local Source Store: {{job.remote-store}}
 Synchronization failed: {{error}}
 
 
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
-
-"###;
-
-const PRUNE_OK_TEMPLATE: &str = r###"
-
-Job ID:       {{jobname}}
-Datastore:    {{store}}
-
-Pruning successful.
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#DataStore-{{store}}>
-
-"###;
-
-const PRUNE_ERR_TEMPLATE: &str = r###"
-
-Job ID:       {{jobname}}
-Datastore:    {{store}}
-
-Pruning failed: {{error}}
-
-
 Please visit the web interface for further details:
 
 <https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
@@ -223,9 +195,6 @@ lazy_static::lazy_static! {
             hb.register_template_string("sync_ok_template", SYNC_OK_TEMPLATE)?;
             hb.register_template_string("sync_err_template", SYNC_ERR_TEMPLATE)?;
 
-            hb.register_template_string("prune_ok_template", PRUNE_OK_TEMPLATE)?;
-            hb.register_template_string("prune_err_template", PRUNE_ERR_TEMPLATE)?;
-
             hb.register_template_string("tape_backup_ok_template", TAPE_BACKUP_OK_TEMPLATE)?;
             hb.register_template_string("tape_backup_err_template", TAPE_BACKUP_ERR_TEMPLATE)?;
 
@@ -490,16 +459,6 @@ pub fn send_prune_status(
     jobname: &str,
     result: &Result<(), Error>,
 ) -> Result<(), Error> {
-    let (email, notify) = match lookup_datastore_notify_settings(store) {
-        (Some(email), notify, _) => (email, notify),
-        (None, _, _) => return Ok(()),
-    };
-
-    let notify_prune = notify.prune.unwrap_or(Notify::Error);
-    if notify_prune == Notify::Never || (result.is_ok() && notify_prune == Notify::Error) {
-        return Ok(());
-    }
-
     let (fqdn, port) = get_server_url();
     let mut data = json!({
         "jobname": jobname,
@@ -508,20 +467,39 @@ pub fn send_prune_status(
         "port": port,
     });
 
-    let text = match result {
-        Ok(()) => HANDLEBARS.render("prune_ok_template", &data)?,
+    let (template, severity) = match result {
+        Ok(()) => ("prune-ok", Severity::Info),
         Err(err) => {
             data["error"] = err.to_string().into();
-            HANDLEBARS.render("prune_err_template", &data)?
+            ("prune-err", Severity::Error)
         }
     };
 
-    let subject = match result {
-        Ok(()) => format!("Pruning datastore '{store}' successful"),
-        Err(_) => format!("Pruning datastore '{store}' failed"),
-    };
+    let metadata = HashMap::from([
+        ("datastore".into(), store.into()),
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "prune".into()),
+    ]);
 
-    send_job_status_mail(&email, &subject, &text)?;
+    let notification = Notification::from_template(severity, template, data, metadata);
+
+    let (email, notify, mode) = lookup_datastore_notify_settings(store);
+    match mode {
+        NotificationMode::LegacySendmail => {
+            let notify = notify.prune.unwrap_or(Notify::Error);
+
+            if notify == Notify::Never || (result.is_ok() && notify == Notify::Error) {
+                return Ok(());
+            }
+
+            if let Some(email) = email {
+                send_sendmail_legacy_notification(notification, &email)?;
+            }
+        }
+        NotificationMode::NotificationSystem => {
+            send_notification(notification)?;
+        }
+    }
 
     Ok(())
 }
diff --git a/templates/Makefile b/templates/Makefile
index 7d4cb19f..1f556aba 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -5,6 +5,10 @@ NOTIFICATION_TEMPLATES=					\
 	default/gc-ok-body.txt.hbs			\
 	default/gc-err-subject.txt.hbs		\
 	default/gc-ok-subject.txt.hbs		\
+	default/prune-err-body.txt.hbs			\
+	default/prune-ok-body.txt.hbs			\
+	default/prune-err-subject.txt.hbs		\
+	default/prune-ok-subject.txt.hbs		\
 	default/test-body.txt.hbs			\
 	default/test-body.html.hbs			\
 	default/test-subject.txt.hbs		\
diff --git a/templates/default/prune-err-body.txt.hbs b/templates/default/prune-err-body.txt.hbs
new file mode 100644
index 00000000..0973c3d9
--- /dev/null
+++ b/templates/default/prune-err-body.txt.hbs
@@ -0,0 +1,10 @@
+
+Job ID:       {{jobname}}
+Datastore:    {{store}}
+
+Pruning failed: {{error}}
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
diff --git a/templates/default/prune-err-subject.txt.hbs b/templates/default/prune-err-subject.txt.hbs
new file mode 100644
index 00000000..836ae722
--- /dev/null
+++ b/templates/default/prune-err-subject.txt.hbs
@@ -0,0 +1 @@
+Pruning datastore '{{ store }}' failed
diff --git a/templates/default/prune-ok-body.txt.hbs b/templates/default/prune-ok-body.txt.hbs
new file mode 100644
index 00000000..b7e449e3
--- /dev/null
+++ b/templates/default/prune-ok-body.txt.hbs
@@ -0,0 +1,10 @@
+
+Job ID:       {{jobname}}
+Datastore:    {{store}}
+
+Pruning successful.
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#DataStore-{{store}}>
diff --git a/templates/default/prune-ok-subject.txt.hbs b/templates/default/prune-ok-subject.txt.hbs
new file mode 100644
index 00000000..3227a062
--- /dev/null
+++ b/templates/default/prune-ok-subject.txt.hbs
@@ -0,0 +1 @@
+Pruning datastore '{{ store }}' successful
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 16/33] server: notifications: send verify notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (14 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 15/33] server: notifications: send prune " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 17/33] server: notifications: send sync " Lukas Wagner
                   ` (19 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

If the `notification-mode` parameter is set to `legacy-sendmail`, then
we still use the new infrastructure, but don't consider the
notification config and use a hard-coded sendmail endpoint directly.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install         |  4 +
 src/server/notifications.rs                  | 85 ++++++--------------
 src/server/verify_job.rs                     | 11 +--
 templates/Makefile                           |  4 +
 templates/default/verify-err-body.txt.hbs    | 14 ++++
 templates/default/verify-err-subject.txt.hbs |  1 +
 templates/default/verify-ok-body.txt.hbs     | 10 +++
 templates/default/verify-ok-subject.txt.hbs  |  1 +
 8 files changed, 61 insertions(+), 69 deletions(-)
 create mode 100644 templates/default/verify-err-body.txt.hbs
 create mode 100644 templates/default/verify-err-subject.txt.hbs
 create mode 100644 templates/default/verify-ok-body.txt.hbs
 create mode 100644 templates/default/verify-ok-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 7b8183dd..dfe671a6 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -52,3 +52,7 @@ usr/share/proxmox-backup/templates/default/prune-ok-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.html.hbs
 usr/share/proxmox-backup/templates/default/test-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/verify-err-body.txt.hbs
+usr/share/proxmox-backup/templates/default/verify-ok-body.txt.hbs
+usr/share/proxmox-backup/templates/default/verify-err-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/verify-ok-subject.txt.hbs
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 73de961e..83b9d7ca 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -23,38 +23,6 @@ use proxmox_notify::{Endpoint, Notification, Severity};
 
 const SPOOL_DIR: &str = concatcp!(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR, "/notifications");
 
-const VERIFY_OK_TEMPLATE: &str = r###"
-
-Job ID:    {{job.id}}
-Datastore: {{job.store}}
-
-Verification successful.
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#DataStore-{{job.store}}>
-
-"###;
-
-const VERIFY_ERR_TEMPLATE: &str = r###"
-
-Job ID:    {{job.id}}
-Datastore: {{job.store}}
-
-Verification failed on these snapshots/groups:
-
-{{#each errors}}
-  {{this~}}
-{{/each}}
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
-
-"###;
-
 const SYNC_OK_TEMPLATE: &str = r###"
 
 Job ID:             {{job.id}}
@@ -189,9 +157,6 @@ lazy_static::lazy_static! {
             hb.set_strict_mode(true);
             hb.register_escape_fn(handlebars::no_escape);
 
-            hb.register_template_string("verify_ok_template", VERIFY_OK_TEMPLATE)?;
-            hb.register_template_string("verify_err_template", VERIFY_ERR_TEMPLATE)?;
-
             hb.register_template_string("sync_ok_template", SYNC_OK_TEMPLATE)?;
             hb.register_template_string("sync_err_template", SYNC_ERR_TEMPLATE)?;
 
@@ -406,8 +371,6 @@ pub fn send_gc_status(
 }
 
 pub fn send_verify_status(
-    email: &str,
-    notify: DatastoreNotify,
     job: VerificationJobConfig,
     result: &Result<Vec<String>, Error>,
 ) -> Result<(), Error> {
@@ -418,39 +381,44 @@ pub fn send_verify_status(
         "port": port,
     });
 
-    let mut result_is_ok = false;
-
-    let text = match result {
-        Ok(errors) if errors.is_empty() => {
-            result_is_ok = true;
-            HANDLEBARS.render("verify_ok_template", &data)?
-        }
+    let (template, severity) = match result {
+        Ok(errors) if errors.is_empty() => ("verify-ok", Severity::Info),
         Ok(errors) => {
             data["errors"] = json!(errors);
-            HANDLEBARS.render("verify_err_template", &data)?
+            ("verify-err", Severity::Error)
         }
         Err(_) => {
-            // aborted job - do not send any email
+            // aborted job - do not send any notification
             return Ok(());
         }
     };
 
-    match notify.verify {
-        None => { /* send notifications by default */ }
-        Some(notify) => {
-            if notify == Notify::Never || (result_is_ok && notify == Notify::Error) {
+    let metadata = HashMap::from([
+        ("datastore".into(), job.store.clone()),
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "verify".into()),
+    ]);
+
+    let notification = Notification::from_template(severity, template, data, metadata);
+
+    let (email, notify, mode) = lookup_datastore_notify_settings(&job.store);
+    match mode {
+        NotificationMode::LegacySendmail => {
+            let notify = notify.verify.unwrap_or(Notify::Always);
+
+            if notify == Notify::Never || (result.is_ok() && notify == Notify::Error) {
                 return Ok(());
             }
+
+            if let Some(email) = email {
+                send_sendmail_legacy_notification(notification, &email)?;
+            }
+        }
+        NotificationMode::NotificationSystem => {
+            send_notification(notification)?;
         }
     }
 
-    let subject = match result {
-        Ok(errors) if errors.is_empty() => format!("Verify Datastore '{}' successful", job.store),
-        _ => format!("Verify Datastore '{}' failed", job.store),
-    };
-
-    send_job_status_mail(email, &subject, &text)?;
-
     Ok(())
 }
 
@@ -746,9 +714,6 @@ pub fn lookup_datastore_notify_settings(
 
 #[test]
 fn test_template_register() {
-    assert!(HANDLEBARS.has_template("verify_ok_template"));
-    assert!(HANDLEBARS.has_template("verify_err_template"));
-
     assert!(HANDLEBARS.has_template("sync_ok_template"));
     assert!(HANDLEBARS.has_template("sync_err_template"));
 
diff --git a/src/server/verify_job.rs b/src/server/verify_job.rs
index fed80e5c..f3cf2277 100644
--- a/src/server/verify_job.rs
+++ b/src/server/verify_job.rs
@@ -23,9 +23,6 @@ pub fn do_verification_job(
     let outdated_after = verification_job.outdated_after;
     let ignore_verified_snapshots = verification_job.ignore_verified.unwrap_or(true);
 
-    let (email, notify, _) =
-        crate::server::lookup_datastore_notify_settings(&verification_job.store);
-
     // FIXME encode namespace here for filter/ACL check?
     let job_id = format!("{}:{}", &verification_job.store, job.jobname());
     let worker_type = job.jobtype().to_string();
@@ -79,12 +76,8 @@ pub fn do_verification_job(
                 eprintln!("could not finish job state for {}: {}", job.jobtype(), err);
             }
 
-            if let Some(email) = email {
-                if let Err(err) =
-                    crate::server::send_verify_status(&email, notify, verification_job, &result)
-                {
-                    eprintln!("send verify notification failed: {}", err);
-                }
+            if let Err(err) = crate::server::send_verify_status(verification_job, &result) {
+                eprintln!("send verify notification failed: {err}");
             }
 
             job_result
diff --git a/templates/Makefile b/templates/Makefile
index 1f556aba..f1adda9f 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -12,6 +12,10 @@ NOTIFICATION_TEMPLATES=					\
 	default/test-body.txt.hbs			\
 	default/test-body.html.hbs			\
 	default/test-subject.txt.hbs		\
+	default/verify-err-body.txt.hbs			\
+	default/verify-ok-body.txt.hbs			\
+	default/verify-err-subject.txt.hbs		\
+	default/verify-ok-subject.txt.hbs		\
 
 all:
 
diff --git a/templates/default/verify-err-body.txt.hbs b/templates/default/verify-err-body.txt.hbs
new file mode 100644
index 00000000..d07b5ce0
--- /dev/null
+++ b/templates/default/verify-err-body.txt.hbs
@@ -0,0 +1,14 @@
+
+Job ID:    {{job.id}}
+Datastore: {{job.store}}
+
+Verification failed on these snapshots/groups:
+
+{{#each errors}}
+    {{this~}}
+{{/each}}
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
diff --git a/templates/default/verify-err-subject.txt.hbs b/templates/default/verify-err-subject.txt.hbs
new file mode 100644
index 00000000..00a2d07f
--- /dev/null
+++ b/templates/default/verify-err-subject.txt.hbs
@@ -0,0 +1 @@
+Verify Datastore '{{ job.store }}' failed
diff --git a/templates/default/verify-ok-body.txt.hbs b/templates/default/verify-ok-body.txt.hbs
new file mode 100644
index 00000000..7560582e
--- /dev/null
+++ b/templates/default/verify-ok-body.txt.hbs
@@ -0,0 +1,10 @@
+
+Job ID:    {{job.id}}
+Datastore: {{job.store}}
+
+Verification successful.
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#DataStore-{{job.store}}>
diff --git a/templates/default/verify-ok-subject.txt.hbs b/templates/default/verify-ok-subject.txt.hbs
new file mode 100644
index 00000000..6020874c
--- /dev/null
+++ b/templates/default/verify-ok-subject.txt.hbs
@@ -0,0 +1 @@
+Verify Datastore '{{ job.store }}' successful
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 17/33] server: notifications: send sync notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (15 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 16/33] server: notifications: send verify " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 18/33] server: notifications: send update " Lukas Wagner
                   ` (18 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

If the `notification-mode` parameter is set to `legacy-sendmail`, then
we still use the new infrastructure, but don't consider the
notification config and use a hard-coded sendmail endpoint directly.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install       |   4 +
 src/api2/pull.rs                           |  10 +-
 src/server/notifications.rs                | 101 ++++++---------------
 templates/Makefile                         |   4 +
 templates/default/sync-err-body.txt.hbs    |  14 +++
 templates/default/sync-err-subject.txt.hbs |   5 +
 templates/default/sync-ok-body.txt.hbs     |  14 +++
 templates/default/sync-ok-subject.txt.hbs  |   5 +
 8 files changed, 75 insertions(+), 82 deletions(-)
 create mode 100644 templates/default/sync-err-body.txt.hbs
 create mode 100644 templates/default/sync-err-subject.txt.hbs
 create mode 100644 templates/default/sync-ok-body.txt.hbs
 create mode 100644 templates/default/sync-ok-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index dfe671a6..86e2b543 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -49,6 +49,10 @@ usr/share/proxmox-backup/templates/default/prune-err-body.txt.hbs
 usr/share/proxmox-backup/templates/default/prune-ok-body.txt.hbs
 usr/share/proxmox-backup/templates/default/prune-err-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/prune-ok-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/sync-err-body.txt.hbs
+usr/share/proxmox-backup/templates/default/sync-ok-body.txt.hbs
+usr/share/proxmox-backup/templates/default/sync-err-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/sync-ok-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.html.hbs
 usr/share/proxmox-backup/templates/default/test-subject.txt.hbs
diff --git a/src/api2/pull.rs b/src/api2/pull.rs
index 7fe2267a..5d02510c 100644
--- a/src/api2/pull.rs
+++ b/src/api2/pull.rs
@@ -114,8 +114,6 @@ pub fn do_sync_job(
         bail!("can't sync to same datastore");
     }
 
-    let (email, notify, _) = crate::server::lookup_datastore_notify_settings(&sync_job.store);
-
     let upid_str = WorkerTask::spawn(
         &worker_type,
         Some(job_id.clone()),
@@ -194,12 +192,8 @@ pub fn do_sync_job(
                 }
             }
 
-            if let Some(email) = email {
-                if let Err(err) =
-                    crate::server::send_sync_status(&email, notify, &sync_job2, &result)
-                {
-                    eprintln!("send sync notification failed: {}", err);
-                }
+            if let Err(err) = crate::server::send_sync_status(&sync_job2, &result) {
+                eprintln!("send sync notification failed: {err}");
             }
 
             result
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 83b9d7ca..eff13640 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -23,44 +23,6 @@ use proxmox_notify::{Endpoint, Notification, Severity};
 
 const SPOOL_DIR: &str = concatcp!(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR, "/notifications");
 
-const SYNC_OK_TEMPLATE: &str = r###"
-
-Job ID:             {{job.id}}
-Datastore:          {{job.store}}
-{{#if job.remote~}}
-Remote:             {{job.remote}}
-Remote Store:       {{job.remote-store}}
-{{else~}}
-Local Source Store: {{job.remote-store}}
-{{/if}}
-Synchronization successful.
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#DataStore-{{job.store}}>
-
-"###;
-
-const SYNC_ERR_TEMPLATE: &str = r###"
-
-Job ID:             {{job.id}}
-Datastore:          {{job.store}}
-{{#if job.remote~}}
-Remote:             {{job.remote}}
-Remote Store:       {{job.remote-store}}
-{{else~}}
-Local Source Store: {{job.remote-store}}
-{{/if}}
-Synchronization failed: {{error}}
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
-
-"###;
-
 const PACKAGE_UPDATES_TEMPLATE: &str = r###"
 Proxmox Backup Server has the following updates available:
 {{#each updates }}
@@ -157,9 +119,6 @@ lazy_static::lazy_static! {
             hb.set_strict_mode(true);
             hb.register_escape_fn(handlebars::no_escape);
 
-            hb.register_template_string("sync_ok_template", SYNC_OK_TEMPLATE)?;
-            hb.register_template_string("sync_err_template", SYNC_ERR_TEMPLATE)?;
-
             hb.register_template_string("tape_backup_ok_template", TAPE_BACKUP_OK_TEMPLATE)?;
             hb.register_template_string("tape_backup_err_template", TAPE_BACKUP_ERR_TEMPLATE)?;
 
@@ -472,21 +431,7 @@ pub fn send_prune_status(
     Ok(())
 }
 
-pub fn send_sync_status(
-    email: &str,
-    notify: DatastoreNotify,
-    job: &SyncJobConfig,
-    result: &Result<(), Error>,
-) -> Result<(), Error> {
-    match notify.sync {
-        None => { /* send notifications by default */ }
-        Some(notify) => {
-            if notify == Notify::Never || (result.is_ok() && notify == Notify::Error) {
-                return Ok(());
-            }
-        }
-    }
-
+pub fn send_sync_status(job: &SyncJobConfig, result: &Result<(), Error>) -> Result<(), Error> {
     let (fqdn, port) = get_server_url();
     let mut data = json!({
         "job": job,
@@ -494,28 +439,39 @@ pub fn send_sync_status(
         "port": port,
     });
 
-    let text = match result {
-        Ok(()) => HANDLEBARS.render("sync_ok_template", &data)?,
+    let (template, severity) = match result {
+        Ok(()) => ("sync-ok", Severity::Info),
         Err(err) => {
             data["error"] = err.to_string().into();
-            HANDLEBARS.render("sync_err_template", &data)?
+            ("sync-err", Severity::Error)
         }
     };
 
-    let tmp_src_string;
-    let source_str = if let Some(remote) = &job.remote {
-        tmp_src_string = format!("Sync remote '{}'", remote);
-        &tmp_src_string
-    } else {
-        "Sync local"
-    };
+    let metadata = HashMap::from([
+        ("datastore".into(), job.store.clone()),
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "sync".into()),
+    ]);
 
-    let subject = match result {
-        Ok(()) => format!("{} datastore '{}' successful", source_str, job.remote_store,),
-        Err(_) => format!("{} datastore '{}' failed", source_str, job.remote_store,),
-    };
+    let notification = Notification::from_template(severity, template, data, metadata);
 
-    send_job_status_mail(email, &subject, &text)?;
+    let (email, notify, mode) = lookup_datastore_notify_settings(&job.store);
+    match mode {
+        NotificationMode::LegacySendmail => {
+            let notify = notify.prune.unwrap_or(Notify::Error);
+
+            if notify == Notify::Never || (result.is_ok() && notify == Notify::Error) {
+                return Ok(());
+            }
+
+            if let Some(email) = email {
+                send_sendmail_legacy_notification(notification, &email)?;
+            }
+        }
+        NotificationMode::NotificationSystem => {
+            send_notification(notification)?;
+        }
+    }
 
     Ok(())
 }
@@ -714,9 +670,6 @@ pub fn lookup_datastore_notify_settings(
 
 #[test]
 fn test_template_register() {
-    assert!(HANDLEBARS.has_template("sync_ok_template"));
-    assert!(HANDLEBARS.has_template("sync_err_template"));
-
     assert!(HANDLEBARS.has_template("tape_backup_ok_template"));
     assert!(HANDLEBARS.has_template("tape_backup_err_template"));
 
diff --git a/templates/Makefile b/templates/Makefile
index f1adda9f..41cdd2da 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -9,6 +9,10 @@ NOTIFICATION_TEMPLATES=					\
 	default/prune-ok-body.txt.hbs			\
 	default/prune-err-subject.txt.hbs		\
 	default/prune-ok-subject.txt.hbs		\
+	default/sync-err-body.txt.hbs			\
+	default/sync-ok-body.txt.hbs			\
+	default/sync-err-subject.txt.hbs		\
+	default/sync-ok-subject.txt.hbs		\
 	default/test-body.txt.hbs			\
 	default/test-body.html.hbs			\
 	default/test-subject.txt.hbs		\
diff --git a/templates/default/sync-err-body.txt.hbs b/templates/default/sync-err-body.txt.hbs
new file mode 100644
index 00000000..a56d9d22
--- /dev/null
+++ b/templates/default/sync-err-body.txt.hbs
@@ -0,0 +1,14 @@
+Job ID:             {{job.id}}
+Datastore:          {{job.store}}
+{{#if job.remote~}}
+Remote:             {{job.remote}}
+Remote Store:       {{job.remote-store}}
+{{else~}}
+Local Source Store: {{job.remote-store}}
+{{/if}}
+Synchronization failed: {{error}}
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
diff --git a/templates/default/sync-err-subject.txt.hbs b/templates/default/sync-err-subject.txt.hbs
new file mode 100644
index 00000000..a1464802
--- /dev/null
+++ b/templates/default/sync-err-subject.txt.hbs
@@ -0,0 +1,5 @@
+{{#if job.remote~}}
+Sync remote '{{ job.remote }}' datastore '{{ job.remote-store }}' failed
+{{else~}}
+Sync local datastore '{{ job.remote-store }}' failed
+{{/if}}
diff --git a/templates/default/sync-ok-body.txt.hbs b/templates/default/sync-ok-body.txt.hbs
new file mode 100644
index 00000000..25c4b33b
--- /dev/null
+++ b/templates/default/sync-ok-body.txt.hbs
@@ -0,0 +1,14 @@
+Job ID:             {{job.id}}
+Datastore:          {{job.store}}
+{{#if job.remote~}}
+Remote:             {{job.remote}}
+Remote Store:       {{job.remote-store}}
+{{else~}}
+Local Source Store: {{job.remote-store}}
+{{/if}}
+Synchronization successful.
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#DataStore-{{job.store}}>
diff --git a/templates/default/sync-ok-subject.txt.hbs b/templates/default/sync-ok-subject.txt.hbs
new file mode 100644
index 00000000..76616b5c
--- /dev/null
+++ b/templates/default/sync-ok-subject.txt.hbs
@@ -0,0 +1,5 @@
+{{#if job.remote~}}
+Sync remote '{{ job.remote }}' datastore '{{ job.remote-store }}' successful
+{{else~}}
+Sync local datastore '{{ job.remote-store }}' successful
+{{/if}}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 18/33] server: notifications: send update notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (16 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 17/33] server: notifications: send sync " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 19/33] server: notifications: send acme " Lukas Wagner
                   ` (17 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install          |  2 +
 src/server/notifications.rs                   | 44 ++++++-------------
 templates/Makefile                            |  2 +
 .../default/package-updates-body.txt.hbs      |  8 ++++
 .../default/package-updates-subject.txt.hbs   |  1 +
 5 files changed, 26 insertions(+), 31 deletions(-)
 create mode 100644 templates/default/package-updates-body.txt.hbs
 create mode 100644 templates/default/package-updates-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 86e2b543..52a01ca5 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -45,6 +45,8 @@ usr/share/proxmox-backup/templates/default/gc-err-body.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-ok-body.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-err-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-ok-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/package-updates-body.txt.hbs
+usr/share/proxmox-backup/templates/default/package-updates-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/prune-err-body.txt.hbs
 usr/share/proxmox-backup/templates/default/prune-ok-body.txt.hbs
 usr/share/proxmox-backup/templates/default/prune-err-subject.txt.hbs
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index eff13640..0e2f3473 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -23,18 +23,6 @@ use proxmox_notify::{Endpoint, Notification, Severity};
 
 const SPOOL_DIR: &str = concatcp!(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR, "/notifications");
 
-const PACKAGE_UPDATES_TEMPLATE: &str = r###"
-Proxmox Backup Server has the following updates available:
-{{#each updates }}
-  {{Package}}: {{OldVersion}} -> {{Version~}}
-{{/each }}
-
-To upgrade visit the web interface:
-
-<https://{{fqdn}}:{{port}}/#pbsServerAdministration:updates>
-
-"###;
-
 const TAPE_BACKUP_OK_TEMPLATE: &str = r###"
 
 {{#if id ~}}
@@ -122,8 +110,6 @@ lazy_static::lazy_static! {
             hb.register_template_string("tape_backup_ok_template", TAPE_BACKUP_OK_TEMPLATE)?;
             hb.register_template_string("tape_backup_err_template", TAPE_BACKUP_ERR_TEMPLATE)?;
 
-            hb.register_template_string("package_update_template", PACKAGE_UPDATES_TEMPLATE)?;
-
             hb.register_template_string("certificate_renewal_err_template", ACME_CERTIFICATE_ERR_RENEWAL)?;
 
             Ok(())
@@ -569,24 +555,22 @@ fn get_server_url() -> (String, usize) {
 }
 
 pub fn send_updates_available(updates: &[&APTUpdateInfo]) -> Result<(), Error> {
-    // update mails always go to the root@pam configured email..
-    if let Some(email) = lookup_user_email(Userid::root_userid()) {
-        let nodename = proxmox_sys::nodename();
-        let subject = format!("New software packages available ({nodename})");
+    let (fqdn, port) = get_server_url();
+    let data = json!({
+        "fqdn": fqdn,
+        "port": port,
+        "updates": updates,
+    });
 
-        let (fqdn, port) = get_server_url();
+    let metadata = HashMap::from([
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "package-updates".into()),
+    ]);
 
-        let text = HANDLEBARS.render(
-            "package_update_template",
-            &json!({
-                "fqdn": fqdn,
-                "port": port,
-                "updates": updates,
-            }),
-        )?;
+    let notification =
+        Notification::from_template(Severity::Info, "package-updates", data, metadata);
 
-        send_job_status_mail(&email, &subject, &text)?;
-    }
+    send_notification(notification)?;
     Ok(())
 }
 
@@ -673,7 +657,5 @@ fn test_template_register() {
     assert!(HANDLEBARS.has_template("tape_backup_ok_template"));
     assert!(HANDLEBARS.has_template("tape_backup_err_template"));
 
-    assert!(HANDLEBARS.has_template("package_update_template"));
-
     assert!(HANDLEBARS.has_template("certificate_renewal_err_template"));
 }
diff --git a/templates/Makefile b/templates/Makefile
index 41cdd2da..56965400 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -5,6 +5,8 @@ NOTIFICATION_TEMPLATES=					\
 	default/gc-ok-body.txt.hbs			\
 	default/gc-err-subject.txt.hbs		\
 	default/gc-ok-subject.txt.hbs		\
+	default/package-updates-body.txt.hbs			\
+	default/package-updates-subject.txt.hbs		\
 	default/prune-err-body.txt.hbs			\
 	default/prune-ok-body.txt.hbs			\
 	default/prune-err-subject.txt.hbs		\
diff --git a/templates/default/package-updates-body.txt.hbs b/templates/default/package-updates-body.txt.hbs
new file mode 100644
index 00000000..62f9c7c4
--- /dev/null
+++ b/templates/default/package-updates-body.txt.hbs
@@ -0,0 +1,8 @@
+Proxmox Backup Server has the following updates available:
+{{#each updates }}
+    {{Package}}: {{OldVersion}} -> {{Version~}}
+{{/each }}
+
+To upgrade visit the web interface:
+
+<https://{{fqdn}}:{{port}}/#pbsServerAdministration:updates>
diff --git a/templates/default/package-updates-subject.txt.hbs b/templates/default/package-updates-subject.txt.hbs
new file mode 100644
index 00000000..c8a775d5
--- /dev/null
+++ b/templates/default/package-updates-subject.txt.hbs
@@ -0,0 +1 @@
+New software packages available ({{ hostname }})
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 19/33] server: notifications: send acme notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (17 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 18/33] server: notifications: send update " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 20/33] server: notifications: send tape " Lukas Wagner
                   ` (16 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install       |  2 ++
 src/server/notifications.rs                | 41 +++++++---------------
 templates/Makefile                         | 24 +++++++------
 templates/default/acme-err-body.txt.hbs    |  7 ++++
 templates/default/acme-err-subject.txt.hbs |  1 +
 5 files changed, 35 insertions(+), 40 deletions(-)
 create mode 100644 templates/default/acme-err-body.txt.hbs
 create mode 100644 templates/default/acme-err-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 52a01ca5..17951780 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -41,6 +41,8 @@ usr/share/zsh/vendor-completions/_pmtx
 usr/share/zsh/vendor-completions/_proxmox-backup-debug
 usr/share/zsh/vendor-completions/_proxmox-backup-manager
 usr/share/zsh/vendor-completions/_proxmox-tape
+usr/share/proxmox-backup/templates/default/acme-err-body.txt.hbs
+usr/share/proxmox-backup/templates/default/acme-err-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-err-body.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-ok-body.txt.hbs
 usr/share/proxmox-backup/templates/default/gc-err-subject.txt.hbs
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 0e2f3473..16b506f0 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -86,18 +86,6 @@ Please visit the web interface for further details:
 
 "###;
 
-const ACME_CERTIFICATE_ERR_RENEWAL: &str = r###"
-
-Proxmox Backup Server was not able to renew a TLS certificate.
-
-Error: {{error}}
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#pbsCertificateConfiguration>
-
-"###;
-
 lazy_static::lazy_static! {
 
     static ref HANDLEBARS: Handlebars<'static> = {
@@ -110,8 +98,6 @@ lazy_static::lazy_static! {
             hb.register_template_string("tape_backup_ok_template", TAPE_BACKUP_OK_TEMPLATE)?;
             hb.register_template_string("tape_backup_err_template", TAPE_BACKUP_ERR_TEMPLATE)?;
 
-            hb.register_template_string("certificate_renewal_err_template", ACME_CERTIFICATE_ERR_RENEWAL)?;
-
             Ok(())
         });
 
@@ -581,23 +567,22 @@ pub fn send_certificate_renewal_mail(result: &Result<(), Error>) -> Result<(), E
         _ => return Ok(()),
     };
 
-    if let Some(email) = lookup_user_email(Userid::root_userid()) {
-        let (fqdn, port) = get_server_url();
+    let (fqdn, port) = get_server_url();
 
-        let text = HANDLEBARS.render(
-            "certificate_renewal_err_template",
-            &json!({
-                "fqdn": fqdn,
-                "port": port,
-                "error": error,
-            }),
-        )?;
+    let data = json!({
+        "fqdn": fqdn,
+        "port": port,
+        "error": error,
+    });
 
-        let subject = "Could not renew certificate";
+    let metadata = HashMap::from([
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "acme".into()),
+    ]);
 
-        send_job_status_mail(&email, subject, &text)?;
-    }
+    let notification = Notification::from_template(Severity::Info, "acme-err", data, metadata);
 
+    send_notification(notification)?;
     Ok(())
 }
 
@@ -656,6 +641,4 @@ pub fn lookup_datastore_notify_settings(
 fn test_template_register() {
     assert!(HANDLEBARS.has_template("tape_backup_ok_template"));
     assert!(HANDLEBARS.has_template("tape_backup_err_template"));
-
-    assert!(HANDLEBARS.has_template("certificate_renewal_err_template"));
 }
diff --git a/templates/Makefile b/templates/Makefile
index 56965400..824d28d9 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -1,12 +1,14 @@
 include ../defines.mk
 
-NOTIFICATION_TEMPLATES=					\
-	default/gc-err-body.txt.hbs			\
-	default/gc-ok-body.txt.hbs			\
-	default/gc-err-subject.txt.hbs		\
-	default/gc-ok-subject.txt.hbs		\
-	default/package-updates-body.txt.hbs			\
-	default/package-updates-subject.txt.hbs		\
+NOTIFICATION_TEMPLATES=						\
+	default/acme-err-body.txt.hbs			\
+	default/acme-err-subject.txt.hbs		\
+	default/gc-err-body.txt.hbs				\
+	default/gc-ok-body.txt.hbs				\
+	default/gc-err-subject.txt.hbs			\
+	default/gc-ok-subject.txt.hbs			\
+	default/package-updates-body.txt.hbs	\
+	default/package-updates-subject.txt.hbs	\
 	default/prune-err-body.txt.hbs			\
 	default/prune-ok-body.txt.hbs			\
 	default/prune-err-subject.txt.hbs		\
@@ -14,10 +16,10 @@ NOTIFICATION_TEMPLATES=					\
 	default/sync-err-body.txt.hbs			\
 	default/sync-ok-body.txt.hbs			\
 	default/sync-err-subject.txt.hbs		\
-	default/sync-ok-subject.txt.hbs		\
-	default/test-body.txt.hbs			\
-	default/test-body.html.hbs			\
-	default/test-subject.txt.hbs		\
+	default/sync-ok-subject.txt.hbs			\
+	default/test-body.txt.hbs				\
+	default/test-body.html.hbs				\
+	default/test-subject.txt.hbs			\
 	default/verify-err-body.txt.hbs			\
 	default/verify-ok-body.txt.hbs			\
 	default/verify-err-subject.txt.hbs		\
diff --git a/templates/default/acme-err-body.txt.hbs b/templates/default/acme-err-body.txt.hbs
new file mode 100644
index 00000000..3cbfea4a
--- /dev/null
+++ b/templates/default/acme-err-body.txt.hbs
@@ -0,0 +1,7 @@
+Proxmox Backup Server was not able to renew a TLS certificate.
+
+Error: {{error}}
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#pbsCertificateConfiguration>
diff --git a/templates/default/acme-err-subject.txt.hbs b/templates/default/acme-err-subject.txt.hbs
new file mode 100644
index 00000000..3cf4fe45
--- /dev/null
+++ b/templates/default/acme-err-subject.txt.hbs
@@ -0,0 +1 @@
+Could not renew certificate
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 20/33] server: notifications: send tape notifications via notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (18 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 19/33] server: notifications: send acme " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 21/33] ui: add notification config panel Lukas Wagner
                   ` (15 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

If the `notification-mode` parameter is set to `legacy-sendmail`, then
we still use the new infrastructure, but don't consider the
notification config and use a hard-coded sendmail endpoint directly.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install          |   6 +
 src/api2/tape/backup.rs                       |  62 ++----
 src/api2/tape/restore.rs                      |  46 ++--
 src/server/notifications.rs                   | 208 +++++-------------
 src/tape/drive/mod.rs                         |  22 +-
 src/tape/mod.rs                               |  27 +++
 src/tape/pool_writer/mod.rs                   |  11 +-
 templates/Makefile                            |   6 +
 .../default/tape-backup-err-body.txt.hbs      |  26 +++
 .../default/tape-backup-err-subject.txt.hbs   |   5 +
 templates/default/tape-backup-ok-body.txt.hbs |  27 +++
 .../default/tape-backup-ok-subject.txt.hbs    |   5 +
 templates/default/tape-load-body.txt.hbs      |  15 ++
 templates/default/tape-load-subject.txt.hbs   |   1 +
 14 files changed, 239 insertions(+), 228 deletions(-)
 create mode 100644 templates/default/tape-backup-err-body.txt.hbs
 create mode 100644 templates/default/tape-backup-err-subject.txt.hbs
 create mode 100644 templates/default/tape-backup-ok-body.txt.hbs
 create mode 100644 templates/default/tape-backup-ok-subject.txt.hbs
 create mode 100644 templates/default/tape-load-body.txt.hbs
 create mode 100644 templates/default/tape-load-subject.txt.hbs

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 17951780..df7d68ee 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -57,6 +57,12 @@ usr/share/proxmox-backup/templates/default/sync-err-body.txt.hbs
 usr/share/proxmox-backup/templates/default/sync-ok-body.txt.hbs
 usr/share/proxmox-backup/templates/default/sync-err-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/sync-ok-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/tape-backup-err-body.txt.hbs
+usr/share/proxmox-backup/templates/default/tape-backup-err-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/tape-backup-ok-body.txt.hbs
+usr/share/proxmox-backup/templates/default/tape-backup-ok-subject.txt.hbs
+usr/share/proxmox-backup/templates/default/tape-load-body.txt.hbs
+usr/share/proxmox-backup/templates/default/tape-load-subject.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.txt.hbs
 usr/share/proxmox-backup/templates/default/test-body.html.hbs
 usr/share/proxmox-backup/templates/default/test-subject.txt.hbs
diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs
index 28d7e720..896e809b 100644
--- a/src/api2/tape/backup.rs
+++ b/src/api2/tape/backup.rs
@@ -10,7 +10,7 @@ use proxmox_sys::{task_log, task_warn, WorkerTaskContext};
 
 use pbs_api_types::{
     print_ns_and_snapshot, print_store_and_ns, Authid, MediaPoolConfig, Operation,
-    TapeBackupJobConfig, TapeBackupJobSetup, TapeBackupJobStatus, Userid, JOB_ID_SCHEMA,
+    TapeBackupJobConfig, TapeBackupJobSetup, TapeBackupJobStatus, JOB_ID_SCHEMA,
     PRIV_DATASTORE_READ, PRIV_TAPE_AUDIT, PRIV_TAPE_WRITE, UPID_SCHEMA,
 };
 
@@ -19,10 +19,11 @@ use pbs_datastore::backup_info::{BackupDir, BackupInfo};
 use pbs_datastore::{DataStore, StoreProgress};
 use proxmox_rest_server::WorkerTask;
 
+use crate::tape::TapeNotificationMode;
 use crate::{
     server::{
         jobstate::{compute_schedule_status, Job, JobState},
-        lookup_user_email, TapeBackupJobSummary,
+        TapeBackupJobSummary,
     },
     tape::{
         changer::update_changer_online_status,
@@ -162,12 +163,6 @@ pub fn do_tape_backup_job(
         Some(lock_tape_device(&drive_config, &setup.drive)?)
     };
 
-    let notify_user = setup
-        .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_id.clone()),
@@ -206,7 +201,6 @@ pub fn do_tape_backup_job(
                     datastore,
                     &pool_config,
                     &setup,
-                    email.clone(),
                     &mut summary,
                     false,
                 )
@@ -214,16 +208,13 @@ pub fn do_tape_backup_job(
 
             let status = worker.create_state(&job_result);
 
-            if let Some(email) = email {
-                if let Err(err) = crate::server::send_tape_backup_status(
-                    &email,
-                    Some(job.jobname()),
-                    &setup,
-                    &job_result,
-                    summary,
-                ) {
-                    eprintln!("send tape backup notification failed: {}", err);
-                }
+            if let Err(err) = crate::server::send_tape_backup_status(
+                Some(job.jobname()),
+                &setup,
+                &job_result,
+                summary,
+            ) {
+                eprintln!("send tape backup notification failed: {err}");
             }
 
             if let Err(err) = job.finish(status) {
@@ -328,12 +319,6 @@ pub fn backup(
 
     let job_id = format!("{}:{}:{}", setup.store, setup.pool, setup.drive);
 
-    let notify_user = setup
-        .notify_user
-        .as_ref()
-        .unwrap_or_else(|| Userid::root_userid());
-    let email = lookup_user_email(notify_user);
-
     let upid_str = WorkerTask::new_thread(
         "tape-backup",
         Some(job_id),
@@ -349,21 +334,14 @@ pub fn backup(
                 datastore,
                 &pool_config,
                 &setup,
-                email.clone(),
                 &mut summary,
                 force_media_set,
             );
 
-            if let Some(email) = email {
-                if let Err(err) = crate::server::send_tape_backup_status(
-                    &email,
-                    None,
-                    &setup,
-                    &job_result,
-                    summary,
-                ) {
-                    eprintln!("send tape backup notification failed: {}", err);
-                }
+            if let Err(err) =
+                crate::server::send_tape_backup_status(None, &setup, &job_result, summary)
+            {
+                eprintln!("send tape backup notification failed: {err}");
             }
 
             // ignore errors
@@ -386,7 +364,6 @@ fn backup_worker(
     datastore: Arc<DataStore>,
     pool_config: &MediaPoolConfig,
     setup: &TapeBackupJobSetup,
-    email: Option<String>,
     summary: &mut TapeBackupJobSummary,
     force_media_set: bool,
 ) -> Result<(), Error> {
@@ -399,9 +376,16 @@ fn backup_worker(
     let ns_magic = !root_namespace.is_root() || setup.max_depth != Some(0);
 
     let pool = MediaPool::with_config(TAPE_STATUS_DIR, pool_config, changer_name, false)?;
+    let notification_mode = TapeNotificationMode::from(setup);
 
-    let mut pool_writer =
-        PoolWriter::new(pool, &setup.drive, worker, email, force_media_set, ns_magic)?;
+    let mut pool_writer = PoolWriter::new(
+        pool,
+        &setup.drive,
+        worker,
+        notification_mode,
+        force_media_set,
+        ns_magic,
+    )?;
 
     let mut group_list = Vec::new();
     let namespaces = datastore.recursive_iter_backup_ns_ok(root_namespace, setup.max_depth)?;
diff --git a/src/api2/tape/restore.rs b/src/api2/tape/restore.rs
index 8273c867..84557bce 100644
--- a/src/api2/tape/restore.rs
+++ b/src/api2/tape/restore.rs
@@ -18,9 +18,10 @@ use proxmox_uuid::Uuid;
 
 use pbs_api_types::{
     parse_ns_and_snapshot, print_ns_and_snapshot, Authid, BackupDir, BackupNamespace, CryptMode,
-    Operation, TapeRestoreNamespace, Userid, DATASTORE_MAP_ARRAY_SCHEMA, DATASTORE_MAP_LIST_SCHEMA,
-    DRIVE_NAME_SCHEMA, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY,
-    PRIV_TAPE_READ, TAPE_RESTORE_NAMESPACE_SCHEMA, TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA,
+    NotificationMode, Operation, TapeRestoreNamespace, Userid, DATASTORE_MAP_ARRAY_SCHEMA,
+    DATASTORE_MAP_LIST_SCHEMA, DRIVE_NAME_SCHEMA, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_BACKUP,
+    PRIV_DATASTORE_MODIFY, PRIV_TAPE_READ, TAPE_RESTORE_NAMESPACE_SCHEMA,
+    TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA,
 };
 use pbs_config::CachedUserInfo;
 use pbs_datastore::dynamic_index::DynamicIndexReader;
@@ -34,8 +35,8 @@ use pbs_tape::{
 use proxmox_rest_server::WorkerTask;
 
 use crate::backup::check_ns_modification_privs;
+use crate::tape::TapeNotificationMode;
 use crate::{
-    server::lookup_user_email,
     tape::{
         drive::{lock_tape_device, request_and_load_media, set_tape_device_state, TapeDriver},
         file_formats::{
@@ -289,6 +290,10 @@ pub const ROUTER: Router = Router::new().post(&API_METHOD_RESTORE);
                 type: Userid,
                 optional: true,
             },
+            "notification-mode": {
+                type: NotificationMode,
+                optional: true,
+            },
             "snapshots": {
                 description: "List of snapshots.",
                 type: Array,
@@ -322,6 +327,7 @@ pub fn restore(
     namespaces: Option<Vec<String>>,
     media_set: String,
     notify_user: Option<Userid>,
+    notification_mode: Option<NotificationMode>,
     snapshots: Option<Vec<String>>,
     owner: Option<Authid>,
     rpcenv: &mut dyn RpcEnvironment,
@@ -329,6 +335,8 @@ pub fn restore(
     let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
     let user_info = CachedUserInfo::new()?;
 
+    let notification_mode = TapeNotificationMode::from((notify_user, notification_mode));
+
     let mut store_map = DataStoreMap::try_from(store)
         .map_err(|err| format_err!("cannot parse store mapping: {err}"))?;
     let namespaces = if let Some(maps) = namespaces {
@@ -394,11 +402,6 @@ pub fn restore(
 
             let restore_owner = owner.as_ref().unwrap_or(&auth_id);
 
-            let email = notify_user
-                .as_ref()
-                .and_then(lookup_user_email)
-                .or_else(|| lookup_user_email(&auth_id.clone().into()));
-
             task_log!(worker, "Mediaset '{media_set}'");
             task_log!(worker, "Pool: {pool}");
 
@@ -412,7 +415,7 @@ pub fn restore(
                     &drive,
                     store_map,
                     restore_owner,
-                    email,
+                    &notification_mode,
                     user_info,
                     &auth_id,
                 )
@@ -425,7 +428,7 @@ pub fn restore(
                     &drive,
                     store_map,
                     restore_owner,
-                    email,
+                    &notification_mode,
                     &auth_id,
                 )
             };
@@ -452,7 +455,7 @@ fn restore_full_worker(
     drive_name: &str,
     store_map: DataStoreMap,
     restore_owner: &Authid,
-    email: Option<String>,
+    notification_mode: &TapeNotificationMode,
     auth_id: &Authid,
 ) -> Result<(), Error> {
     let members = inventory.compute_media_set_members(&media_set_uuid)?;
@@ -519,7 +522,7 @@ fn restore_full_worker(
             &store_map,
             &mut checked_chunks_map,
             restore_owner,
-            &email,
+            notification_mode,
             auth_id,
         )?;
     }
@@ -635,7 +638,7 @@ fn restore_list_worker(
     drive_name: &str,
     store_map: DataStoreMap,
     restore_owner: &Authid,
-    email: Option<String>,
+    notification_mode: &TapeNotificationMode,
     user_info: Arc<CachedUserInfo>,
     auth_id: &Authid,
 ) -> Result<(), Error> {
@@ -779,7 +782,7 @@ fn restore_list_worker(
                 &drive_config,
                 drive_name,
                 &media_id.label,
-                &email,
+                notification_mode,
             )?;
             file_list.sort_unstable();
 
@@ -833,7 +836,7 @@ fn restore_list_worker(
                 &drive_config,
                 drive_name,
                 &media_id.label,
-                &email,
+                notification_mode,
             )?;
             restore_file_chunk_map(worker.clone(), &mut drive, &store_map, file_chunk_map)?;
         }
@@ -1241,7 +1244,7 @@ pub fn request_and_restore_media(
     store_map: &DataStoreMap,
     checked_chunks_map: &mut HashMap<String, HashSet<[u8; 32]>>,
     restore_owner: &Authid,
-    email: &Option<String>,
+    notification_mode: &TapeNotificationMode,
     auth_id: &Authid,
 ) -> Result<(), Error> {
     let media_set_uuid = match media_id.media_set_label {
@@ -1249,8 +1252,13 @@ pub fn request_and_restore_media(
         Some(ref set) => &set.uuid,
     };
 
-    let (mut drive, info) =
-        request_and_load_media(&worker, drive_config, drive_name, &media_id.label, email)?;
+    let (mut drive, info) = request_and_load_media(
+        &worker,
+        drive_config,
+        drive_name,
+        &media_id.label,
+        notification_mode,
+    )?;
 
     match info.media_set_label {
         None => {
diff --git a/src/server/notifications.rs b/src/server/notifications.rs
index 16b506f0..cb2f852b 100644
--- a/src/server/notifications.rs
+++ b/src/server/notifications.rs
@@ -1,19 +1,17 @@
-use anyhow::Error;
-use const_format::concatcp;
-use serde_json::json;
 use std::collections::HashMap;
 use std::path::Path;
 use std::time::{Duration, Instant};
 
-use handlebars::{Handlebars, TemplateError};
+use anyhow::Error;
+use const_format::concatcp;
 use nix::unistd::Uid;
+use serde_json::json;
 
-use proxmox_lang::try_block;
 use proxmox_notify::context::pbs::PBS_CONTEXT;
 use proxmox_schema::ApiType;
-use proxmox_sys::email::sendmail;
 use proxmox_sys::fs::{create_path, CreateOptions};
 
+use crate::tape::TapeNotificationMode;
 use pbs_api_types::{
     APTUpdateInfo, DataStoreConfig, DatastoreNotify, GarbageCollectionStatus, NotificationMode,
     Notify, SyncJobConfig, TapeBackupJobSetup, User, Userid, VerificationJobConfig,
@@ -23,92 +21,6 @@ use proxmox_notify::{Endpoint, Notification, Severity};
 
 const SPOOL_DIR: &str = concatcp!(pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR, "/notifications");
 
-const TAPE_BACKUP_OK_TEMPLATE: &str = r###"
-
-{{#if id ~}}
-Job ID:     {{id}}
-{{/if~}}
-Datastore:  {{job.store}}
-Tape Pool:  {{job.pool}}
-Tape Drive: {{job.drive}}
-
-{{#if snapshot-list ~}}
-Snapshots included:
-
-{{#each snapshot-list~}}
-{{this}}
-{{/each~}}
-{{/if}}
-Duration: {{duration}}
-{{#if used-tapes }}
-Used Tapes:
-{{#each used-tapes~}}
-{{this}}
-{{/each~}}
-{{/if}}
-Tape Backup successful.
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#DataStore-{{job.store}}>
-
-"###;
-
-const TAPE_BACKUP_ERR_TEMPLATE: &str = r###"
-
-{{#if id ~}}
-Job ID:     {{id}}
-{{/if~}}
-Datastore:  {{job.store}}
-Tape Pool:  {{job.pool}}
-Tape Drive: {{job.drive}}
-
-{{#if snapshot-list ~}}
-Snapshots included:
-
-{{#each snapshot-list~}}
-{{this}}
-{{/each~}}
-{{/if}}
-{{#if used-tapes }}
-Used Tapes:
-{{#each used-tapes~}}
-{{this}}
-{{/each~}}
-{{/if}}
-Tape Backup failed: {{error}}
-
-
-Please visit the web interface for further details:
-
-<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
-
-"###;
-
-lazy_static::lazy_static! {
-
-    static ref HANDLEBARS: Handlebars<'static> = {
-        let mut hb = Handlebars::new();
-        let result: Result<(), TemplateError> = try_block!({
-
-            hb.set_strict_mode(true);
-            hb.register_escape_fn(handlebars::no_escape);
-
-            hb.register_template_string("tape_backup_ok_template", TAPE_BACKUP_OK_TEMPLATE)?;
-            hb.register_template_string("tape_backup_err_template", TAPE_BACKUP_ERR_TEMPLATE)?;
-
-            Ok(())
-        });
-
-        if let Err(err) = result {
-            eprintln!("error during template registration: {err}");
-        }
-
-        hb
-    };
-}
-
 /// Initialize the notification system by setting context in proxmox_notify
 pub fn init() -> Result<(), Error> {
     proxmox_notify::context::set_context(&PBS_CONTEXT);
@@ -218,30 +130,6 @@ pub struct TapeBackupJobSummary {
     pub used_tapes: Option<Vec<String>>,
 }
 
-fn send_job_status_mail(email: &str, subject: &str, text: &str) -> Result<(), Error> {
-    let (config, _) = crate::config::node::config()?;
-    let from = config.email_from;
-
-    // NOTE: some (web)mailers have big problems displaying text mails, so include html as well
-    let escaped_text = handlebars::html_escape(text);
-    let html = format!("<html><body><pre>\n{escaped_text}\n<pre>");
-
-    let nodename = proxmox_sys::nodename();
-
-    let author = format!("Proxmox Backup Server - {nodename}");
-
-    sendmail(
-        &[email],
-        subject,
-        Some(text),
-        Some(&html),
-        from.as_deref(),
-        Some(&author),
-    )?;
-
-    Ok(())
-}
-
 pub fn send_gc_status(
     datastore: &str,
     status: &GarbageCollectionStatus,
@@ -449,7 +337,6 @@ pub fn send_sync_status(job: &SyncJobConfig, result: &Result<(), Error>) -> Resu
 }
 
 pub fn send_tape_backup_status(
-    email: &str,
     id: Option<&str>,
     job: &TapeBackupJobSetup,
     result: &Result<(), Error>,
@@ -464,62 +351,81 @@ pub fn send_tape_backup_status(
         "id": id,
         "snapshot-list": summary.snapshot_list,
         "used-tapes": summary.used_tapes,
-        "duration": duration.to_string(),
+        "job-duration": duration.to_string(),
     });
 
-    let text = match result {
-        Ok(()) => HANDLEBARS.render("tape_backup_ok_template", &data)?,
+    let (template, severity) = match result {
+        Ok(()) => ("tape-backup-ok", Severity::Info),
         Err(err) => {
             data["error"] = err.to_string().into();
-            HANDLEBARS.render("tape_backup_err_template", &data)?
+            ("tape-backup-err", Severity::Error)
         }
     };
 
-    let subject = match (result, id) {
-        (Ok(()), Some(id)) => format!("Tape Backup '{id}' datastore '{}' successful", job.store,),
-        (Ok(()), None) => format!("Tape Backup datastore '{}' successful", job.store,),
-        (Err(_), Some(id)) => format!("Tape Backup '{id}' datastore '{}' failed", job.store,),
-        (Err(_), None) => format!("Tape Backup datastore '{}' failed", job.store,),
-    };
+    let metadata = HashMap::from([
+        ("datastore".into(), job.store.clone()),
+        ("media-pool".into(), job.pool.clone()),
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "tape-backup".into()),
+    ]);
+    let notification = Notification::from_template(severity, template, data, metadata);
 
-    send_job_status_mail(email, &subject, &text)?;
+    let mode = TapeNotificationMode::from(job);
+
+    match &mode {
+        TapeNotificationMode::LegacySendmail { notify_user } => {
+            let email = lookup_user_email(notify_user);
+
+            if let Some(email) = email {
+                send_sendmail_legacy_notification(notification, &email)?;
+            }
+        }
+        TapeNotificationMode::NotificationSystem => {
+            send_notification(notification)?;
+        }
+    }
 
     Ok(())
 }
 
 /// Send email to a person to request a manual media change
-pub fn send_load_media_email(
+pub fn send_load_media_notification(
+    mode: &TapeNotificationMode,
     changer: bool,
     device: &str,
     label_text: &str,
-    to: &str,
     reason: Option<String>,
 ) -> Result<(), Error> {
-    use std::fmt::Write as _;
-
     let device_type = if changer { "changer" } else { "drive" };
 
-    let subject = format!("Load Media '{label_text}' request for {device_type} '{device}'");
+    let data = json!({
+        "device-type": device_type,
+        "device": device,
+        "label-text": label_text,
+        "reason": reason,
+        "is-changer": changer,
+    });
 
-    let mut text = String::new();
+    let metadata = HashMap::from([
+        ("hostname".into(), proxmox_sys::nodename().into()),
+        ("type".into(), "tape-load".into()),
+    ]);
+    let notification = Notification::from_template(Severity::Notice, "tape-load", data, metadata);
 
-    if let Some(reason) = reason {
-        let _ = write!(
-            text,
-            "The {device_type} has the wrong or no tape(s) inserted. Error:\n{reason}\n\n"
-        );
-    }
+    match mode {
+        TapeNotificationMode::LegacySendmail { notify_user } => {
+            let email = lookup_user_email(notify_user);
 
-    if changer {
-        text.push_str("Please insert the requested media into the changer.\n\n");
-        let _ = writeln!(text, "Changer: {device}");
-    } else {
-        text.push_str("Please insert the requested media into the backup drive.\n\n");
-        let _ = writeln!(text, "Drive: {device}");
+            if let Some(email) = email {
+                send_sendmail_legacy_notification(notification, &email)?;
+            }
+        }
+        TapeNotificationMode::NotificationSystem => {
+            send_notification(notification)?;
+        }
     }
-    let _ = writeln!(text, "Media: {label_text}");
 
-    send_job_status_mail(to, &subject, &text)
+    Ok(())
 }
 
 fn get_server_url() -> (String, usize) {
@@ -636,9 +542,3 @@ pub fn lookup_datastore_notify_settings(
 
     (email, notify, notification_mode)
 }
-
-#[test]
-fn test_template_register() {
-    assert!(HANDLEBARS.has_template("tape_backup_ok_template"));
-    assert!(HANDLEBARS.has_template("tape_backup_err_template"));
-}
diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index 8607d64b..39602461 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -27,8 +27,9 @@ use pbs_key_config::KeyConfig;
 
 use pbs_tape::{sg_tape::TapeAlertFlags, BlockReadError, MediaContentHeader, TapeRead, TapeWrite};
 
+use crate::tape::TapeNotificationMode;
 use crate::{
-    server::send_load_media_email,
+    server::send_load_media_notification,
     tape::{
         changer::{MediaChange, MtxMediaChanger},
         drive::virtual_tape::open_virtual_tape_drive,
@@ -368,7 +369,7 @@ pub fn request_and_load_media(
     config: &SectionConfigData,
     drive: &str,
     label: &MediaLabel,
-    notify_email: &Option<String>,
+    notification_mode: &TapeNotificationMode,
 ) -> Result<(Box<dyn TapeDriver>, MediaId), Error> {
     let check_label = |handle: &mut dyn TapeDriver, uuid: &proxmox_uuid::Uuid| {
         if let Ok((Some(media_id), _)) = handle.read_label() {
@@ -428,15 +429,14 @@ pub fn request_and_load_media(
                                     device_type,
                                     device
                                 );
-                                if let Some(to) = notify_email {
-                                    send_load_media_email(
-                                        changer.is_some(),
-                                        device,
-                                        &label_text,
-                                        to,
-                                        Some(new.to_string()),
-                                    )?;
-                                }
+                                send_load_media_notification(
+                                    notification_mode,
+                                    changer.is_some(),
+                                    device,
+                                    &label_text,
+                                    Some(new.to_string()),
+                                )?;
+
                                 *old = new;
                             }
                             Ok(())
diff --git a/src/tape/mod.rs b/src/tape/mod.rs
index 7a928884..f276f948 100644
--- a/src/tape/mod.rs
+++ b/src/tape/mod.rs
@@ -1,6 +1,7 @@
 //! Magnetic tape backup
 
 use anyhow::{format_err, Error};
+use proxmox_auth_api::types::Userid;
 
 use proxmox_sys::fs::{create_path, CreateOptions};
 
@@ -29,6 +30,7 @@ pub use media_catalog::*;
 
 mod media_catalog_cache;
 pub use media_catalog_cache::*;
+use pbs_api_types::{NotificationMode, TapeBackupJobSetup};
 
 mod pool_writer;
 pub use pool_writer::*;
@@ -128,3 +130,28 @@ pub fn create_changer_state_dir() -> Result<(), Error> {
 
     Ok(())
 }
+
+#[derive(Clone)]
+pub enum TapeNotificationMode {
+    LegacySendmail { notify_user: Userid },
+    NotificationSystem,
+}
+
+impl From<&TapeBackupJobSetup> for TapeNotificationMode {
+    fn from(value: &TapeBackupJobSetup) -> Self {
+        Self::from((value.notify_user.clone(), value.notification_mode.clone()))
+    }
+}
+
+impl From<(Option<Userid>, Option<NotificationMode>)> for TapeNotificationMode {
+    fn from(value: (Option<Userid>, Option<NotificationMode>)) -> Self {
+        match value.1.as_ref().unwrap_or(&Default::default()) {
+            NotificationMode::LegacySendmail => {
+                let notify_user = value.0.as_ref().unwrap_or(Userid::root_userid()).clone();
+
+                Self::LegacySendmail { notify_user }
+            }
+            NotificationMode::NotificationSystem => Self::NotificationSystem,
+        }
+    }
+}
diff --git a/src/tape/pool_writer/mod.rs b/src/tape/pool_writer/mod.rs
index a6ba4a1d..21426080 100644
--- a/src/tape/pool_writer/mod.rs
+++ b/src/tape/pool_writer/mod.rs
@@ -25,7 +25,8 @@ use crate::tape::{
     file_formats::{
         tape_write_catalog, tape_write_snapshot_archive, ChunkArchiveWriter, MediaSetLabel,
     },
-    MediaCatalog, MediaId, MediaPool, COMMIT_BLOCK_SIZE, MAX_CHUNK_ARCHIVE_SIZE, TAPE_STATUS_DIR,
+    MediaCatalog, MediaId, MediaPool, TapeNotificationMode, COMMIT_BLOCK_SIZE,
+    MAX_CHUNK_ARCHIVE_SIZE, TAPE_STATUS_DIR,
 };
 
 use super::file_formats::{
@@ -52,7 +53,7 @@ pub struct PoolWriter {
     drive_name: String,
     status: Option<PoolWriterState>,
     catalog_set: Arc<Mutex<CatalogSet>>,
-    notify_email: Option<String>,
+    notification_mode: TapeNotificationMode,
     ns_magic: bool,
     used_tapes: HashSet<Uuid>,
 }
@@ -62,7 +63,7 @@ impl PoolWriter {
         mut pool: MediaPool,
         drive_name: &str,
         worker: &WorkerTask,
-        notify_email: Option<String>,
+        notification_mode: TapeNotificationMode,
         force_media_set: bool,
         ns_magic: bool,
     ) -> Result<Self, Error> {
@@ -90,7 +91,7 @@ impl PoolWriter {
             drive_name: drive_name.to_string(),
             status: None,
             catalog_set: Arc::new(Mutex::new(catalog_set)),
-            notify_email,
+            notification_mode,
             ns_magic,
             used_tapes: HashSet::new(),
         })
@@ -248,7 +249,7 @@ impl PoolWriter {
             &drive_config,
             &self.drive_name,
             media.label(),
-            &self.notify_email,
+            &self.notification_mode,
         )?;
 
         // test for critical tape alert flags
diff --git a/templates/Makefile b/templates/Makefile
index 824d28d9..0f8ad72c 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -17,6 +17,12 @@ NOTIFICATION_TEMPLATES=						\
 	default/sync-ok-body.txt.hbs			\
 	default/sync-err-subject.txt.hbs		\
 	default/sync-ok-subject.txt.hbs			\
+	default/tape-backup-err-body.txt.hbs	\
+	default/tape-backup-err-subject.txt.hbs	\
+	default/tape-backup-ok-body.txt.hbs		\
+	default/tape-backup-ok-subject.txt.hbs	\
+	default/tape-load-body.txt.hbs			\
+	default/tape-load-subject.txt.hbs		\
 	default/test-body.txt.hbs				\
 	default/test-body.html.hbs				\
 	default/test-subject.txt.hbs			\
diff --git a/templates/default/tape-backup-err-body.txt.hbs b/templates/default/tape-backup-err-body.txt.hbs
new file mode 100644
index 00000000..cc45c882
--- /dev/null
+++ b/templates/default/tape-backup-err-body.txt.hbs
@@ -0,0 +1,26 @@
+{{#if id ~}}
+Job ID:     {{id}}
+{{/if~}}
+Datastore:  {{job.store}}
+Tape Pool:  {{job.pool}}
+Tape Drive: {{job.drive}}
+
+{{#if snapshot-list ~}}
+Snapshots included:
+
+{{#each snapshot-list~}}
+{{this}}
+{{/each~}}
+{{/if}}
+{{#if used-tapes }}
+Used Tapes:
+{{#each used-tapes~}}
+{{this}}
+{{/each~}}
+{{/if}}
+Tape Backup failed: {{error}}
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#pbsServerAdministration:tasks>
diff --git a/templates/default/tape-backup-err-subject.txt.hbs b/templates/default/tape-backup-err-subject.txt.hbs
new file mode 100644
index 00000000..b52d338a
--- /dev/null
+++ b/templates/default/tape-backup-err-subject.txt.hbs
@@ -0,0 +1,5 @@
+{{#if id~}}
+Tape Backup '{{ id }}' datastore '{{ job.store }}' failed
+{{else~}}
+Tape Backup datastore '{{ job.store }}' failed
+{{/if}}
diff --git a/templates/default/tape-backup-ok-body.txt.hbs b/templates/default/tape-backup-ok-body.txt.hbs
new file mode 100644
index 00000000..ede51d05
--- /dev/null
+++ b/templates/default/tape-backup-ok-body.txt.hbs
@@ -0,0 +1,27 @@
+{{#if id ~}}
+Job ID:     {{id}}
+{{/if~}}
+Datastore:  {{job.store}}
+Tape Pool:  {{job.pool}}
+Tape Drive: {{job.drive}}
+
+{{#if snapshot-list ~}}
+Snapshots included:
+
+{{#each snapshot-list~}}
+{{this}}
+{{/each~}}
+{{/if}}
+Duration: {{job-duration}}
+{{#if used-tapes }}
+Used Tapes:
+{{#each used-tapes~}}
+{{this}}
+{{/each~}}
+{{/if}}
+Tape Backup successful.
+
+
+Please visit the web interface for further details:
+
+<https://{{fqdn}}:{{port}}/#DataStore-{{job.store}}>
diff --git a/templates/default/tape-backup-ok-subject.txt.hbs b/templates/default/tape-backup-ok-subject.txt.hbs
new file mode 100644
index 00000000..c475c05b
--- /dev/null
+++ b/templates/default/tape-backup-ok-subject.txt.hbs
@@ -0,0 +1,5 @@
+{{#if id~}}
+Tape Backup '{{ id }}' datastore '{{ job.store }}' successful
+{{else~}}
+Tape Backup datastore '{{ job.store }}' successful
+{{/if}}
diff --git a/templates/default/tape-load-body.txt.hbs b/templates/default/tape-load-body.txt.hbs
new file mode 100644
index 00000000..ddc8a9e1
--- /dev/null
+++ b/templates/default/tape-load-body.txt.hbs
@@ -0,0 +1,15 @@
+{{#if reason~}}
+The {{ device-type }} has the wrong or no tape(s) inserted. Error:
+{{ reason }}
+
+{{/if~}}
+{{#if is-changer~}}
+Please insert the requested media into the changer.
+
+Changer: {{ device }}
+{{else}}
+Please insert the requested media into the backup drive.
+
+Drive: {{ device }}
+{{/if}}
+Media: {{ label-text }}
diff --git a/templates/default/tape-load-subject.txt.hbs b/templates/default/tape-load-subject.txt.hbs
new file mode 100644
index 00000000..10f6a02e
--- /dev/null
+++ b/templates/default/tape-load-subject.txt.hbs
@@ -0,0 +1 @@
+Load Media '{{ label-text }}' request for {{ device-type }} '{{ device }}'
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 21/33] ui: add notification config panel
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (19 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 20/33] server: notifications: send tape " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 22/33] ui: tape backup job: add selector for notification-mode Lukas Wagner
                   ` (14 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

This commit adds the same notification configuration panel that we
already use in Proxmox VE.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/Makefile                         |  1 +
 www/NavigationTree.js                |  6 ++++++
 www/config/NotificationConfigView.js | 11 +++++++++++
 3 files changed, 18 insertions(+)
 create mode 100644 www/config/NotificationConfigView.js

diff --git a/www/Makefile b/www/Makefile
index 79cb4c04..f3d90bc4 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -67,6 +67,7 @@ JSSRC=							\
 	config/CertificateView.js			\
 	config/NodeOptionView.js			\
 	config/MetricServerView.js			\
+	config/NotificationConfigView.js	\
 	window/ACLEdit.js				\
 	window/BackupGroupChangeOwner.js		\
 	window/CreateDirectory.js			\
diff --git a/www/NavigationTree.js b/www/NavigationTree.js
index 3e0040f7..a5ea390f 100644
--- a/www/NavigationTree.js
+++ b/www/NavigationTree.js
@@ -68,6 +68,12 @@ Ext.define('PBS.store.NavigationStore', {
 			path: 'pbsCertificateConfiguration',
 			leaf: true,
 		    },
+		    {
+			text: gettext('Notifications'),
+			iconCls: 'fa fa-bell-o',
+			path: 'pbsNotificationConfigView',
+			leaf: true,
+		    },
 		    {
 			text: gettext('Subscription'),
 			iconCls: 'fa fa-support',
diff --git a/www/config/NotificationConfigView.js b/www/config/NotificationConfigView.js
new file mode 100644
index 00000000..904cee71
--- /dev/null
+++ b/www/config/NotificationConfigView.js
@@ -0,0 +1,11 @@
+Ext.define('PBS.config.NotificationConfigView', {
+    extend: 'Proxmox.panel.NotificationConfigView',
+    alias: ['widget.pbsNotificationConfigView'],
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    cbindData: function(_initialConfig) {
+        return {
+            baseUrl: '/config/notifications',
+        };
+    },
+});
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 22/33] ui: tape backup job: add selector for notification-mode
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (20 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 21/33] ui: add notification config panel Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 23/33] ui: tape backup: add selector for 'notification-mode' Lukas Wagner
                   ` (13 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/tape/window/TapeBackupJob.js | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/www/tape/window/TapeBackupJob.js b/www/tape/window/TapeBackupJob.js
index abbbaa0b..309dda0b 100644
--- a/www/tape/window/TapeBackupJob.js
+++ b/www/tape/window/TapeBackupJob.js
@@ -46,6 +46,15 @@ Ext.define('PBS.TapeManagement.BackupJobEdit', {
 	},
     },
 
+    viewModel: {
+	data: {
+	    notificationMode: 'notification-system',
+	},
+	formulas: {
+	    notificationSystemSelected: (get) => get('notificationMode') === 'notification-system',
+	},
+    },
+
     items: {
 	xtype: 'tabpanel',
 	bodyPadding: 10,
@@ -109,6 +118,18 @@ Ext.define('PBS.TapeManagement.BackupJobEdit', {
 			fieldLabel: gettext('Drive'),
 			name: 'drive',
 		    },
+		    {
+			xtype: 'proxmoxKVComboBox',
+			comboItems: [
+			    ['legacy-sendmail', gettext('Email (legacy)')],
+			    ['notification-system', gettext('Notification system')],
+			],
+			fieldLabel: gettext('Notification mode'),
+			name: 'notification-mode',
+			bind: {
+			    value: '{notificationMode}',
+			},
+		    },
 		    {
 			xtype: 'pmxUserSelector',
 			name: 'notify-user',
@@ -117,6 +138,9 @@ Ext.define('PBS.TapeManagement.BackupJobEdit', {
 			allowBlank: true,
 			value: null,
 			renderer: Ext.String.htmlEncode,
+			bind: {
+			    disabled: "{notificationSystemSelected}",
+			},
 		    },
 		],
 
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 23/33] ui: tape backup: add selector for 'notification-mode'
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (21 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 22/33] ui: tape backup job: add selector for notification-mode Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 24/33] ui: tape restore: add 'notification-mode' parameter Lukas Wagner
                   ` (12 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/tape/window/TapeBackup.js | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/www/tape/window/TapeBackup.js b/www/tape/window/TapeBackup.js
index 941b6b83..a8385339 100644
--- a/www/tape/window/TapeBackup.js
+++ b/www/tape/window/TapeBackup.js
@@ -8,6 +8,15 @@ Ext.define('PBS.TapeManagement.TapeBackupWindow', {
     showTaskViewer: true,
     isCreate: true,
 
+    viewModel: {
+	data: {
+	    notificationMode: 'notification-system',
+	},
+	formulas: {
+	    notificationSystemSelected: (get) => get('notificationMode') === 'notification-system',
+	},
+    },
+
     items: [
 	{
 	    xtype: 'inputpanel',
@@ -81,6 +90,19 @@ Ext.define('PBS.TapeManagement.TapeBackupWindow', {
 		    name: 'eject-media',
 		    fieldLabel: gettext('Eject Media'),
 		},
+		{
+		    xtype: 'proxmoxKVComboBox',
+		    labelWidth: 150,
+		    comboItems: [
+			['legacy-sendmail', gettext('Email (legacy)')],
+			['notification-system', gettext('Notification system')],
+		    ],
+		    fieldLabel: gettext('Notification mode'),
+		    name: 'notification-mode',
+		    bind: {
+			value: '{notificationMode}',
+		    },
+		},
 		{
 		    xtype: 'pmxUserSelector',
 		    labelWidth: 150,
@@ -90,6 +112,9 @@ Ext.define('PBS.TapeManagement.TapeBackupWindow', {
 		    value: null,
 		    allowBlank: true,
 		    skipEmptyText: true,
+		    bind: {
+			disabled: "{notificationSystemSelected}",
+		    },
 		    renderer: Ext.String.htmlEncode,
 		},
 	    ],
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 24/33] ui: tape restore: add 'notification-mode' parameter
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (22 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 23/33] ui: tape backup: add selector for 'notification-mode' Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 25/33] ui: datastore options: " Lukas Wagner
                   ` (11 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/tape/window/TapeRestore.js | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/www/tape/window/TapeRestore.js b/www/tape/window/TapeRestore.js
index c686c958..61a2fcd7 100644
--- a/www/tape/window/TapeRestore.js
+++ b/www/tape/window/TapeRestore.js
@@ -33,6 +33,7 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
 	data: {
 	    uuid: "",
 	    singleDatastore: true,
+	    notificationMode: 'notification-system',
 	},
 	formulas: {
 	    singleSelectorLabel: get =>
@@ -40,6 +41,7 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
 	    singleSelectorEmptyText: get => get('singleDatastore') ? '' : Proxmox.Utils.NoneText,
 	    singleSelectorLabelNs: get =>
 		get('singleDatastore') ? gettext('Target Namespace') : gettext('Default Namespace'),
+	    notificationSystemSelected: (get) => get('notificationMode') === 'notification-system',
 	},
     },
 
@@ -428,6 +430,18 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
 			return values;
 		    },
 		    column1: [
+			{
+			    xtype: 'proxmoxKVComboBox',
+			    comboItems: [
+				['legacy-sendmail', gettext('Email (legacy)')],
+				['notification-system', gettext('Notification system')],
+			    ],
+			    fieldLabel: gettext('Notification mode'),
+			    name: 'notification-mode',
+			    bind: {
+				value: '{notificationMode}',
+			    },
+			},
 			{
 			    xtype: 'pmxUserSelector',
 			    name: 'notify-user',
@@ -437,6 +451,9 @@ Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
 			    allowBlank: true,
 			    skipEmptyText: true,
 			    renderer: Ext.String.htmlEncode,
+			    bind: {
+				disabled: "{notificationSystemSelected}",
+			    },
 			},
 			{
 			    xtype: 'pbsAuthidSelector',
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 25/33] ui: datastore options: add 'notification-mode' parameter
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (23 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 24/33] ui: tape restore: add 'notification-mode' parameter Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 26/33] ui: utils: add overrides for known notification metadata fields/values Lukas Wagner
                   ` (10 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/datastore/OptionView.js | 15 +++++++++++++++
 www/window/NotifyOptions.js | 38 +++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/www/datastore/OptionView.js b/www/datastore/OptionView.js
index 6f1be969..e1f38af6 100644
--- a/www/datastore/OptionView.js
+++ b/www/datastore/OptionView.js
@@ -159,6 +159,21 @@ Ext.define('PBS.Datastore.Options', {
     },
 
     rows: {
+	"notification-mode": {
+	    required: true,
+	    defaultValue: 'legacy-sendmail',
+	    header: gettext('Notification mode'),
+	    renderer: function(value) {
+		if (value === 'notification-system') {
+		    return gettext('Notification system');
+		} else {
+		    return gettext('Email (legacy)');
+		}
+	    },
+	    editor: {
+		xtype: 'pbsNotifyOptionEdit',
+	    },
+	},
 	"notify": {
 	    required: true,
 	    header: gettext('Notify'),
diff --git a/www/window/NotifyOptions.js b/www/window/NotifyOptions.js
index 11765b43..95323486 100644
--- a/www/window/NotifyOptions.js
+++ b/www/window/NotifyOptions.js
@@ -44,6 +44,15 @@ Ext.define('PBS.window.NotifyOptions', {
 	labelWidth: 120,
     },
 
+    viewModel: {
+	data: {
+	    notificationMode: 'notification-system',
+	},
+	formulas: {
+	    notificationSystemSelected: (get) => get('notificationMode') === 'notification-system',
+	},
+    },
+
     items: {
 	xtype: 'inputpanel',
 	onGetValues: function(values) {
@@ -60,6 +69,18 @@ Ext.define('PBS.window.NotifyOptions', {
 	    return values;
 	},
 	items: [
+	    {
+		xtype: 'proxmoxKVComboBox',
+		comboItems: [
+		    ['legacy-sendmail', gettext('Email (legacy)')],
+		    ['notification-system', gettext('Notification system')],
+		],
+		fieldLabel: gettext('Notification mode'),
+		name: 'notification-mode',
+		bind: {
+		    value: '{notificationMode}',
+		},
+	    },
 	    {
 		xtype: 'pmxUserSelector',
 		name: 'notify-user',
@@ -69,6 +90,9 @@ Ext.define('PBS.window.NotifyOptions', {
 		allowBlank: true,
 		renderer: Ext.String.htmlEncode,
 		deleteEmpty: true,
+		bind: {
+		    disabled: "{notificationSystemSelected}",
+		},
 	    },
 	    {
 		xtype: 'pbsNotifyType',
@@ -76,6 +100,9 @@ Ext.define('PBS.window.NotifyOptions', {
 		fieldLabel: gettext('Verification Jobs'),
 		value: '__default__',
 		deleteEmpty: false,
+		bind: {
+		    disabled: "{notificationSystemSelected}",
+		},
 	    },
 	    {
 		xtype: 'pbsNotifyType',
@@ -83,6 +110,9 @@ Ext.define('PBS.window.NotifyOptions', {
 		fieldLabel: gettext('Sync Jobs'),
 		value: '__default__',
 		deleteEmpty: false,
+		bind: {
+		    disabled: "{notificationSystemSelected}",
+		},
 	    },
 	    {
 		xtype: 'pbsNotifyErrorDefaultType',
@@ -90,6 +120,9 @@ Ext.define('PBS.window.NotifyOptions', {
 		fieldLabel: gettext('Prune Jobs'),
 		value: '__default__',
 		deleteEmpty: false,
+		bind: {
+		    disabled: "{notificationSystemSelected}",
+		},
 	    },
 	    {
 		xtype: 'pbsNotifyType',
@@ -97,6 +130,9 @@ Ext.define('PBS.window.NotifyOptions', {
 		fieldLabel: gettext('Garbage Collection'),
 		value: '__default__',
 		deleteEmpty: false,
+		bind: {
+		    disabled: "{notificationSystemSelected}",
+		},
 	    },
 	],
     },
@@ -107,6 +143,8 @@ Ext.define('PBS.window.NotifyOptions', {
 	let options = {
 	    'notify-user': values['notify-user'],
 	    'verify-new': values['verify-new'],
+	    'notification-mode':
+		values['notification-mode'] ? values['notification-mode'] : 'legacy-sendmail',
 	};
 
 	let notify = {};
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 26/33] ui: utils: add overrides for known notification metadata fields/values
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (24 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 25/33] ui: datastore options: " Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 27/33] ui: datastore edit: make new stores use notification system by default Lukas Wagner
                   ` (9 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

This mechanism allows having nice, translatable notification event
types and fields.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/Utils.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/www/Utils.js b/www/Utils.js
index 5357949b..15a2c98c 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -429,6 +429,22 @@ Ext.define('PBS.Utils', {
 	    zfscreate: [gettext('ZFS Storage'), gettext('Create')],
 	});
 
+	Proxmox.Utils.overrideNotificationFieldName({
+	    'datastore': gettext('Datastore'),
+	    'media-pool': gettext('Media Pool'),
+	});
+
+	Proxmox.Utils.overrideNotificationFieldValue({
+	    'acme': gettext('ACME certificate renewal'),
+	    'gc': gettext('Garbage collection'),
+	    'package-updates': gettext('Package updates are available'),
+	    'prune': gettext('Prune job'),
+	    'sync': gettext('Sync job'),
+	    'tape-backup': gettext('Tape backup notifications'),
+	    'tape-load': gettext('Tape loading request'),
+	    'verify': gettext('Verification job'),
+	});
+
 	Proxmox.Schema.overrideAuthDomains({
 	    pbs: {
 		name: 'Proxmox Backup authentication server',
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 27/33] ui: datastore edit: make new stores use notification system by default
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (25 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 26/33] ui: utils: add overrides for known notification metadata fields/values Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 28/33] ui: permissions paths: add /system/notifications to combobox Lukas Wagner
                   ` (8 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/window/DataStoreEdit.js | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/www/window/DataStoreEdit.js b/www/window/DataStoreEdit.js
index aecf6b8d..b6115460 100644
--- a/www/window/DataStoreEdit.js
+++ b/www/window/DataStoreEdit.js
@@ -39,6 +39,9 @@ Ext.define('PBS.DataStoreEdit', {
 		title: gettext('General'),
 		xtype: 'inputpanel',
 		onlineHelp: 'datastore_intro',
+		cbind: {
+		    isCreate: '{isCreate}',
+		},
 		column1: [
 		    {
 			xtype: 'pmxDisplayEditField',
@@ -90,6 +93,16 @@ Ext.define('PBS.DataStoreEdit', {
 			fieldLabel: gettext('Comment'),
 		    },
 		],
+
+		onGetValues: function(values) {
+		    let me = this;
+
+		    if (me.isCreate) {
+			// New datastores default to using the notification system
+			values['notification-mode'] = 'notification-system';
+		    }
+		    return values;
+		},
 	    },
 	    {
 		title: gettext('Prune Options'),
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 28/33] ui: permissions paths: add /system/notifications to combobox
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (26 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 27/33] ui: datastore edit: make new stores use notification system by default Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 29/33] proxmox-backup-manager: add CLI for notification targets Lukas Wagner
                   ` (7 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

The /system/notifications ACL path is used for configuring the
notification system.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/form/PermissionPathSelector.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/www/form/PermissionPathSelector.js b/www/form/PermissionPathSelector.js
index 3f8b5376..8dcea195 100644
--- a/www/form/PermissionPathSelector.js
+++ b/www/form/PermissionPathSelector.js
@@ -19,6 +19,7 @@ Ext.define('PBS.data.PermissionPathsStore', {
 	{ 'value': '/system/network' },
 	{ 'value': '/system/network/dns' },
 	{ 'value': '/system/network/interfaces' },
+	{ 'value': '/system/notifications' },
 	{ 'value': '/system/services' },
 	{ 'value': '/system/status' },
 	{ 'value': '/system/tasks' },
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 29/33] proxmox-backup-manager: add CLI for notification targets
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (27 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 28/33] ui: permissions paths: add /system/notifications to combobox Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 30/33] proxmox-backup-manager: add CLI for notification matchers Lukas Wagner
                   ` (6 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/api2/config/notifications/mod.rs          | 10 ++--
 src/bin/proxmox-backup-manager.rs             |  2 +
 src/bin/proxmox_backup_manager/mod.rs         |  2 +
 .../notifications/mod.rs                      |  9 ++++
 .../notifications/targets.rs                  | 51 +++++++++++++++++++
 5 files changed, 69 insertions(+), 5 deletions(-)
 create mode 100644 src/bin/proxmox_backup_manager/notifications/mod.rs
 create mode 100644 src/bin/proxmox_backup_manager/notifications/targets.rs

diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index 2c8b780e..0f09cd4b 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -11,11 +11,11 @@ use crate::api2::config::datastore::list_datastores;
 use crate::api2::config::media_pool::list_pools;
 use pbs_api_types::PRIV_SYS_AUDIT;
 
-mod gotify;
-mod matchers;
-mod sendmail;
-mod smtp;
-mod targets;
+pub mod gotify;
+pub mod matchers;
+pub mod sendmail;
+pub mod smtp;
+pub mod targets;
 
 #[sortable]
 const SUBDIRS: SubdirMap = &sorted!([
diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs
index 115207f3..132de290 100644
--- a/src/bin/proxmox-backup-manager.rs
+++ b/src/bin/proxmox-backup-manager.rs
@@ -430,6 +430,7 @@ async fn get_versions(verbose: bool, param: Value) -> Result<Value, Error> {
 
 async fn run() -> Result<(), Error> {
     init_cli_logger("PBS_LOG", "info");
+    proxmox_backup::server::notifications::init()?;
 
     let cmd_def = CliCommandMap::new()
         .insert("acl", acl_commands())
@@ -439,6 +440,7 @@ async fn run() -> Result<(), Error> {
         .insert("ldap", ldap_commands())
         .insert("network", network_commands())
         .insert("node", node_commands())
+        .insert("notification", notification_commands())
         .insert("user", user_commands())
         .insert("openid", openid_commands())
         .insert("remote", remote_commands())
diff --git a/src/bin/proxmox_backup_manager/mod.rs b/src/bin/proxmox_backup_manager/mod.rs
index 8a1c140c..8992a7a7 100644
--- a/src/bin/proxmox_backup_manager/mod.rs
+++ b/src/bin/proxmox_backup_manager/mod.rs
@@ -28,6 +28,8 @@ mod disk;
 pub use disk::*;
 mod node;
 pub use node::*;
+mod notifications;
+pub use notifications::*;
 mod openid;
 pub use openid::*;
 mod traffic_control;
diff --git a/src/bin/proxmox_backup_manager/notifications/mod.rs b/src/bin/proxmox_backup_manager/notifications/mod.rs
new file mode 100644
index 00000000..fdd5abe3
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/notifications/mod.rs
@@ -0,0 +1,9 @@
+use proxmox_router::cli::{CliCommandMap, CommandLineInterface};
+
+mod targets;
+
+pub fn notification_commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new().insert("target", targets::commands());
+
+    cmd_def.into()
+}
diff --git a/src/bin/proxmox_backup_manager/notifications/targets.rs b/src/bin/proxmox_backup_manager/notifications/targets.rs
new file mode 100644
index 00000000..37603a92
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/notifications/targets.rs
@@ -0,0 +1,51 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
+use proxmox_schema::api;
+
+use proxmox_backup::api2;
+
+#[api(
+    input: {
+        properties: {
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// List targets.
+fn list_targets(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::targets::API_METHOD_LIST_TARGETS;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options()
+        .column(ColumnConfig::new("disable"))
+        .column(ColumnConfig::new("name"))
+        .column(ColumnConfig::new("type"))
+        .column(ColumnConfig::new("origin"))
+        .column(ColumnConfig::new("comment"));
+
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+pub fn commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new()
+        .insert("list", CliCommand::new(&API_METHOD_LIST_TARGETS))
+        .insert(
+            "test",
+            CliCommand::new(&api2::config::notifications::targets::API_METHOD_TEST_TARGET)
+                .arg_param(&["name"]),
+        );
+
+    cmd_def.into()
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 30/33] proxmox-backup-manager: add CLI for notification matchers
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (28 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 29/33] proxmox-backup-manager: add CLI for notification targets Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 31/33] proxmox-backup-manager: add CLI for gotify endpoints Lukas Wagner
                   ` (5 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 .../notifications/matchers.rs                 | 93 +++++++++++++++++++
 .../notifications/mod.rs                      |  5 +-
 2 files changed, 97 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/proxmox_backup_manager/notifications/matchers.rs

diff --git a/src/bin/proxmox_backup_manager/notifications/matchers.rs b/src/bin/proxmox_backup_manager/notifications/matchers.rs
new file mode 100644
index 00000000..cfa3aaa4
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/notifications/matchers.rs
@@ -0,0 +1,93 @@
+use anyhow::Error;
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use serde_json::Value;
+
+use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
+use proxmox_schema::api;
+
+use proxmox_backup::api2;
+
+#[api(
+    input: {
+        properties: {
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// List notification matchers.
+fn list_matchers(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::matchers::API_METHOD_LIST_MATCHERS;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options()
+        .column(ColumnConfig::new("disable"))
+        .column(ColumnConfig::new("name"))
+        .column(ColumnConfig::new("origin"))
+        .column(ColumnConfig::new("comment"));
+
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// Show a single matcher.
+fn show_matcher(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::matchers::API_METHOD_GET_MATCHER;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options();
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+pub fn commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new()
+        .insert("list", CliCommand::new(&API_METHOD_LIST_MATCHERS))
+        .insert(
+            "show",
+            CliCommand::new(&API_METHOD_SHOW_MATCHER).arg_param(&["name"]),
+        )
+        .insert(
+            "create",
+            CliCommand::new(&api2::config::notifications::matchers::API_METHOD_ADD_MATCHER)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "update",
+            CliCommand::new(&api2::config::notifications::matchers::API_METHOD_UPDATE_MATCHER)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "delete",
+            CliCommand::new(&api2::config::notifications::matchers::API_METHOD_DELETE_MATCHER)
+                .arg_param(&["name"]),
+        );
+    cmd_def.into()
+}
diff --git a/src/bin/proxmox_backup_manager/notifications/mod.rs b/src/bin/proxmox_backup_manager/notifications/mod.rs
index fdd5abe3..d83563f5 100644
--- a/src/bin/proxmox_backup_manager/notifications/mod.rs
+++ b/src/bin/proxmox_backup_manager/notifications/mod.rs
@@ -1,9 +1,12 @@
 use proxmox_router::cli::{CliCommandMap, CommandLineInterface};
 
+mod matchers;
 mod targets;
 
 pub fn notification_commands() -> CommandLineInterface {
-    let cmd_def = CliCommandMap::new().insert("target", targets::commands());
+    let cmd_def = CliCommandMap::new()
+        .insert("target", targets::commands())
+        .insert("matcher", matchers::commands());
 
     cmd_def.into()
 }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 31/33] proxmox-backup-manager: add CLI for gotify endpoints
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (29 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 30/33] proxmox-backup-manager: add CLI for notification matchers Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 32/33] proxmox-backup-manager: add CLI for sendmail endpoints Lukas Wagner
                   ` (4 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 .../notifications/gotify.rs                   | 93 +++++++++++++++++++
 .../notifications/mod.rs                      |  8 +-
 2 files changed, 99 insertions(+), 2 deletions(-)
 create mode 100644 src/bin/proxmox_backup_manager/notifications/gotify.rs

diff --git a/src/bin/proxmox_backup_manager/notifications/gotify.rs b/src/bin/proxmox_backup_manager/notifications/gotify.rs
new file mode 100644
index 00000000..541df82e
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/notifications/gotify.rs
@@ -0,0 +1,93 @@
+use anyhow::Error;
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use serde_json::Value;
+
+use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
+use proxmox_schema::api;
+
+use proxmox_backup::api2;
+
+#[api(
+    input: {
+        properties: {
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// List all endpoints.
+fn list_endpoints(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::gotify::API_METHOD_LIST_ENDPOINTS;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options()
+        .column(ColumnConfig::new("disable"))
+        .column(ColumnConfig::new("name"))
+        .column(ColumnConfig::new("server"))
+        .column(ColumnConfig::new("comment"));
+
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// Show a single endpoint.
+fn show_endpoint(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::gotify::API_METHOD_GET_ENDPOINT;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options();
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+pub fn commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new()
+        .insert("list", CliCommand::new(&API_METHOD_LIST_ENDPOINTS))
+        .insert(
+            "show",
+            CliCommand::new(&API_METHOD_SHOW_ENDPOINT).arg_param(&["name"]),
+        )
+        .insert(
+            "create",
+            CliCommand::new(&api2::config::notifications::gotify::API_METHOD_ADD_ENDPOINT)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "update",
+            CliCommand::new(&api2::config::notifications::gotify::API_METHOD_UPDATE_ENDPOINT)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "delete",
+            CliCommand::new(&api2::config::notifications::gotify::API_METHOD_DELETE_ENDPOINT)
+                .arg_param(&["name"]),
+        );
+    cmd_def.into()
+}
diff --git a/src/bin/proxmox_backup_manager/notifications/mod.rs b/src/bin/proxmox_backup_manager/notifications/mod.rs
index d83563f5..798d0066 100644
--- a/src/bin/proxmox_backup_manager/notifications/mod.rs
+++ b/src/bin/proxmox_backup_manager/notifications/mod.rs
@@ -1,12 +1,16 @@
 use proxmox_router::cli::{CliCommandMap, CommandLineInterface};
 
+mod gotify;
 mod matchers;
 mod targets;
 
 pub fn notification_commands() -> CommandLineInterface {
+    let endpoint_def = CliCommandMap::new().insert("gotify", gotify::commands());
+
     let cmd_def = CliCommandMap::new()
-        .insert("target", targets::commands())
-        .insert("matcher", matchers::commands());
+        .insert("endpoint", endpoint_def)
+        .insert("matcher", matchers::commands())
+        .insert("target", targets::commands());
 
     cmd_def.into()
 }
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 32/33] proxmox-backup-manager: add CLI for sendmail endpoints
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (30 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 31/33] proxmox-backup-manager: add CLI for gotify endpoints Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 33/33] proxmox-backup-manager: add CLI for SMTP endpoints Lukas Wagner
                   ` (3 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 .../notifications/mod.rs                      |  5 +-
 .../notifications/sendmail.rs                 | 94 +++++++++++++++++++
 2 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/proxmox_backup_manager/notifications/sendmail.rs

diff --git a/src/bin/proxmox_backup_manager/notifications/mod.rs b/src/bin/proxmox_backup_manager/notifications/mod.rs
index 798d0066..19a9dd0d 100644
--- a/src/bin/proxmox_backup_manager/notifications/mod.rs
+++ b/src/bin/proxmox_backup_manager/notifications/mod.rs
@@ -2,10 +2,13 @@ use proxmox_router::cli::{CliCommandMap, CommandLineInterface};
 
 mod gotify;
 mod matchers;
+mod sendmail;
 mod targets;
 
 pub fn notification_commands() -> CommandLineInterface {
-    let endpoint_def = CliCommandMap::new().insert("gotify", gotify::commands());
+    let endpoint_def = CliCommandMap::new()
+        .insert("gotify", gotify::commands())
+        .insert("sendmail", sendmail::commands());
 
     let cmd_def = CliCommandMap::new()
         .insert("endpoint", endpoint_def)
diff --git a/src/bin/proxmox_backup_manager/notifications/sendmail.rs b/src/bin/proxmox_backup_manager/notifications/sendmail.rs
new file mode 100644
index 00000000..202e15b0
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/notifications/sendmail.rs
@@ -0,0 +1,94 @@
+use anyhow::Error;
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use serde_json::Value;
+
+use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
+use proxmox_schema::api;
+
+use proxmox_backup::api2;
+
+#[api(
+    input: {
+        properties: {
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// List all endpoints.
+fn list_endpoints(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::sendmail::API_METHOD_LIST_ENDPOINTS;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options()
+        .column(ColumnConfig::new("disable"))
+        .column(ColumnConfig::new("name"))
+        .column(ColumnConfig::new("mailto"))
+        .column(ColumnConfig::new("mailto-user"))
+        .column(ColumnConfig::new("comment"));
+
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// Show a single endpoint.
+fn show_endpoint(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::sendmail::API_METHOD_GET_ENDPOINT;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options();
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+pub fn commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new()
+        .insert("list", CliCommand::new(&API_METHOD_LIST_ENDPOINTS))
+        .insert(
+            "show",
+            CliCommand::new(&API_METHOD_SHOW_ENDPOINT).arg_param(&["name"]),
+        )
+        .insert(
+            "create",
+            CliCommand::new(&api2::config::notifications::sendmail::API_METHOD_ADD_ENDPOINT)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "update",
+            CliCommand::new(&api2::config::notifications::sendmail::API_METHOD_UPDATE_ENDPOINT)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "delete",
+            CliCommand::new(&api2::config::notifications::sendmail::API_METHOD_DELETE_ENDPOINT)
+                .arg_param(&["name"]),
+        );
+    cmd_def.into()
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 33/33] proxmox-backup-manager: add CLI for SMTP endpoints
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (31 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 32/33] proxmox-backup-manager: add CLI for sendmail endpoints Lukas Wagner
@ 2024-04-12 10:06 ` Lukas Wagner
  2024-04-12 13:59 ` [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Gabriel Goller
                   ` (2 subsequent siblings)
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 10:06 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 .../notifications/mod.rs                      |  4 +-
 .../notifications/smtp.rs                     | 96 +++++++++++++++++++
 2 files changed, 99 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/proxmox_backup_manager/notifications/smtp.rs

diff --git a/src/bin/proxmox_backup_manager/notifications/mod.rs b/src/bin/proxmox_backup_manager/notifications/mod.rs
index 19a9dd0d..678f9c54 100644
--- a/src/bin/proxmox_backup_manager/notifications/mod.rs
+++ b/src/bin/proxmox_backup_manager/notifications/mod.rs
@@ -3,12 +3,14 @@ use proxmox_router::cli::{CliCommandMap, CommandLineInterface};
 mod gotify;
 mod matchers;
 mod sendmail;
+mod smtp;
 mod targets;
 
 pub fn notification_commands() -> CommandLineInterface {
     let endpoint_def = CliCommandMap::new()
         .insert("gotify", gotify::commands())
-        .insert("sendmail", sendmail::commands());
+        .insert("sendmail", sendmail::commands())
+        .insert("smtp", smtp::commands());
 
     let cmd_def = CliCommandMap::new()
         .insert("endpoint", endpoint_def)
diff --git a/src/bin/proxmox_backup_manager/notifications/smtp.rs b/src/bin/proxmox_backup_manager/notifications/smtp.rs
new file mode 100644
index 00000000..3a2b15ef
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/notifications/smtp.rs
@@ -0,0 +1,96 @@
+use anyhow::Error;
+use proxmox_notify::schema::ENTITY_NAME_SCHEMA;
+use serde_json::Value;
+
+use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
+use proxmox_schema::api;
+
+use proxmox_backup::api2;
+
+#[api(
+    input: {
+        properties: {
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// List all endpoints.
+fn list_endpoints(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::smtp::API_METHOD_LIST_ENDPOINTS;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options()
+        .column(ColumnConfig::new("disable"))
+        .column(ColumnConfig::new("name"))
+        .column(ColumnConfig::new("server"))
+        .column(ColumnConfig::new("from-address"))
+        .column(ColumnConfig::new("mailto"))
+        .column(ColumnConfig::new("mailto-user"))
+        .column(ColumnConfig::new("comment"));
+
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                schema: ENTITY_NAME_SCHEMA,
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// Show a single endpoint.
+fn show_endpoint(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::notifications::smtp::API_METHOD_GET_ENDPOINT;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options();
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+pub fn commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new()
+        .insert("list", CliCommand::new(&API_METHOD_LIST_ENDPOINTS))
+        .insert(
+            "show",
+            CliCommand::new(&API_METHOD_SHOW_ENDPOINT).arg_param(&["name"]),
+        )
+        .insert(
+            "create",
+            CliCommand::new(&api2::config::notifications::smtp::API_METHOD_ADD_ENDPOINT)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "update",
+            CliCommand::new(&api2::config::notifications::smtp::API_METHOD_UPDATE_ENDPOINT)
+                .arg_param(&["name"]),
+        )
+        .insert(
+            "delete",
+            CliCommand::new(&api2::config::notifications::smtp::API_METHOD_DELETE_ENDPOINT)
+                .arg_param(&["name"]),
+        );
+    cmd_def.into()
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (32 preceding siblings ...)
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 33/33] proxmox-backup-manager: add CLI for SMTP endpoints Lukas Wagner
@ 2024-04-12 13:59 ` Gabriel Goller
  2024-04-12 14:09   ` Lukas Wagner
  2024-04-17 12:31 ` Gabriel Goller
  2024-04-17 14:38 ` Lukas Wagner
  35 siblings, 1 reply; 47+ messages in thread
From: Gabriel Goller @ 2024-04-12 13:59 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

First of all, thanks for implementing this! I can't count how many times
I've seen a forum post asking for better notifications in pbs!

Some high-level comments:
 - The 'hostname' field doesn't make any sense in pbs (AFAICT) because
   there is only one node.
 - The 'type' field could have hardcoded values, such as 'gc', 'verify',
   etc. this way we can also have a drop-down menu in the ui. This makes
   it a lot easier than checking the docs/guessing.
 - A notification when a backup succeeds/fails would be very useful as
   well.
 - Also having datastore and namespace filters on the notification
   matchers would be great (if feasible obv.).


Another thing I noticed is that creating a datastore through the cli 
still uses the legacy notification system.

Will make a more in depth review next week!





^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-12 13:59 ` [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Gabriel Goller
@ 2024-04-12 14:09   ` Lukas Wagner
  2024-04-17  8:22     ` Lukas Wagner
  0 siblings, 1 reply; 47+ messages in thread
From: Lukas Wagner @ 2024-04-12 14:09 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Gabriel Goller

Hey! Thanks a lot for this first round of feedback!

On 12.04.24 15:59, Gabriel Goller wrote:
> First of all, thanks for implementing this! I can't count how many times
> I've seen a forum post asking for better notifications in pbs!
> 
> Some high-level comments:
>  - The 'hostname' field doesn't make any sense in pbs (AFAICT) because
>    there is only one node.

Right, we might just kick that out then.

>  - The 'type' field could have hardcoded values, such as 'gc', 'verify',
>    etc. this way we can also have a drop-down menu in the ui. This makes
>    it a lot easier than checking the docs/guessing.

If you apply/deploy the `proxmox-widget-toolkit` patches from
https://lists.proxmox.com/pipermail/pve-devel/2024-February/061992.html

then you should automatically get an improved UI for creating matchers
where one can select from known types and other metadata values. :)


>  - A notification when a backup succeeds/fails would be very useful as
>    well.

For now I have just ported the existing notifications - new ones
can of course be added later :)

>  - Also having datastore and namespace filters on the notification
>    matchers would be great (if feasible obv.).

Matching on datastores should also work already with the WTK-patches
mentioned above

> 
> 
> Another thing I noticed is that creating a datastore through the cli 
> still uses the legacy notification system.
> 

Ah, thx!

> Will make a more in depth review next week!
> 

Greatly appreciated!




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system Lukas Wagner
@ 2024-04-15  9:41   ` Gabriel Goller
  2024-04-15 14:10     ` Lukas Wagner
  2024-04-16  9:37   ` Gabriel Goller
  1 sibling, 1 reply; 47+ messages in thread
From: Gabriel Goller @ 2024-04-15  9:41 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

On Fri Apr 12, 2024 at 12:06 PM CEST, Lukas Wagner wrote:
> diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
> index 41375d72..ff5bdccf 100644
> --- a/src/server/gc_job.rs
> +++ b/src/server/gc_job.rs
> @@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
>  ) -> Result<String, Error> {
>      let store = datastore.name().to_string();
>  
> -    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
> -
>      let worker_type = job.jobtype().to_string();
>      let upid_str = WorkerTask::new_thread(
>          &worker_type,
> @@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
>                  eprintln!("could not finish job state for {}: {err}", job.jobtype());
>              }
>  
> -            if let Some(email) = email {
> -                let gc_status = datastore.last_gc_status();
> -                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
> -                    eprintln!("send gc notification failed: {err}");
> -                }
> +            let gc_status = datastore.last_gc_status();
> +            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
> +                eprintln!("send gc notification failed: {err}");
>              }

When a gc-job returns warnings (for example chunks missing), the
notification does not include them and simply returns a success.
Including the warnings would be great, but is a little bit problematic:
 - how do we get them? the TaskStatus only stores a counter for warnings
   and would somehow need to store them or search through the tasklog.
 - do we add another template for `warning`? the severity `warning` is 
   also kinda useless right now.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-15  9:41   ` Gabriel Goller
@ 2024-04-15 14:10     ` Lukas Wagner
  0 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-15 14:10 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Gabriel Goller



On  2024-04-15 11:41, Gabriel Goller wrote:
> On Fri Apr 12, 2024 at 12:06 PM CEST, Lukas Wagner wrote:
>> diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
>> index 41375d72..ff5bdccf 100644
>> --- a/src/server/gc_job.rs
>> +++ b/src/server/gc_job.rs
>> @@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
>>  ) -> Result<String, Error> {
>>      let store = datastore.name().to_string();
>>  
>> -    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
>> -
>>      let worker_type = job.jobtype().to_string();
>>      let upid_str = WorkerTask::new_thread(
>>          &worker_type,
>> @@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
>>                  eprintln!("could not finish job state for {}: {err}", job.jobtype());
>>              }
>>  
>> -            if let Some(email) = email {
>> -                let gc_status = datastore.last_gc_status();
>> -                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
>> -                    eprintln!("send gc notification failed: {err}");
>> -                }
>> +            let gc_status = datastore.last_gc_status();
>> +            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
>> +                eprintln!("send gc notification failed: {err}");
>>              }
> 
> When a gc-job returns warnings (for example chunks missing), the
> notification does not include them and simply returns a success.
> Including the warnings would be great, but is a little bit problematic:
>  - how do we get them? the TaskStatus only stores a counter for warnings
>    and would somehow need to store them or search through the tasklog.
>  - do we add another template for `warning`? the severity `warning` is 
>    also kinda useless right now.
> 

I agree, this would indeed be quite useful. I'll note that down for the future.
As of now I do not want to add new notifications or modify existing ones in
order to avoid scope creep.

-- 
- Lukas




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system Lukas Wagner
  2024-04-15  9:41   ` Gabriel Goller
@ 2024-04-16  9:37   ` Gabriel Goller
  2024-04-16 12:13     ` Lukas Wagner
  1 sibling, 1 reply; 47+ messages in thread
From: Gabriel Goller @ 2024-04-16  9:37 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

On Fri Apr 12, 2024 at 12:06 PM CEST, Lukas Wagner wrote:
> diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
> index 41375d72..ff5bdccf 100644
> --- a/src/server/gc_job.rs
> +++ b/src/server/gc_job.rs
> @@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
>  ) -> Result<String, Error> {
>      let store = datastore.name().to_string();
>  
> -    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
> -
>      let worker_type = job.jobtype().to_string();
>      let upid_str = WorkerTask::new_thread(
>          &worker_type,
> @@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
>                  eprintln!("could not finish job state for {}: {err}", job.jobtype());
>              }
>  
> -            if let Some(email) = email {
> -                let gc_status = datastore.last_gc_status();
> -                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
> -                    eprintln!("send gc notification failed: {err}");
> -                }
> +            let gc_status = datastore.last_gc_status();
> +            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
> +                eprintln!("send gc notification failed: {err}");

I think we should use 'task_err!()' here. I know eprintln is used above,
and technically works because we redirect stderr in the service setup 
but it's still slow and kinda the legacy method of printing task errors.

>              }
>  
>              result




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-16  9:37   ` Gabriel Goller
@ 2024-04-16 12:13     ` Lukas Wagner
  2024-04-17  7:46       ` Fabian Grünbichler
  0 siblings, 1 reply; 47+ messages in thread
From: Lukas Wagner @ 2024-04-16 12:13 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Gabriel Goller



On  2024-04-16 11:37, Gabriel Goller wrote:
> On Fri Apr 12, 2024 at 12:06 PM CEST, Lukas Wagner wrote:
>> diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
>> index 41375d72..ff5bdccf 100644
>> --- a/src/server/gc_job.rs
>> +++ b/src/server/gc_job.rs
>> @@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
>>  ) -> Result<String, Error> {
>>      let store = datastore.name().to_string();
>>  
>> -    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
>> -
>>      let worker_type = job.jobtype().to_string();
>>      let upid_str = WorkerTask::new_thread(
>>          &worker_type,
>> @@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
>>                  eprintln!("could not finish job state for {}: {err}", job.jobtype());
>>              }
>>  
>> -            if let Some(email) = email {
>> -                let gc_status = datastore.last_gc_status();
>> -                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
>> -                    eprintln!("send gc notification failed: {err}");
>> -                }
>> +            let gc_status = datastore.last_gc_status();
>> +            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
>> +                eprintln!("send gc notification failed: {err}");
> 
> I think we should use 'task_err!()' here. I know eprintln is used above,
> and technically works because we redirect stderr in the service setup 
> but it's still slow and kinda the legacy method of printing task errors.

I think the reason why the original code does not use task_log is because the
job is already marked as finished at that point:

            if let Err(err) = job.finish(status) {
                eprintln!("could not finish job state for {}: {err}", job.jobtype());
            }

            let gc_status = datastore.last_gc_status();
            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
                eprintln!("send gc notification failed: {err}");
            }

Other jobs seem to follow the same pattern - use task_log! before, and eprintln/log::error!
after the job is finished.
A `task_log!` after the job is finished still seems to work, but I'm not sure if that
might lead to problems. What do you think?


-- 
- Lukas




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-16 12:13     ` Lukas Wagner
@ 2024-04-17  7:46       ` Fabian Grünbichler
  2024-04-17 14:26         ` Lukas Wagner
  0 siblings, 1 reply; 47+ messages in thread
From: Fabian Grünbichler @ 2024-04-17  7:46 UTC (permalink / raw)
  To: Gabriel Goller, Proxmox Backup Server development discussion

On April 16, 2024 2:13 pm, Lukas Wagner wrote:
> 
> 
> On  2024-04-16 11:37, Gabriel Goller wrote:
>> On Fri Apr 12, 2024 at 12:06 PM CEST, Lukas Wagner wrote:
>>> diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
>>> index 41375d72..ff5bdccf 100644
>>> --- a/src/server/gc_job.rs
>>> +++ b/src/server/gc_job.rs
>>> @@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
>>>  ) -> Result<String, Error> {
>>>      let store = datastore.name().to_string();
>>>  
>>> -    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
>>> -
>>>      let worker_type = job.jobtype().to_string();
>>>      let upid_str = WorkerTask::new_thread(
>>>          &worker_type,
>>> @@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
>>>                  eprintln!("could not finish job state for {}: {err}", job.jobtype());
>>>              }
>>>  
>>> -            if let Some(email) = email {
>>> -                let gc_status = datastore.last_gc_status();
>>> -                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
>>> -                    eprintln!("send gc notification failed: {err}");
>>> -                }
>>> +            let gc_status = datastore.last_gc_status();
>>> +            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
>>> +                eprintln!("send gc notification failed: {err}");
>> 
>> I think we should use 'task_err!()' here. I know eprintln is used above,
>> and technically works because we redirect stderr in the service setup 
>> but it's still slow and kinda the legacy method of printing task errors.
> 
> I think the reason why the original code does not use task_log is because the
> job is already marked as finished at that point:
> 
>             if let Err(err) = job.finish(status) {
>                 eprintln!("could not finish job state for {}: {err}", job.jobtype());
>             }
> 
>             let gc_status = datastore.last_gc_status();
>             if let Err(err) = send_gc_status(&store, &gc_status, &result) {
>                 eprintln!("send gc notification failed: {err}");
>             }
> 
> Other jobs seem to follow the same pattern - use task_log! before, and eprintln/log::error!
> after the job is finished.
> A `task_log!` after the job is finished still seems to work, but I'm not sure if that
> might lead to problems. What do you think?

I don't think this is a problem per se - job.finish() just marks the job as
finished in the job state, it's not related to the worker task
finish/exit itself.

since task_log (and friends) require the worker to still exist, and the
last thing that happens when the worker fn returns its result is that
result being logged via the same logging mechanism, this should be fine.
but we might still want to switch the order around, so that a warning
for notification failure actually gets counted?

if it's such a common pattern to do job.start at the start, and
job.finish near/at the end of a worker task, I wonder whether we
couldn't handle that in a uniform fashion ;) then we might also not end
up calling worker.create_state twice (once for job.finish, and once for
worker.log_result as part of the worker exiting) with slightly different
states..




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-12 14:09   ` Lukas Wagner
@ 2024-04-17  8:22     ` Lukas Wagner
  2024-04-17 10:26       ` Gabriel Goller
  0 siblings, 1 reply; 47+ messages in thread
From: Lukas Wagner @ 2024-04-17  8:22 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Gabriel Goller

On  2024-04-12 16:09, Lukas Wagner wrote:
> On 12.04.24 15:59, Gabriel Goller wrote:
>> Another thing I noticed is that creating a datastore through the cli 
>> still uses the legacy notification system.
>>
> 
> Ah, thx!
> 

On second thought: I think I want to use the 'old system' by default for anything
that might be automated in some fashion (e.g. calling proxmox-backup-manager from a script, or
creating a datastore via the API).
Because a `proxmox-backup-manager datastore create ... --notify gc=never` 
(or an equivalent API call) would suddenly behave differently if we default to
the notification system (--notify is ignored then).
In the web UI it seems fine to automatically opt-in (we don't offer the option to change
notification settings there anyway), but for the CLI/API I'm not sure
if this is a good idea.

We *could* have some magic approach which opts into the notification system iff
  - mailto-user is *not* set (we defaulted to sending to root@pam then)
  - notify is also *not* set

But I'm not sure if I'm a fan of that - the 'auto' mode for backup jobs on PVE created
enough confusion already, so I'd rather be more explicit here - even if that
means that we have an opt-in approach until the next major release.


-- 
- Lukas




^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-17  8:22     ` Lukas Wagner
@ 2024-04-17 10:26       ` Gabriel Goller
  2024-04-17 10:26         ` Gabriel Goller
  0 siblings, 1 reply; 47+ messages in thread
From: Gabriel Goller @ 2024-04-17 10:26 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

On Wed Apr 17, 2024 at 10:22 AM CEST, Lukas Wagner wrote:
> On second thought: I think I want to use the 'old system' by default for anything
> that might be automated in some fashion (e.g. calling proxmox-backup-manager from a script, or
> creating a datastore via the API).

Ooh, this is actually a good point, this could then be changed on a
future (prob. major) update!
Disregard my comment earlier then :)

> Because a `proxmox-backup-manager datastore create ... --notify gc=never` 
> (or an equivalent API call) would suddenly behave differently if we default to
> the notification system (--notify is ignored then).
> In the web UI it seems fine to automatically opt-in (we don't offer the option to change
> notification settings there anyway), but for the CLI/API I'm not sure
> if this is a good idea.
>
> We *could* have some magic approach which opts into the notification system iff
>   - mailto-user is *not* set (we defaulted to sending to root@pam then)
>   - notify is also *not* set
>
> But I'm not sure if I'm a fan of that - the 'auto' mode for backup jobs on PVE created
> enough confusion already, so I'd rather be more explicit here - even if that
> means that we have an opt-in approach until the next major release.



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-17 10:26       ` Gabriel Goller
@ 2024-04-17 10:26         ` Gabriel Goller
  0 siblings, 0 replies; 47+ messages in thread
From: Gabriel Goller @ 2024-04-17 10:26 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

On Wed Apr 17, 2024 at 10:22 AM CEST, Lukas Wagner wrote:
> On second thought: I think I want to use the 'old system' by default for anything
> that might be automated in some fashion (e.g. calling proxmox-backup-manager from a script, or
> creating a datastore via the API).

Ooh, this is actually a good point, this could then be changed on a
future (prob. major) update!
Disregard my comment earlier then :)

> Because a `proxmox-backup-manager datastore create ... --notify gc=never` 
> (or an equivalent API call) would suddenly behave differently if we default to
> the notification system (--notify is ignored then).
> In the web UI it seems fine to automatically opt-in (we don't offer the option to change
> notification settings there anyway), but for the CLI/API I'm not sure
> if this is a good idea.
>
> We *could* have some magic approach which opts into the notification system iff
>   - mailto-user is *not* set (we defaulted to sending to root@pam then)
>   - notify is also *not* set
>
> But I'm not sure if I'm a fan of that - the 'auto' mode for backup jobs on PVE created
> enough confusion already, so I'd rather be more explicit here - even if that
> means that we have an opt-in approach until the next major release.





^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (33 preceding siblings ...)
  2024-04-12 13:59 ` [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Gabriel Goller
@ 2024-04-17 12:31 ` Gabriel Goller
  2024-04-17 14:38 ` Lukas Wagner
  35 siblings, 0 replies; 47+ messages in thread
From: Gabriel Goller @ 2024-04-17 12:31 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

LGTM!

You can consider:

Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system
  2024-04-17  7:46       ` Fabian Grünbichler
@ 2024-04-17 14:26         ` Lukas Wagner
  0 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-17 14:26 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion,
	Fabian Grünbichler, Gabriel Goller



On  2024-04-17 09:46, Fabian Grünbichler wrote:
> On April 16, 2024 2:13 pm, Lukas Wagner wrote:
>>
>>
>> On  2024-04-16 11:37, Gabriel Goller wrote:
>>> On Fri Apr 12, 2024 at 12:06 PM CEST, Lukas Wagner wrote:
>>>> diff --git a/src/server/gc_job.rs b/src/server/gc_job.rs
>>>> index 41375d72..ff5bdccf 100644
>>>> --- a/src/server/gc_job.rs
>>>> +++ b/src/server/gc_job.rs
>>>> @@ -19,8 +19,6 @@ pub fn do_garbage_collection_job(
>>>>  ) -> Result<String, Error> {
>>>>      let store = datastore.name().to_string();
>>>>  
>>>> -    let (email, notify) = crate::server::lookup_datastore_notify_settings(&store);
>>>> -
>>>>      let worker_type = job.jobtype().to_string();
>>>>      let upid_str = WorkerTask::new_thread(
>>>>          &worker_type,
>>>> @@ -43,11 +41,9 @@ pub fn do_garbage_collection_job(
>>>>                  eprintln!("could not finish job state for {}: {err}", job.jobtype());
>>>>              }
>>>>  
>>>> -            if let Some(email) = email {
>>>> -                let gc_status = datastore.last_gc_status();
>>>> -                if let Err(err) = send_gc_status(&email, notify, &store, &gc_status, &result) {
>>>> -                    eprintln!("send gc notification failed: {err}");
>>>> -                }
>>>> +            let gc_status = datastore.last_gc_status();
>>>> +            if let Err(err) = send_gc_status(&store, &gc_status, &result) {
>>>> +                eprintln!("send gc notification failed: {err}");
>>>
>>> I think we should use 'task_err!()' here. I know eprintln is used above,
>>> and technically works because we redirect stderr in the service setup 
>>> but it's still slow and kinda the legacy method of printing task errors.
>>
>> I think the reason why the original code does not use task_log is because the
>> job is already marked as finished at that point:
>>
>>             if let Err(err) = job.finish(status) {
>>                 eprintln!("could not finish job state for {}: {err}", job.jobtype());
>>             }
>>
>>             let gc_status = datastore.last_gc_status();
>>             if let Err(err) = send_gc_status(&store, &gc_status, &result) {
>>                 eprintln!("send gc notification failed: {err}");
>>             }
>>
>> Other jobs seem to follow the same pattern - use task_log! before, and eprintln/log::error!
>> after the job is finished.
>> A `task_log!` after the job is finished still seems to work, but I'm not sure if that
>> might lead to problems. What do you think?
> 
> I don't think this is a problem per se - job.finish() just marks the job as
> finished in the job state, it's not related to the worker task
> finish/exit itself.
> 
> since task_log (and friends) require the worker to still exist, and the
> last thing that happens when the worker fn returns its result is that
> result being logged via the same logging mechanism, this should be fine.
> but we might still want to switch the order around, so that a warning
> for notification failure actually gets counted?
> 
> if it's such a common pattern to do job.start at the start, and
> job.finish near/at the end of a worker task, I wonder whether we
> couldn't handle that in a uniform fashion ;) then we might also not end
> up calling worker.create_state twice (once for job.finish, and once for
> worker.log_result as part of the worker exiting) with slightly different
> states..
> 

Thanks a lot for your input!

I think I'll do the changes in a followup (if only to replace the eprintln!) - since
Gabriel's proxmox_log/tracing series is ripping out the task_log! macro any way.


-- 
- Lukas


_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system
  2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
                   ` (34 preceding siblings ...)
  2024-04-17 12:31 ` Gabriel Goller
@ 2024-04-17 14:38 ` Lukas Wagner
  35 siblings, 0 replies; 47+ messages in thread
From: Lukas Wagner @ 2024-04-17 14:38 UTC (permalink / raw)
  To: pbs-devel

Superseded-by: https://lists.proxmox.com/pipermail/pbs-devel/2024-April/008639.html

On  2024-04-12 12:05, Lukas Wagner wrote:
> These patches integrate the notification system which was introduced in Proxmox VE
> in 8.1 into Proxmox Backup Server.
> 
> Some highlights/noteworthy details from the series:
>   - notification template files are installed in /usr/share/proxmox-backup/templates/default
>     and are rendered when a notification is sent
>   - since sending notifications needs to be done from a privileged context
>     (to read protected passwords/tokens from the priv config file),
>     we queue notifications to be sent in /var/lib/proxmox-backup/notifications
>     and periodically send any queued notifications via a worker in the
>     privileged process
>   - The API endpoint paths are prefixed with /config/notifications
>   - API endpoints which read/modify notification system config require
>     Sys.Audit (read) or Sys.Modify (modify) permissions on /system/notifications
> 
>   - tape-{backup,restore} settings and datastore options now have a
>     'notification-mode' parameter, which allows to choose between the
>     'legacy' behavior (sendmail to a selected user's email address) and
>     the event-based notification system. If the parameter is not set, we
>     default to the legacy behavior in order to keep existing behavior
>     as is for now. For new datastores/tape backup jobs created
>     interactively from teh UI, we set the parameter to 'notification-system'
>     and opt in into the new system by default.
> 
> Still missing/rough edges:
>   - documentation (will follow soon)
>   - Datastore option view in UI could be improved. When 'notification-mode' is
>     set to 'notification-system', we should indicate that 'mailto-user' and the
>     other notification settings have no effect. Already did that in the edit
>     window, but in the grid panel I did not find a quick way to do that.
> 
>   - still needs a bit more testing (e.g. ACME notifications) - would greatly
>     appreciate people helping here!
> 
> Open questions:
>   - Right now the 'default-matcher' matches every notification, same as in PVE.
>     Up until now, datastore prune notifications were only sent on errors, so
>     maybe we should also have the default-matcher work the same?
>     The default matcher would then probably be:
>       - !All
>           - severity: error
>           - match-field: exact:type=prune
> 
> This patch series requires the patches for 'proxmox' from
> https://lists.proxmox.com/pipermail/pve-devel/2024-April/062708.html
> to be applied.
> 
> The dependency for proxmox-widget-toolkit should be bumped to at least
> 4.1.4 (we need: "utils: add mechanism to add and override translatable notification event
> descriptions in the product specific UIs")
> Also, while not strictly needed, the patches for widget-toolkit from
> https://lists.proxmox.com/pipermail/pve-devel/2024-February/061992.html
> make creating matchers much easier - the required API endpoints were
> already implemented by this series.
> 
> proxmox-backup:
> 
> Lukas Wagner (33):
>   pbs-config: add module for loading notification config
>   server: rename email_notifications module to notifications
>   notifications: allow sending notifications via proxmox_notify
>   buildsys: install templates for test notifications
>   pbs-config: acl: add /system/notifications as known ACL path
>   api: add endpoints for querying/testing notification targets
>   api: add endpoints for notification matchers
>   api: add endpoints for sendmail targets
>   api: add endpoints for smtp targets
>   api: add endpoints for gotify targets
>   api: add endpoints for querying known notification values/fields
>   api-types: api: datatore: add notification-mode parameter
>   api-types: api: tape: add notification-mode parameter
>   server: notifications: send GC notifications via notification system
>   server: notifications: send prune notifications via notification
>     system
>   server: notifications: send verify notifications via notification
>     system
>   server: notifications: send sync notifications via notification system
>   server: notifications: send update notifications via notification
>     system
>   server: notifications: send acme notifications via notification system
>   server: notifications: send tape notifications via notification system
>   ui: add notification config panel
>   ui: tape backup job: add selector for notification-mode
>   ui: tape backup: add selector for 'notification-mode'
>   ui: tape restore: add 'notification-mode' parameter
>   ui: datastore options: add 'notification-mode' parameter
>   ui: utils: add overrides for known notification metadata fields/values
>   ui: datastore edit: make new stores use notification system by default
>   ui: permissions paths: add /system/notifications to combobox
>   proxmox-backup-manager: add CLI for notification targets
>   proxmox-backup-manager: add CLI for notification matchers
>   proxmox-backup-manager: add CLI for gotify endpoints
>   proxmox-backup-manager: add CLI for sendmail endpoints
>   proxmox-backup-manager: add CLI for SMTP endpoints
> 
>  Cargo.toml                                    |   3 +
>  Makefile                                      |   5 +-
>  debian/proxmox-backup-server.install          |  29 +
>  pbs-api-types/src/datastore.rs                |  22 +
>  pbs-api-types/src/jobs.rs                     |   8 +-
>  pbs-config/Cargo.toml                         |   1 +
>  pbs-config/src/acl.rs                         |   3 +-
>  pbs-config/src/lib.rs                         |   1 +
>  pbs-config/src/notifications.rs               |  41 +
>  src/api2/config/datastore.rs                  |   9 +
>  src/api2/config/mod.rs                        |   2 +
>  src/api2/config/notifications/gotify.rs       | 190 +++++
>  src/api2/config/notifications/matchers.rs     | 170 ++++
>  src/api2/config/notifications/mod.rs          | 175 ++++
>  src/api2/config/notifications/sendmail.rs     | 178 ++++
>  src/api2/config/notifications/smtp.rs         | 191 +++++
>  src/api2/config/notifications/targets.rs      |  63 ++
>  src/api2/config/tape_backup_job.rs            |   8 +
>  src/api2/pull.rs                              |  10 +-
>  src/api2/tape/backup.rs                       |  62 +-
>  src/api2/tape/restore.rs                      |  46 +-
>  src/bin/proxmox-backup-api.rs                 |  11 +
>  src/bin/proxmox-backup-manager.rs             |   2 +
>  src/bin/proxmox-backup-proxy.rs               |   1 +
>  src/bin/proxmox_backup_manager/mod.rs         |   2 +
>  .../notifications/gotify.rs                   |  93 +++
>  .../notifications/matchers.rs                 |  93 +++
>  .../notifications/mod.rs                      |  21 +
>  .../notifications/sendmail.rs                 |  94 +++
>  .../notifications/smtp.rs                     |  96 +++
>  .../notifications/targets.rs                  |  51 ++
>  src/server/email_notifications.rs             | 763 ------------------
>  src/server/gc_job.rs                          |  10 +-
>  src/server/mod.rs                             |   4 +-
>  src/server/notifications.rs                   | 544 +++++++++++++
>  src/server/verify_job.rs                      |  10 +-
>  src/tape/drive/mod.rs                         |  22 +-
>  src/tape/mod.rs                               |  27 +
>  src/tape/pool_writer/mod.rs                   |  11 +-
>  templates/Makefile                            |  41 +
>  templates/default/acme-err-body.txt.hbs       |   7 +
>  templates/default/acme-err-subject.txt.hbs    |   1 +
>  templates/default/gc-err-body.txt.hbs         |   8 +
>  templates/default/gc-err-subject.txt.hbs      |   1 +
>  templates/default/gc-ok-body.txt.hbs          |  23 +
>  templates/default/gc-ok-subject.txt.hbs       |   1 +
>  .../default/package-updates-body.txt.hbs      |   8 +
>  .../default/package-updates-subject.txt.hbs   |   1 +
>  templates/default/prune-err-body.txt.hbs      |  10 +
>  templates/default/prune-err-subject.txt.hbs   |   1 +
>  templates/default/prune-ok-body.txt.hbs       |  10 +
>  templates/default/prune-ok-subject.txt.hbs    |   1 +
>  templates/default/sync-err-body.txt.hbs       |  14 +
>  templates/default/sync-err-subject.txt.hbs    |   5 +
>  templates/default/sync-ok-body.txt.hbs        |  14 +
>  templates/default/sync-ok-subject.txt.hbs     |   5 +
>  .../default/tape-backup-err-body.txt.hbs      |  26 +
>  .../default/tape-backup-err-subject.txt.hbs   |   5 +
>  templates/default/tape-backup-ok-body.txt.hbs |  27 +
>  .../default/tape-backup-ok-subject.txt.hbs    |   5 +
>  templates/default/tape-load-body.txt.hbs      |  15 +
>  templates/default/tape-load-subject.txt.hbs   |   1 +
>  templates/default/test-body.html.hbs          |   1 +
>  templates/default/test-body.txt.hbs           |   1 +
>  templates/default/test-subject.txt.hbs        |   1 +
>  templates/default/verify-err-body.txt.hbs     |  14 +
>  templates/default/verify-err-subject.txt.hbs  |   1 +
>  templates/default/verify-ok-body.txt.hbs      |  10 +
>  templates/default/verify-ok-subject.txt.hbs   |   1 +
>  www/Makefile                                  |   1 +
>  www/NavigationTree.js                         |   6 +
>  www/Utils.js                                  |  16 +
>  www/config/NotificationConfigView.js          |  11 +
>  www/datastore/OptionView.js                   |  15 +
>  www/form/PermissionPathSelector.js            |   1 +
>  www/tape/window/TapeBackup.js                 |  25 +
>  www/tape/window/TapeBackupJob.js              |  24 +
>  www/tape/window/TapeRestore.js                |  17 +
>  www/window/DataStoreEdit.js                   |  13 +
>  www/window/NotifyOptions.js                   |  38 +
>  80 files changed, 2630 insertions(+), 868 deletions(-)
>  create mode 100644 pbs-config/src/notifications.rs
>  create mode 100644 src/api2/config/notifications/gotify.rs
>  create mode 100644 src/api2/config/notifications/matchers.rs
>  create mode 100644 src/api2/config/notifications/mod.rs
>  create mode 100644 src/api2/config/notifications/sendmail.rs
>  create mode 100644 src/api2/config/notifications/smtp.rs
>  create mode 100644 src/api2/config/notifications/targets.rs
>  create mode 100644 src/bin/proxmox_backup_manager/notifications/gotify.rs
>  create mode 100644 src/bin/proxmox_backup_manager/notifications/matchers.rs
>  create mode 100644 src/bin/proxmox_backup_manager/notifications/mod.rs
>  create mode 100644 src/bin/proxmox_backup_manager/notifications/sendmail.rs
>  create mode 100644 src/bin/proxmox_backup_manager/notifications/smtp.rs
>  create mode 100644 src/bin/proxmox_backup_manager/notifications/targets.rs
>  delete mode 100644 src/server/email_notifications.rs
>  create mode 100644 src/server/notifications.rs
>  create mode 100644 templates/Makefile
>  create mode 100644 templates/default/acme-err-body.txt.hbs
>  create mode 100644 templates/default/acme-err-subject.txt.hbs
>  create mode 100644 templates/default/gc-err-body.txt.hbs
>  create mode 100644 templates/default/gc-err-subject.txt.hbs
>  create mode 100644 templates/default/gc-ok-body.txt.hbs
>  create mode 100644 templates/default/gc-ok-subject.txt.hbs
>  create mode 100644 templates/default/package-updates-body.txt.hbs
>  create mode 100644 templates/default/package-updates-subject.txt.hbs
>  create mode 100644 templates/default/prune-err-body.txt.hbs
>  create mode 100644 templates/default/prune-err-subject.txt.hbs
>  create mode 100644 templates/default/prune-ok-body.txt.hbs
>  create mode 100644 templates/default/prune-ok-subject.txt.hbs
>  create mode 100644 templates/default/sync-err-body.txt.hbs
>  create mode 100644 templates/default/sync-err-subject.txt.hbs
>  create mode 100644 templates/default/sync-ok-body.txt.hbs
>  create mode 100644 templates/default/sync-ok-subject.txt.hbs
>  create mode 100644 templates/default/tape-backup-err-body.txt.hbs
>  create mode 100644 templates/default/tape-backup-err-subject.txt.hbs
>  create mode 100644 templates/default/tape-backup-ok-body.txt.hbs
>  create mode 100644 templates/default/tape-backup-ok-subject.txt.hbs
>  create mode 100644 templates/default/tape-load-body.txt.hbs
>  create mode 100644 templates/default/tape-load-subject.txt.hbs
>  create mode 100644 templates/default/test-body.html.hbs
>  create mode 100644 templates/default/test-body.txt.hbs
>  create mode 100644 templates/default/test-subject.txt.hbs
>  create mode 100644 templates/default/verify-err-body.txt.hbs
>  create mode 100644 templates/default/verify-err-subject.txt.hbs
>  create mode 100644 templates/default/verify-ok-body.txt.hbs
>  create mode 100644 templates/default/verify-ok-subject.txt.hbs
>  create mode 100644 www/config/NotificationConfigView.js
> 
> 
> Summary over all repositories:
>   80 files changed, 2630 insertions(+), 868 deletions(-)
> 

-- 
- Lukas


_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


^ permalink raw reply	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2024-04-17 14:38 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-12 10:05 [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Lukas Wagner
2024-04-12 10:05 ` [pbs-devel] [PATCH proxmox-backup 01/33] pbs-config: add module for loading notification config Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 02/33] server: rename email_notifications module to notifications Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 03/33] notifications: allow sending notifications via proxmox_notify Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 04/33] buildsys: install templates for test notifications Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 05/33] pbs-config: acl: add /system/notifications as known ACL path Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 06/33] api: add endpoints for querying/testing notification targets Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 07/33] api: add endpoints for notification matchers Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 08/33] api: add endpoints for sendmail targets Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 09/33] api: add endpoints for smtp targets Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 10/33] api: add endpoints for gotify targets Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 11/33] api: add endpoints for querying known notification values/fields Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 12/33] api-types: api: datatore: add notification-mode parameter Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 13/33] api-types: api: tape: " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 14/33] server: notifications: send GC notifications via notification system Lukas Wagner
2024-04-15  9:41   ` Gabriel Goller
2024-04-15 14:10     ` Lukas Wagner
2024-04-16  9:37   ` Gabriel Goller
2024-04-16 12:13     ` Lukas Wagner
2024-04-17  7:46       ` Fabian Grünbichler
2024-04-17 14:26         ` Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 15/33] server: notifications: send prune " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 16/33] server: notifications: send verify " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 17/33] server: notifications: send sync " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 18/33] server: notifications: send update " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 19/33] server: notifications: send acme " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 20/33] server: notifications: send tape " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 21/33] ui: add notification config panel Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 22/33] ui: tape backup job: add selector for notification-mode Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 23/33] ui: tape backup: add selector for 'notification-mode' Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 24/33] ui: tape restore: add 'notification-mode' parameter Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 25/33] ui: datastore options: " Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 26/33] ui: utils: add overrides for known notification metadata fields/values Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 27/33] ui: datastore edit: make new stores use notification system by default Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 28/33] ui: permissions paths: add /system/notifications to combobox Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 29/33] proxmox-backup-manager: add CLI for notification targets Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 30/33] proxmox-backup-manager: add CLI for notification matchers Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 31/33] proxmox-backup-manager: add CLI for gotify endpoints Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 32/33] proxmox-backup-manager: add CLI for sendmail endpoints Lukas Wagner
2024-04-12 10:06 ` [pbs-devel] [PATCH proxmox-backup 33/33] proxmox-backup-manager: add CLI for SMTP endpoints Lukas Wagner
2024-04-12 13:59 ` [pbs-devel] [PATCH proxmox-backup 00/33] integrate notification system Gabriel Goller
2024-04-12 14:09   ` Lukas Wagner
2024-04-17  8:22     ` Lukas Wagner
2024-04-17 10:26       ` Gabriel Goller
2024-04-17 10:26         ` Gabriel Goller
2024-04-17 12:31 ` Gabriel Goller
2024-04-17 14:38 ` Lukas Wagner

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