public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH many v4 00/43] integrate notification system
@ 2024-04-22 12:37 Lukas Wagner
  2024-04-22 12:37 ` [pbs-devel] [PATCH proxmox v4 01/43] notify: expose `config` module Lukas Wagner
                   ` (44 more replies)
  0 siblings, 45 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:37 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/test endpoint) 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 the UI, we set the parameter to 'notification-system'
    and opt in into the new system by default. For the CLI/API we don't
    so that we don't cause problems with any automations/scripts -
    there I'd change the default to the new system with the next major release

Rough edges:
  - 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.

Prerequisites:

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

  - 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")

Changes since v3:
  - Pull in UI changes from 'notification metadata matching improvments' as
    component overrides - this can and should be removed once the
    widget toolkit patches are applied.
  - no other changes apart from that

Changes since v2:
  - consolidate verify/prune/sync/tape-job to job-id
  - add a note to docs that package update notifications
    are only sent by nodes with an active subscription

Changes since v1:
  - proxmox_notify: Change default-matcher to exclude 'prune success' notifications
  - proxmox_notify: Move sendmail/forward functions from proxmox_sys
  - proxmox_notify: Improve docs for API types
  - proxmox-widget-toolkit: allow to override default mail author
  - Add documentation, man pages, etc. (mostly copied from PVE, some
    stylistic touchups, some rephrasing)
  - Add additional matchable metadata fields, allowing to match on
    individual jobs:
    - verification-job
    - prune-job
    - sync-job
    - tape-job
  - Sort queued notifications by timestamp before sending
  - Fix missing hostname in package-update notification
  - Change default mail author in the UI from Proxmox VE to Proxmox Backup Server ($hostname)

proxmox:

Lukas Wagner (5):
  notify: expose `config` module
  notify: use std::sync::OnceCell instead of lazy_static!
  notify: pbs-context: exclude successful prunes in default matcher
  notify: endpoints: matcher: improve descriptions for API types
  notify: add getter for notification timestamp

 proxmox-notify/Cargo.toml                |  1 -
 proxmox-notify/src/config.rs             | 23 ++++++++++++-------
 proxmox-notify/src/context/pbs.rs        |  5 ++++-
 proxmox-notify/src/endpoints/gotify.rs   |  6 ++---
 proxmox-notify/src/endpoints/sendmail.rs | 15 ++++++++-----
 proxmox-notify/src/endpoints/smtp.rs     | 28 +++++++++++++++---------
 proxmox-notify/src/lib.rs                | 11 ++++++----
 proxmox-notify/src/matcher.rs            | 18 +++++++--------
 8 files changed, 66 insertions(+), 41 deletions(-)


proxmox-widget-toolkit:

Lukas Wagner (1):
  sendmail: smtp: allow to overide default mail author

 src/Schema.js                  | 9 +++++++++
 src/panel/SendmailEditPanel.js | 2 +-
 src/panel/SmtpEditPanel.js     | 2 +-
 src/window/EndpointEditBase.js | 1 +
 4 files changed, 12 insertions(+), 2 deletions(-)


proxmox-backup:

Lukas Wagner (37):
  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
  docgen: generate synopsis for notifications{-priv,}.cfg
  docs: add documentation for notification system
  ui: util: override default mail author for sendmail/smtp targets
  ui: notifications: pull in UX improvements for match rules creation

 Cargo.toml                                    |    3 +
 Makefile                                      |    5 +-
 debian/proxmox-backup-server.install          |   31 +
 docs/Makefile                                 |    6 +-
 docs/conf.py                                  |    2 +
 docs/config/notifications-priv/format.rst     |    1 +
 docs/config/notifications-priv/man5.rst       |   24 +
 docs/config/notifications/format.rst          |   28 +
 docs/config/notifications/man5.rst            |   24 +
 docs/configuration-files.rst                  |   30 +
 docs/index.rst                                |    1 +
 docs/notifications.rst                        |  213 ++++
 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          |  210 ++++
 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/docgen.rs                             |    4 +
 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                   |  566 +++++++++
 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                                  |    2 +
 www/NavigationTree.js                         |    6 +
 www/OnlineHelpInfo.js                         |   24 +
 www/Utils.js                                  |   39 +
 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/NotificationMatcherOverride.js     | 1105 +++++++++++++++++
 www/window/NotifyOptions.js                   |   38 +
 92 files changed, 4174 insertions(+), 869 deletions(-)
 create mode 100644 docs/config/notifications-priv/format.rst
 create mode 100644 docs/config/notifications-priv/man5.rst
 create mode 100644 docs/config/notifications/format.rst
 create mode 100644 docs/config/notifications/man5.rst
 create mode 100644 docs/notifications.rst
 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
 create mode 100644 www/window/NotificationMatcherOverride.js


Summary over all repositories:
  104 files changed, 4252 insertions(+), 912 deletions(-)

-- 
Generated by git-murpp 0.7.1


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


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

* [pbs-devel] [PATCH proxmox v4 01/43] notify: expose `config` module
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
@ 2024-04-22 12:37 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 02/43] notify: use std::sync::OnceCell instead of lazy_static! Lukas Wagner
                   ` (43 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:37 UTC (permalink / raw)
  To: pbs-devel

This is needed because we want to access CONFIG and PRIVATE_CONFIG
from the docgen helper in PBS.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-notify/src/lib.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/proxmox-notify/src/lib.rs b/proxmox-notify/src/lib.rs
index 292396b..f9917d9 100644
--- a/proxmox-notify/src/lib.rs
+++ b/proxmox-notify/src/lib.rs
@@ -18,14 +18,13 @@ use matcher::{MatcherConfig, MATCHER_TYPENAME};
 
 pub mod api;
 pub mod context;
+pub mod config;
 pub mod endpoints;
 pub mod filter;
 pub mod group;
 pub mod renderer;
 pub mod schema;
 
-mod config;
-
 #[derive(Debug)]
 pub enum Error {
     /// There was an error serializing the config
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox v4 02/43] notify: use std::sync::OnceCell instead of lazy_static!
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
  2024-04-22 12:37 ` [pbs-devel] [PATCH proxmox v4 01/43] notify: expose `config` module Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 03/43] notify: pbs-context: exclude successful prunes in default matcher Lukas Wagner
                   ` (42 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-notify/Cargo.toml    |  1 -
 proxmox-notify/src/config.rs | 23 +++++++++++++++--------
 proxmox-notify/src/lib.rs    |  5 ++---
 3 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/proxmox-notify/Cargo.toml b/proxmox-notify/Cargo.toml
index 797b1ac..3a97847 100644
--- a/proxmox-notify/Cargo.toml
+++ b/proxmox-notify/Cargo.toml
@@ -11,7 +11,6 @@ exclude.workspace = true
 anyhow.workspace = true
 const_format.workspace = true
 handlebars = { workspace = true }
-lazy_static.workspace = true
 lettre = { workspace = true, optional = true }
 log.workspace = true
 mail-parser = { workspace = true, optional = true }
diff --git a/proxmox-notify/src/config.rs b/proxmox-notify/src/config.rs
index fe25ea7..4445371 100644
--- a/proxmox-notify/src/config.rs
+++ b/proxmox-notify/src/config.rs
@@ -1,4 +1,4 @@
-use lazy_static::lazy_static;
+use std::sync::OnceLock;
 
 use proxmox_schema::{ApiType, ObjectSchema};
 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
@@ -9,9 +9,16 @@ use crate::matcher::{MatcherConfig, MATCHER_TYPENAME};
 use crate::schema::BACKEND_NAME_SCHEMA;
 use crate::Error;
 
-lazy_static! {
-    pub static ref CONFIG: SectionConfig = config_init();
-    pub static ref PRIVATE_CONFIG: SectionConfig = private_config_init();
+/// Section config schema for the public config file.
+pub fn config_parser() -> &'static SectionConfig {
+    static CONFIG: OnceLock<SectionConfig> = OnceLock::new();
+    CONFIG.get_or_init(|| config_init())
+}
+
+/// Section config schema for the private config file.
+pub fn private_config_parser() -> &'static SectionConfig {
+    static CONFIG: OnceLock<SectionConfig> = OnceLock::new();
+    CONFIG.get_or_init(|| private_config_init())
 }
 
 fn config_init() -> SectionConfig {
@@ -108,7 +115,7 @@ fn private_config_init() -> SectionConfig {
 
 pub fn config(raw_config: &str) -> Result<(SectionConfigData, [u8; 32]), Error> {
     let digest = openssl::sha::sha256(raw_config.as_bytes());
-    let mut data = CONFIG
+    let mut data = config_parser()
         .parse("notifications.cfg", raw_config)
         .map_err(|err| Error::ConfigDeserialization(err.into()))?;
 
@@ -139,20 +146,20 @@ pub fn config(raw_config: &str) -> Result<(SectionConfigData, [u8; 32]), Error>
 
 pub fn private_config(raw_config: &str) -> Result<(SectionConfigData, [u8; 32]), Error> {
     let digest = openssl::sha::sha256(raw_config.as_bytes());
-    let data = PRIVATE_CONFIG
+    let data = private_config_parser()
         .parse("priv/notifications.cfg", raw_config)
         .map_err(|err| Error::ConfigDeserialization(err.into()))?;
     Ok((data, digest))
 }
 
 pub fn write(config: &SectionConfigData) -> Result<String, Error> {
-    CONFIG
+    config_parser()
         .write("notifications.cfg", config)
         .map_err(|err| Error::ConfigSerialization(err.into()))
 }
 
 pub fn write_private(config: &SectionConfigData) -> Result<String, Error> {
-    PRIVATE_CONFIG
+    private_config_parser()
         .write("priv/notifications.cfg", config)
         .map_err(|err| Error::ConfigSerialization(err.into()))
 }
diff --git a/proxmox-notify/src/lib.rs b/proxmox-notify/src/lib.rs
index f9917d9..c69ac26 100644
--- a/proxmox-notify/src/lib.rs
+++ b/proxmox-notify/src/lib.rs
@@ -13,12 +13,11 @@ use proxmox_section_config::SectionConfigData;
 use proxmox_uuid::Uuid;
 
 pub mod matcher;
-use crate::config::CONFIG;
 use matcher::{MatcherConfig, MATCHER_TYPENAME};
 
 pub mod api;
-pub mod context;
 pub mod config;
+pub mod context;
 pub mod endpoints;
 pub mod filter;
 pub mod group;
@@ -280,7 +279,7 @@ impl Config {
 
         let default_config = context().default_config();
 
-        let builtin_config = CONFIG
+        let builtin_config = config::config_parser()
             .parse("<builtin>", default_config)
             .map_err(|err| Error::ConfigDeserialization(err.into()))?;
 
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox v4 03/43] notify: pbs-context: exclude successful prunes in default matcher
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
  2024-04-22 12:37 ` [pbs-devel] [PATCH proxmox v4 01/43] notify: expose `config` module Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 02/43] notify: use std::sync::OnceCell instead of lazy_static! Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 04/43] notify: endpoints: matcher: improve descriptions for API types Lukas Wagner
                   ` (41 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

PBS sends notifications for all events but successful prune jobs.
There we only care about errors.

This commit adapts the 'default-matcher' to reflect that behavior
as well.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-notify/src/context/pbs.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/proxmox-notify/src/context/pbs.rs b/proxmox-notify/src/context/pbs.rs
index 299f685..2de305f 100644
--- a/proxmox-notify/src/context/pbs.rs
+++ b/proxmox-notify/src/context/pbs.rs
@@ -66,8 +66,11 @@ sendmail: mail-to-root
 
 matcher: default-matcher
     mode all
+    invert-match true
+    match-field exact:type=prune
+    match-severity info
     target mail-to-root
-    comment Route all notifications to mail-to-root
+    comment Route everything but successful prune job notifications to mail-to-root
 ";
 
 #[derive(Debug)]
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox v4 04/43] notify: endpoints: matcher: improve descriptions for API types
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (2 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 03/43] notify: pbs-context: exclude successful prunes in default matcher Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 05/43] notify: add getter for notification timestamp Lukas Wagner
                   ` (40 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

proxmox-schema will automatically append text (e.g. 'Can be specified
more than once'), so we should end every comment with a '.'.

Also copy over some text from PVE docs, since these doc comments will
now be visible in the PBS documentation.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-notify/src/endpoints/gotify.rs   |  6 ++---
 proxmox-notify/src/endpoints/sendmail.rs | 15 ++++++++-----
 proxmox-notify/src/endpoints/smtp.rs     | 28 +++++++++++++++---------
 proxmox-notify/src/matcher.rs            | 18 +++++++--------
 4 files changed, 40 insertions(+), 27 deletions(-)

diff --git a/proxmox-notify/src/endpoints/gotify.rs b/proxmox-notify/src/endpoints/gotify.rs
index ee8ca51..30ee76a 100644
--- a/proxmox-notify/src/endpoints/gotify.rs
+++ b/proxmox-notify/src/endpoints/gotify.rs
@@ -40,12 +40,12 @@ pub(crate) const GOTIFY_TYPENAME: &str = "gotify";
 #[serde(rename_all = "kebab-case")]
 /// Config for  Gotify notification endpoints
 pub struct GotifyConfig {
-    /// Name of the endpoint
+    /// Name of the endpoint.
     #[updater(skip)]
     pub name: String,
-    /// Gotify Server URL
+    /// Gotify Server URL.
     pub server: String,
-    /// Comment
+    /// Comment.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub comment: Option<String>,
     /// Deprecated.
diff --git a/proxmox-notify/src/endpoints/sendmail.rs b/proxmox-notify/src/endpoints/sendmail.rs
index 47901ef..da0c0cc 100644
--- a/proxmox-notify/src/endpoints/sendmail.rs
+++ b/proxmox-notify/src/endpoints/sendmail.rs
@@ -43,21 +43,26 @@ pub struct SendmailConfig {
     /// Name of the endpoint
     #[updater(skip)]
     pub name: String,
-    /// Mail recipients
+    /// Mail address to send a mail to.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub mailto: Vec<String>,
-    /// Mail recipients
+    /// Users to send a mail to. The email address of the user
+    /// will be looked up in users.cfg.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub mailto_user: Vec<String>,
-    /// `From` address for the mail
+    /// `From` address for sent E-Mails.
+    /// If the parameter is not set, the plugin will fall back to the
+    /// email-from setting from node.cfg (PBS).
+    /// If that is also not set, the plugin will default to root@$hostname,
+    /// where $hostname is the hostname of the node.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub from_address: Option<String>,
-    /// Author of the mail
+    /// Author of the mail. Defaults to 'Proxmox Backup Server ($hostname)'
     #[serde(skip_serializing_if = "Option::is_none")]
     pub author: Option<String>,
-    /// Comment
+    /// Comment.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub comment: Option<String>,
     /// Deprecated.
diff --git a/proxmox-notify/src/endpoints/smtp.rs b/proxmox-notify/src/endpoints/smtp.rs
index f04583a..27afeba 100644
--- a/proxmox-notify/src/endpoints/smtp.rs
+++ b/proxmox-notify/src/endpoints/smtp.rs
@@ -66,33 +66,41 @@ pub enum SmtpMode {
 #[serde(rename_all = "kebab-case")]
 /// Config for Sendmail notification endpoints
 pub struct SmtpConfig {
-    /// Name of the endpoint
+    /// Name of the endpoint.
     #[updater(skip)]
     pub name: String,
-    /// Host name or IP of the SMTP relay
+    /// Host name or IP of the SMTP relay.
     pub server: String,
-    /// Port to use when connecting to the SMTP relay
+    /// The port to connect to.
+    /// If not set, the used port defaults to 25 (insecure), 465 (tls)
+    /// or 587 (starttls), depending on the value of mode
     #[serde(skip_serializing_if = "Option::is_none")]
     pub port: Option<u16>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub mode: Option<SmtpMode>,
-    /// Username for authentication
+    /// Username to use during authentication.
+    /// If no username is set, no authentication will be performed.
+    /// The PLAIN and LOGIN authentication methods are supported
     #[serde(skip_serializing_if = "Option::is_none")]
     pub username: Option<String>,
-    /// Mail recipients
+    /// Mail address to send a mail to.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub mailto: Vec<String>,
-    /// Mail recipients
+    /// Users to send a mail to. The email address of the user
+    /// will be looked up in users.cfg.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub mailto_user: Vec<String>,
-    /// `From` address for the mail
+    /// `From` address for the mail.
+    /// SMTP relays might require that this address is owned by the user
+    /// in order to avoid spoofing. The `From` header in the email will be
+    /// set to `$author <$from-address>`.
     pub from_address: String,
-    /// Author of the mail
+    /// Author of the mail. Defaults to 'Proxmox Backup Server ($hostname)'
     #[serde(skip_serializing_if = "Option::is_none")]
     pub author: Option<String>,
-    /// Comment
+    /// Comment.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub comment: Option<String>,
     /// Disable this target.
@@ -136,7 +144,7 @@ pub struct SmtpPrivateConfig {
     /// Name of the endpoint
     #[updater(skip)]
     pub name: String,
-    /// Authentication token
+    /// The password to use during authentication.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub password: Option<String>,
 }
diff --git a/proxmox-notify/src/matcher.rs b/proxmox-notify/src/matcher.rs
index 2d30378..986deee 100644
--- a/proxmox-notify/src/matcher.rs
+++ b/proxmox-notify/src/matcher.rs
@@ -108,42 +108,42 @@ pub const MATCH_FIELD_ENTRY_SCHEMA: Schema = StringSchema::new("Match metadata f
 #[serde(rename_all = "kebab-case")]
 /// Config for Sendmail notification endpoints
 pub struct MatcherConfig {
-    /// Name of the matcher
+    /// Name of the matcher.
     #[updater(skip)]
     pub name: String,
 
-    /// List of matched metadata fields
+    /// List of matched metadata fields.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub match_field: Vec<FieldMatcher>,
 
-    /// List of matched severity levels
+    /// List of matched severity levels.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub match_severity: Vec<SeverityMatcher>,
 
-    /// List of matched severity levels
+    /// List of matched severity levels.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub match_calendar: Vec<CalendarMatcher>,
-    /// Decide if 'all' or 'any' match statements must match
+    /// Decide if 'all' or 'any' match statements must match.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub mode: Option<MatchModeOperator>,
 
-    /// Invert match of the whole filter
+    /// Invert match of the whole filter.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub invert_match: Option<bool>,
 
-    /// Targets to notify
+    /// Targets to notify.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     #[updater(serde(skip_serializing_if = "Option::is_none"))]
     pub target: Vec<String>,
 
-    /// Comment
+    /// Comment.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub comment: Option<String>,
 
-    /// Disable this matcher
+    /// Disable this matcher.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub disable: Option<bool>,
 
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox v4 05/43] notify: add getter for notification timestamp
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (3 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 04/43] notify: endpoints: matcher: improve descriptions for API types Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH widget-toolkit v4 06/43] sendmail: smtp: allow to overide default mail author Lukas Wagner
                   ` (39 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-notify/src/lib.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/proxmox-notify/src/lib.rs b/proxmox-notify/src/lib.rs
index c69ac26..53f897a 100644
--- a/proxmox-notify/src/lib.rs
+++ b/proxmox-notify/src/lib.rs
@@ -261,6 +261,11 @@ impl Notification {
     pub fn id(&self) -> &Uuid {
         &self.id
     }
+
+    /// Return the notification's timestamp
+    pub fn timestamp(&self) -> i64 {
+        self.metadata.timestamp
+    }
 }
 
 /// Notification configuration
-- 
2.39.2



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


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

* [pbs-devel] [PATCH widget-toolkit v4 06/43] sendmail: smtp: allow to overide default mail author
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (4 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 05/43] notify: add getter for notification timestamp Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 07/43] pbs-config: add module for loading notification config Lukas Wagner
                   ` (38 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

In PBS, we obviously don't have "Proxmox VE" as a default sender, so
we need a mechanism to change the default author.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/Schema.js                  | 9 +++++++++
 src/panel/SendmailEditPanel.js | 2 +-
 src/panel/SmtpEditPanel.js     | 2 +-
 src/window/EndpointEditBase.js | 1 +
 4 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/Schema.js b/src/Schema.js
index 7833fc0..42541e0 100644
--- a/src/Schema.js
+++ b/src/Schema.js
@@ -52,11 +52,13 @@ Ext.define('Proxmox.Schema', { // a singleton
 	    name: 'Sendmail',
 	    ipanel: 'pmxSendmailEditPanel',
 	    iconCls: 'fa-envelope-o',
+	    defaultMailAuthor: 'Proxmox VE',
 	},
 	smtp: {
 	    name: 'SMTP',
 	    ipanel: 'pmxSmtpEditPanel',
 	    iconCls: 'fa-envelope-o',
+	    defaultMailAuthor: 'Proxmox VE',
 	},
 	gotify: {
 	    name: 'Gotify',
@@ -65,6 +67,13 @@ Ext.define('Proxmox.Schema', { // a singleton
 	},
     },
 
+    // to add or change existing for product specific ones
+    overrideEndpointTypes: function(extra) {
+	for (const [key, value] of Object.entries(extra)) {
+	    Proxmox.Schema.notificationEndpointTypes[key] = value;
+	}
+    },
+
     pxarFileTypes: {
 	b: { icon: 'cube', label: gettext('Block Device') },
 	c: { icon: 'tty', label: gettext('Character Device') },
diff --git a/src/panel/SendmailEditPanel.js b/src/panel/SendmailEditPanel.js
index 5a154c1..ccdfe98 100644
--- a/src/panel/SendmailEditPanel.js
+++ b/src/panel/SendmailEditPanel.js
@@ -58,8 +58,8 @@ Ext.define('Proxmox.panel.SendmailEditPanel', {
 	    fieldLabel: gettext('Author'),
 	    name: 'author',
 	    allowBlank: true,
-	    emptyText: 'Proxmox VE',
 	    cbind: {
+		emptyText: '{defaultMailAuthor}',
 		deleteEmpty: '{!isCreate}',
 	    },
 	},
diff --git a/src/panel/SmtpEditPanel.js b/src/panel/SmtpEditPanel.js
index 8b258e0..edb6344 100644
--- a/src/panel/SmtpEditPanel.js
+++ b/src/panel/SmtpEditPanel.js
@@ -161,8 +161,8 @@ Ext.define('Proxmox.panel.SmtpEditPanel', {
 	    fieldLabel: gettext('Author'),
 	    name: 'author',
 	    allowBlank: true,
-	    emptyText: gettext('Proxmox VE'),
 	    cbind: {
+		emptyText: '{defaultMailAuthor}',
 		deleteEmpty: '{!isCreate}',
 	    },
 	},
diff --git a/src/window/EndpointEditBase.js b/src/window/EndpointEditBase.js
index f42d0ea..993fa16 100644
--- a/src/window/EndpointEditBase.js
+++ b/src/window/EndpointEditBase.js
@@ -45,6 +45,7 @@ Ext.define('Proxmox.window.EndpointEditBase', {
 		isCreate: me.isCreate,
 		baseUrl: me.baseUrl,
 		type: me.type,
+		defaultMailAuthor: endpointConfig.defaultMailAuthor,
 	    }],
 	});
 
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox-backup v4 07/43] pbs-config: add module for loading notification config
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (5 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH widget-toolkit v4 06/43] sendmail: smtp: allow to overide default mail author Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 08/43] server: rename email_notifications module to notifications Lukas Wagner
                   ` (37 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 08/43] server: rename email_notifications module to notifications
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (6 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 07/43] pbs-config: add module for loading notification config Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 09/43] notifications: allow sending notifications via proxmox_notify Lukas Wagner
                   ` (36 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 09/43] notifications: allow sending notifications via proxmox_notify
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (7 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 08/43] server: rename email_notifications module to notifications Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 10/43] buildsys: install templates for test notifications Lukas Wagner
                   ` (35 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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     | 107 +++++++++++++++++++++++++++++++-
 3 files changed, 118 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..9fb202d8 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,102 @@ 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?;
+
+    let mut notifications = Vec::new();
+
+    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 bytes = tokio::fs::read(p).await?;
+                let notification: Notification = serde_json::from_slice(&bytes)?;
+                notifications.push(notification);
+
+                // 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?;
+            }
+        }
+    }
+
+    // Make sure that we send the oldest notification first
+    notifications.sort_unstable_by_key(|n| n.timestamp());
+
+    let res = tokio::task::spawn_blocking(move || {
+        let config = pbs_config::notifications::config()?;
+        for notification in notifications {
+            if let Err(err) = proxmox_notify::api::common::send(&config, &notification) {
+                log::error!("failed to send notification: {err}");
+            }
+        }
+
+        Ok::<(), Error>(())
+    })
+    .await?;
+
+    if let Err(e) = res {
+        log::error!("could not read notification config: {e}");
+    }
+
+    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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 10/43] buildsys: install templates for test notifications
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (8 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 09/43] notifications: allow sending notifications via proxmox_notify Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 11/43] pbs-config: acl: add /system/notifications as known ACL path Lukas Wagner
                   ` (34 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 11/43] pbs-config: acl: add /system/notifications as known ACL path
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (9 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 10/43] buildsys: install templates for test notifications Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 12/43] api: add endpoints for querying/testing notification targets Lukas Wagner
                   ` (33 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 12/43] api: add endpoints for querying/testing notification targets
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (10 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 11/43] pbs-config: acl: add /system/notifications as known ACL path Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 13/43] api: add endpoints for notification matchers Lukas Wagner
                   ` (32 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

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

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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..6c5017b0
--- /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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 13/43] api: add endpoints for notification matchers
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (11 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 12/43] api: add endpoints for querying/testing notification targets Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 14/43] api: add endpoints for sendmail targets Lukas Wagner
                   ` (31 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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..fba1859c
--- /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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 14/43] api: add endpoints for sendmail targets
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (12 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 13/43] api: add endpoints for notification matchers Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 15/43] api: add endpoints for smtp targets Lukas Wagner
                   ` (30 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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..b8ce6b2b
--- /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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 15/43] api: add endpoints for smtp targets
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (13 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 14/43] api: add endpoints for sendmail targets Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 16/43] api: add endpoints for gotify targets Lukas Wagner
                   ` (29 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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..8df2ab18
--- /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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 16/43] api: add endpoints for gotify targets
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (14 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 15/43] api: add endpoints for smtp targets Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 17/43] api: add endpoints for querying known notification values/fields Lukas Wagner
                   ` (28 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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..1f14b377
--- /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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 17/43] api: add endpoints for querying known notification values/fields
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (15 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 16/43] api: add endpoints for gotify targets Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 18/43] api-types: api: datatore: add notification-mode parameter Lukas Wagner
                   ` (27 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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 | 181 ++++++++++++++++++++++++++-
 1 file changed, 180 insertions(+), 1 deletion(-)

diff --git a/src/api2/config/notifications/mod.rs b/src/api2/config/notifications/mod.rs
index 9c4a0cd9..804626b0 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -1,7 +1,22 @@
-use proxmox_router::list_subdirs_api_method;
+use anyhow::Error;
+use serde::Serialize;
+use serde_json::Value;
+use std::cmp::Ordering;
+
+use proxmox_router::{list_subdirs_api_method, Permission, RpcEnvironment};
 use proxmox_router::{Router, SubdirMap};
+use proxmox_schema::api;
 use proxmox_sortable_macro::sortable;
 
+use pbs_api_types::PRIV_SYS_AUDIT;
+
+use crate::api2::admin::prune::list_prune_jobs;
+use crate::api2::admin::sync::list_sync_jobs;
+use crate::api2::admin::verify::list_verification_jobs;
+use crate::api2::config::datastore::list_datastores;
+use crate::api2::config::media_pool::list_pools;
+use crate::api2::tape::backup::list_tape_backup_jobs;
+
 mod gotify;
 mod matchers;
 mod sendmail;
@@ -11,6 +26,8 @@ mod targets;
 #[sortable]
 const SUBDIRS: SubdirMap = &sorted!([
     ("endpoints", &ENDPOINT_ROUTER),
+    ("matcher-fields", &FIELD_ROUTER),
+    ("matcher-field-values", &VALUE_ROUTER),
     ("targets", &targets::ROUTER),
     ("matchers", &matchers::ROUTER),
 ]);
@@ -29,3 +46,165 @@ 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>,
+}
+
+#[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() -> Result<Vec<MatchableField>, Error> {
+    let fields = [
+        "datastore",
+        "hostname",
+        "job-id",
+        "media-pool",
+        "type",
+    ]
+    .into_iter()
+    .map(Into::into)
+    .map(|name| MatchableField { name })
+    .collect();
+
+    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.clone(), rpcenv)?;
+
+    for datastore in datastores {
+        values.push(MatchableValue {
+            field: "datastore".into(),
+            value: datastore.name.clone(),
+            comment: datastore.comment.clone(),
+        });
+    }
+
+    let pools = list_pools(rpcenv)?;
+    for pool in pools {
+        values.push(MatchableValue {
+            field: "media-pool".into(),
+            value: pool.name.clone(),
+            comment: None,
+        });
+    }
+
+    let tape_backup_jobs = list_tape_backup_jobs(param.clone(), rpcenv)?;
+    for job in tape_backup_jobs {
+        values.push(MatchableValue {
+            field: "job-id".into(),
+            value: job.config.id,
+            comment: job.config.comment,
+        });
+    }
+
+    let prune_jobs = list_prune_jobs(None, param.clone(), rpcenv)?;
+    for job in prune_jobs {
+        values.push(MatchableValue {
+            field: "job-id".into(),
+            value: job.config.id,
+            comment: job.config.comment,
+        });
+    }
+
+    let sync_jobs = list_sync_jobs(None, param.clone(), rpcenv)?;
+    for job in sync_jobs {
+        values.push(MatchableValue {
+            field: "job-id".into(),
+            value: job.config.id,
+            comment: job.config.comment,
+        });
+    }
+
+    let verify_jobs = list_verification_jobs(None, param.clone(), rpcenv)?;
+    for job in verify_jobs {
+        values.push(MatchableValue {
+            field: "job-id".into(),
+            value: job.config.id,
+            comment: job.config.comment,
+        });
+    }
+
+    values.push(MatchableValue {
+        field: "hostname".into(),
+        value: proxmox_sys::nodename().into(),
+        comment: None,
+    });
+
+    for ty in [
+        "acme",
+        "gc",
+        "package-updates",
+        "prune",
+        "sync",
+        "system-mail",
+        "tape-backup",
+        "tape-load",
+        "verify",
+    ] {
+        values.push(MatchableValue {
+            field: "type".into(),
+            value: ty.into(),
+            comment: None,
+        });
+    }
+
+    values.sort_by(|a, b| match a.field.cmp(&b.field) {
+        Ordering::Equal => a.value.cmp(&b.value),
+        ord => ord,
+    });
+
+    Ok(values)
+}
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox-backup v4 18/43] api-types: api: datatore: add notification-mode parameter
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (16 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 17/43] api: add endpoints for querying known notification values/fields Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 19/43] api-types: api: tape: " Lukas Wagner
                   ` (26 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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..76b6ef43 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 to 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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 19/43] api-types: api: tape: add notification-mode parameter
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (17 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 18/43] api-types: api: datatore: add notification-mode parameter Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 20/43] server: notifications: send GC notifications via notification system Lukas Wagner
                   ` (25 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 20/43] server: notifications: send GC notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (18 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 19/43] api-types: api: tape: " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 21/43] server: notifications: send prune " Lukas Wagner
                   ` (24 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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 9fb202d8..a9b79cc8 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)?;
 
@@ -388,6 +340,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 {
@@ -424,21 +389,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,
@@ -446,7 +400,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)
@@ -457,20 +411,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(())
 }
@@ -530,8 +502,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);
@@ -766,7 +738,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 {
@@ -778,12 +752,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 {
@@ -791,68 +765,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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 21/43] server: notifications: send prune notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (19 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 20/43] server: notifications: send GC notifications via notification system Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 22/43] server: notifications: send verify " Lukas Wagner
                   ` (23 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
---
 debian/proxmox-backup-server.install        |  4 ++
 src/server/notifications.rs                 | 77 ++++++++-------------
 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, 58 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 a9b79cc8..ea092c90 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)?;
 
@@ -501,16 +470,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,
@@ -519,20 +478,40 @@ 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([
+        ("job-id".into(), jobname.to_string()),
+        ("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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 22/43] server: notifications: send verify notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (20 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 21/43] server: notifications: send prune " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 23/43] server: notifications: send sync " Lukas Wagner
                   ` (22 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
---
 debian/proxmox-backup-server.install         |  4 +
 src/server/notifications.rs                  | 86 ++++++--------------
 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, 62 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 ea092c90..cbbb1ba9 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)?;
 
@@ -417,8 +382,6 @@ pub fn send_gc_status(
 }
 
 pub fn send_verify_status(
-    email: &str,
-    notify: DatastoreNotify,
     job: VerificationJobConfig,
     result: &Result<Vec<String>, Error>,
 ) -> Result<(), Error> {
@@ -429,39 +392,45 @@ 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([
+        ("job-id".into(), job.id.clone()),
+        ("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(())
 }
 
@@ -758,9 +727,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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 23/43] server: notifications: send sync notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (21 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 22/43] server: notifications: send verify " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 24/43] server: notifications: send update " Lukas Wagner
                   ` (21 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
---
 debian/proxmox-backup-server.install       |   4 +
 src/api2/pull.rs                           |  10 +-
 src/server/notifications.rs                | 102 ++++++---------------
 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, 76 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 cbbb1ba9..5f854e4c 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)?;
 
@@ -485,21 +444,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,
@@ -507,28 +452,40 @@ 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([
+        ("job-id".into(), job.id.clone()),
+        ("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(())
 }
@@ -727,9 +684,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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 24/43] server: notifications: send update notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (22 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 23/43] server: notifications: send sync " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 25/43] server: notifications: send acme " Lukas Wagner
                   ` (20 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
---
 debian/proxmox-backup-server.install          |  2 +
 src/server/notifications.rs                   | 47 +++++++------------
 templates/Makefile                            |  2 +
 .../default/package-updates-body.txt.hbs      |  8 ++++
 .../default/package-updates-subject.txt.hbs   |  1 +
 5 files changed, 29 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 5f854e4c..8459a8ce 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(())
@@ -583,24 +569,25 @@ 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 hostname = proxmox_sys::nodename().to_string();
 
-        let (fqdn, port) = get_server_url();
+    let data = json!({
+        "fqdn": fqdn,
+        "hostname": &hostname,
+        "port": port,
+        "updates": updates,
+    });
 
-        let text = HANDLEBARS.render(
-            "package_update_template",
-            &json!({
-                "fqdn": fqdn,
-                "port": port,
-                "updates": updates,
-            }),
-        )?;
+    let metadata = HashMap::from([
+        ("hostname".into(), hostname),
+        ("type".into(), "package-updates".into()),
+    ]);
 
-        send_job_status_mail(&email, &subject, &text)?;
-    }
+    let notification =
+        Notification::from_template(Severity::Info, "package-updates", data, metadata);
+
+    send_notification(notification)?;
     Ok(())
 }
 
@@ -687,7 +674,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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 25/43] server: notifications: send acme notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (23 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 24/43] server: notifications: send update " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 26/43] server: notifications: send tape " Lukas Wagner
                   ` (19 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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 8459a8ce..11e78ff2 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(())
         });
 
@@ -598,23 +584,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(())
 }
 
@@ -673,6 +658,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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 26/43] server: notifications: send tape notifications via notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (24 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 25/43] server: notifications: send acme " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 27/43] ui: add notification config panel Lukas Wagner
                   ` (18 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
---
 debian/proxmox-backup-server.install          |   6 +
 src/api2/tape/backup.rs                       |  62 ++---
 src/api2/tape/restore.rs                      |  46 ++--
 src/server/notifications.rs                   | 213 +++++-------------
 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, 244 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 11e78ff2..d142971b 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);
@@ -229,30 +141,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,
@@ -463,7 +351,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>,
@@ -478,62 +365,86 @@ 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 mut 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()),
+    ]);
 
-    send_job_status_mail(email, &subject, &text)?;
+    if let Some(id) = id {
+        metadata.insert("job-id".into(), id.into());
+    }
+
+    let notification = Notification::from_template(severity, template, data, metadata);
+
+    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) {
@@ -653,9 +564,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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 27/43] ui: add notification config panel
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (25 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 26/43] server: notifications: send tape " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode Lukas Wagner
                   ` (17 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (26 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 27/43] ui: add notification config panel Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 14:32   ` Dominik Csapak
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 29/43] ui: tape backup: add selector for 'notification-mode' Lukas Wagner
                   ` (16 subsequent siblings)
  44 siblings, 1 reply; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 29/43] ui: tape backup: add selector for 'notification-mode'
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (27 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 30/43] ui: tape restore: add 'notification-mode' parameter Lukas Wagner
                   ` (15 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 30/43] ui: tape restore: add 'notification-mode' parameter
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (28 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 29/43] ui: tape backup: add selector for 'notification-mode' Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 31/43] ui: datastore options: " Lukas Wagner
                   ` (14 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 31/43] ui: datastore options: add 'notification-mode' parameter
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (29 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 30/43] ui: tape restore: add 'notification-mode' parameter Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 32/43] ui: utils: add overrides for known notification metadata fields/values Lukas Wagner
                   ` (13 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 32/43] ui: utils: add overrides for known notification metadata fields/values
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (30 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 31/43] ui: datastore options: " Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 33/43] ui: datastore edit: make new stores use notification system by default Lukas Wagner
                   ` (12 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@proxmox.com>
---
 www/Utils.js | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/www/Utils.js b/www/Utils.js
index 5357949b..69bc2bfd 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -429,6 +429,23 @@ Ext.define('PBS.Utils', {
 	    zfscreate: [gettext('ZFS Storage'), gettext('Create')],
 	});
 
+	Proxmox.Utils.overrideNotificationFieldName({
+	    'datastore': gettext('Datastore'),
+	    'job-id': gettext('Job ID'),
+	    '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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 33/43] ui: datastore edit: make new stores use notification system by default
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (31 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 32/43] ui: utils: add overrides for known notification metadata fields/values Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 34/43] ui: permissions paths: add /system/notifications to combobox Lukas Wagner
                   ` (11 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 34/43] ui: permissions paths: add /system/notifications to combobox
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (32 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 33/43] ui: datastore edit: make new stores use notification system by default Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 35/43] proxmox-backup-manager: add CLI for notification targets Lukas Wagner
                   ` (10 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 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>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 35/43] proxmox-backup-manager: add CLI for notification targets
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (33 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 34/43] ui: permissions paths: add /system/notifications to combobox Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 36/43] proxmox-backup-manager: add CLI for notification matchers Lukas Wagner
                   ` (9 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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 804626b0..b5589407 100644
--- a/src/api2/config/notifications/mod.rs
+++ b/src/api2/config/notifications/mod.rs
@@ -17,11 +17,11 @@ use crate::api2::config::datastore::list_datastores;
 use crate::api2::config::media_pool::list_pools;
 use crate::api2::tape::backup::list_tape_backup_jobs;
 
-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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 36/43] proxmox-backup-manager: add CLI for notification matchers
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (34 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 35/43] proxmox-backup-manager: add CLI for notification targets Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 37/43] proxmox-backup-manager: add CLI for gotify endpoints Lukas Wagner
                   ` (8 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 37/43] proxmox-backup-manager: add CLI for gotify endpoints
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (35 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 36/43] proxmox-backup-manager: add CLI for notification matchers Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 38/43] proxmox-backup-manager: add CLI for sendmail endpoints Lukas Wagner
                   ` (7 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 38/43] proxmox-backup-manager: add CLI for sendmail endpoints
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (36 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 37/43] proxmox-backup-manager: add CLI for gotify endpoints Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 39/43] proxmox-backup-manager: add CLI for SMTP endpoints Lukas Wagner
                   ` (6 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 39/43] proxmox-backup-manager: add CLI for SMTP endpoints
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (37 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 38/43] proxmox-backup-manager: add CLI for sendmail endpoints Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 40/43] docgen: generate synopsis for notifications{-priv, }.cfg Lukas Wagner
                   ` (5 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Reviewed-by: Gabriel Goller <g.goller@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



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


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

* [pbs-devel] [PATCH proxmox-backup v4 40/43] docgen: generate synopsis for notifications{-priv, }.cfg
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (38 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 39/43] proxmox-backup-manager: add CLI for SMTP endpoints Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 41/43] docs: add documentation for notification system Lukas Wagner
                   ` (4 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/bin/docgen.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/bin/docgen.rs b/src/bin/docgen.rs
index 7ee991a0..66d71423 100644
--- a/src/bin/docgen.rs
+++ b/src/bin/docgen.rs
@@ -31,6 +31,10 @@ fn main() -> Result<(), Error> {
             "apidata.js" => generate_api_tree(),
             "datastore.cfg" => dump_section_config(&pbs_config::datastore::CONFIG),
             "domains.cfg" => dump_section_config(&pbs_config::domains::CONFIG),
+            "notifications.cfg" => dump_section_config(proxmox_notify::config::config_parser()),
+            "notifications-priv.cfg" => {
+                dump_section_config(proxmox_notify::config::private_config_parser())
+            }
             "tape.cfg" => dump_section_config(&pbs_config::drive::CONFIG),
             "tape-job.cfg" => dump_section_config(&pbs_config::tape_job::CONFIG),
             "user.cfg" => dump_section_config(&pbs_config::user::CONFIG),
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox-backup v4 41/43] docs: add documentation for notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (39 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 40/43] docgen: generate synopsis for notifications{-priv, }.cfg Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 42/43] ui: util: override default mail author for sendmail/smtp targets Lukas Wagner
                   ` (3 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Mostly copied from PVE and adapted where it makes sense.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 debian/proxmox-backup-server.install      |   2 +
 docs/Makefile                             |   6 +-
 docs/conf.py                              |   2 +
 docs/config/notifications-priv/format.rst |   1 +
 docs/config/notifications-priv/man5.rst   |  24 +++
 docs/config/notifications/format.rst      |  28 +++
 docs/config/notifications/man5.rst        |  24 +++
 docs/configuration-files.rst              |  30 +++
 docs/index.rst                            |   1 +
 docs/notifications.rst                    | 213 ++++++++++++++++++++++
 www/OnlineHelpInfo.js                     |  24 +++
 11 files changed, 354 insertions(+), 1 deletion(-)
 create mode 100644 docs/config/notifications-priv/format.rst
 create mode 100644 docs/config/notifications-priv/man5.rst
 create mode 100644 docs/config/notifications/format.rst
 create mode 100644 docs/config/notifications/man5.rst
 create mode 100644 docs/notifications.rst

diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index df7d68ee..ef1e9ba1 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -30,6 +30,8 @@ usr/share/man/man5/acl.cfg.5
 usr/share/man/man5/datastore.cfg.5
 usr/share/man/man5/domains.cfg.5
 usr/share/man/man5/media-pool.cfg.5
+usr/share/man/man5/notifications.cfg.5
+usr/share/man/man5/notifications-priv.cfg.5
 usr/share/man/man5/remote.cfg.5
 usr/share/man/man5/sync.cfg.5
 usr/share/man/man5/tape-job.cfg.5
diff --git a/docs/Makefile b/docs/Makefile
index 0d0963f3..a617fc91 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -11,6 +11,8 @@ GENERATED_SYNOPSIS := 						\
 	pmtx/synopsis.rst					\
 	pmt/synopsis.rst					\
 	config/media-pool/config.rst				\
+	config/notifications/config.rst				\
+	config/notifications-priv/config.rst				\
 	config/tape/config.rst					\
 	config/tape-job/config.rst				\
 	config/user/config.rst					\
@@ -43,7 +45,9 @@ MAN5_PAGES :=				\
 	sync.cfg.5			\
 	verification.cfg.5		\
 	datastore.cfg.5			\
-	domains.cfg.5
+	domains.cfg.5			\
+	notifications.cfg.5		\
+	notifications-priv.cfg.5	\
 
 PRUNE_SIMULATOR_FILES := 					\
 	prune-simulator/index.html				\
diff --git a/docs/conf.py b/docs/conf.py
index f85cd187..fec234b9 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -114,6 +114,8 @@ man_pages = [
     ('config/tape/man5', 'tape.cfg', 'Tape Drive and Changer Configuration', [author], 5),
     ('config/user/man5', 'user.cfg', 'User Configuration', [author], 5),
     ('config/verification/man5', 'verification.cfg', 'Verification Job Configuration', [author], 5),
+    ('config/notifications/man5', 'notifications.cfg', 'Notification target/matcher configuration', [author], 5),
+    ('config/notifications-priv/man5', 'notifications-priv.cfg', 'Notification target secrets', [author], 5),
 ]
 
 
diff --git a/docs/config/notifications-priv/format.rst b/docs/config/notifications-priv/format.rst
new file mode 100644
index 00000000..7d92c979
--- /dev/null
+++ b/docs/config/notifications-priv/format.rst
@@ -0,0 +1 @@
+This file contains protected credentials for notification targets.
diff --git a/docs/config/notifications-priv/man5.rst b/docs/config/notifications-priv/man5.rst
new file mode 100644
index 00000000..827c8ac7
--- /dev/null
+++ b/docs/config/notifications-priv/man5.rst
@@ -0,0 +1,24 @@
+:orphan:
+
+======================
+notifications-priv.cfg
+======================
+
+Description
+===========
+
+The file /etc/proxmox-backup/notifications-priv.cfg is a configuration file
+for Proxmox Backup Server. It contains the configration for the 
+notification system configuration.
+
+File Format
+===========
+
+.. include:: format.rst
+
+Options
+=======
+
+.. include:: config.rst
+
+.. include:: ../../pbs-copyright.rst
diff --git a/docs/config/notifications/format.rst b/docs/config/notifications/format.rst
new file mode 100644
index 00000000..e29facb5
--- /dev/null
+++ b/docs/config/notifications/format.rst
@@ -0,0 +1,28 @@
+This file contains configuration for notification targets and notification
+matchers.
+
+.. Each user configuration section starts with the header ``<realm-type>: <name>``,
+.. followed by the realm's configuration options.
+..
+.. For LDAP realms, the LDAP bind password is stored in ``ldap_passwords.json``.
+..
+.. ::
+..
+..   openid: master
+.. 	client-id pbs
+.. 	comment
+.. 	issuer-url http://192.168.0.10:8080/realms/master
+.. 	username-claim username
+..
+..   ldap: ldap-server
+.. 	base-dn OU=People,DC=ldap-server,DC=example,DC=com
+.. 	mode ldaps
+.. 	server1 192.168.0.10
+.. 	sync-attributes email=mail
+.. 	sync-defaults-options enable-new=0,remove-vanished=acl;entry
+.. 	user-attr uid
+.. 	user-classes inetorgperson,posixaccount,person,user
+..
+..
+.. You can use the ``proxmox-backup-manager openid`` and ``proxmox-backup-manager ldap`` commands to manipulate
+.. this file.
diff --git a/docs/config/notifications/man5.rst b/docs/config/notifications/man5.rst
new file mode 100644
index 00000000..96844162
--- /dev/null
+++ b/docs/config/notifications/man5.rst
@@ -0,0 +1,24 @@
+:orphan:
+
+==================
+notifications.cfg
+==================
+
+Description
+===========
+
+The file /etc/proxmox-backup/notifications.cfg is a configuration file
+for Proxmox Backup Server. It contains the configration for the 
+notification system configuration.
+
+File Format
+===========
+
+.. include:: format.rst
+
+Options
+=======
+
+.. include:: config.rst
+
+.. include:: ../../pbs-copyright.rst
diff --git a/docs/configuration-files.rst b/docs/configuration-files.rst
index ba54a7b0..ee8367e5 100644
--- a/docs/configuration-files.rst
+++ b/docs/configuration-files.rst
@@ -67,6 +67,36 @@ Options
 
 .. include:: config/media-pool/config.rst
 
+.. _notifications.cfg:
+
+``notifications.cfg``
+~~~~~~~~~~~~~~~~~~~~~
+
+File Format
+^^^^^^^^^^^
+
+.. include:: config/notifications/format.rst
+
+
+Options
+^^^^^^^
+
+.. include:: config/notifications/config.rst
+
+``notifications-priv.cfg``
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+File Format
+^^^^^^^^^^^
+
+.. include:: config/notifications-priv/format.rst
+
+
+Options
+^^^^^^^
+
+.. include:: config/notifications-priv/config.rst
+
 
 ``tape.cfg``
 ~~~~~~~~~~~~
diff --git a/docs/index.rst b/docs/index.rst
index 8e13c24f..86add2d7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -34,6 +34,7 @@ in the section entitled "GNU Free Documentation License".
    maintenance.rst
    sysadmin.rst
    network-management.rst
+   notifications.rst
    technical-overview.rst
    faq.rst
 
diff --git a/docs/notifications.rst b/docs/notifications.rst
new file mode 100644
index 00000000..4f9b01b7
--- /dev/null
+++ b/docs/notifications.rst
@@ -0,0 +1,213 @@
+Notifications
+=============
+
+Overview
+--------
+
+Proxmox Backup Server will send notifications if case of noteworthy
+events.
+
+There are a number of different :ref:`Notification Events`,
+each with their own set of metadata fields that can be used in
+notification matchers.
+
+A notification matcher determines *which* notifications shall be sent *where*.
+A matcher has *match rules*, that can be used to
+match on certain notification properties (e.g. timestamp, severity,
+metadata fields).
+If a matcher matches a notification, the notification will be routed
+to a configured set of notification targets.
+
+A notification target is an abstraction for a destination where a
+notification should be sent to - for instance a Gotify server instance,
+or a set of email addresses.
+There are multiple types of notification targets, including
+sendmail, which uses the system's sendmail command to send emails,
+or gotify, which sends a notification to a Gotify instance.
+
+The notification system can be configured in the GUI under
+``Configuration -> Notifications``. The configuration is stored in
+``/etc/proxmox-backup/notifications.cfg`` and
+``/etc/proxmox-backup/notifications-priv.cfg`` -
+the latter contains sensitive configuration options such as
+passwords or authentication tokens for notification targets and
+can only be read by ``root``.
+
+
+Notification Targets
+--------------------
+
+Proxmox Backup Server offers multiple types of notification targets.
+
+.. _notification_targets_sendmail:
+
+Sendmail
+^^^^^^^^
+The sendmail binary is a program commonly found on Unix-like operating systems
+that handles the sending of email messages.
+It is a command-line utility that allows users and applications to send emails
+directly from the command line or from within scripts.
+
+The sendmail notification target uses the ``sendmail`` binary to send emails to a
+list of configured users or email addresses. If a user is selected as a recipient,
+the email address configured in user's settings will be used.
+For the ``root@pam`` user, this is the email address entered during installation.
+A user's email address can be configured in ``Configuration -> Access Control -> User Management``.
+If a user has no associated email address, no email will be sent.
+
+.. NOTE:: In standard Proxmox Backup Server installations, the ``sendmail`` binary is provided by
+   Postfix. It may be necessary to configure Postfix so that it can deliver
+   mails correctly - for example by setting an external mail relay (smart host).
+   In case of failed delivery, check the system logs for messages logged by
+   the Postfix daemon.
+
+See :ref:`notifications.cfg` for all configuration options.
+
+.. _notification_targets_smtp:
+
+SMTP
+^^^^
+SMTP notification targets can send emails directly to an SMTP mail relay.
+This target does not use the system's MTA to deliver emails.
+Similar to sendmail targets, if a user is selected as a recipient, the user's configured
+email address will be used.
+
+.. NOTE:: Unlike sendmail targets, SMTP targets do not have any queuing/retry mechanism
+   in case of a failed mail delivery.
+
+See :ref:`notifications.cfg` for all configuration options.
+
+.. _notification_targets_gotify:
+
+Gotify
+^^^^^^
+`Gotify <http://gotify.net>`_ is an open-source self-hosted notification server that
+allows you to send push notifications to various devices and
+applications. It provides a simple API and web interface, making it easy to
+integrate with different platforms and services.
+
+See :ref:`notifications.cfg` for all configuration options.
+
+.. _notification_matchers:
+
+Notification Matchers
+---------------------
+
+Notification matchers route notifications to notification targets based
+on their matching rules. These rules can match certain properties of a
+notification, such as the timestamp (``match-calendar``), the severity of
+the notification (``match-severity``) or metadata fields (``match-field``).
+If a notification is matched by a matcher, all targets configured for the
+matcher will receive the notification.
+
+An arbitrary number of matchers can be created, each with with their own
+matching rules and targets to notify.
+Every target is notified at most once for every notification, even if
+the target is used in multiple matchers.
+
+A matcher without rules matches any notification; the configured targets
+will always be notified.
+
+See :ref:`notifications.cfg` for all configuration options.
+
+Calendar Matching Rules
+^^^^^^^^^^^^^^^^^^^^^^^
+A calendar matcher matches a notification's timestamp.
+
+Examples:
+
+* ``match-calendar 8-12``
+* ``match-calendar 8:00-15:30``
+* ``match-calendar mon-fri 9:00-17:00``
+* ``match-calendar sun,tue-wed,fri 9-17``
+
+Field Matching Rules
+^^^^^^^^^^^^^^^^^^^^
+Notifications have a selection of metadata fields that can be matched.
+When using ``exact`` as a matching mode, a ``,`` can be used as a separator.
+The matching rule then matches if the metadata field has **any** of the specified
+values.
+
+Examples:
+
+* ``match-field exact:type=gc`` Only match notifications for garbage collection jobs
+* ``match-field exact:type=prune,verify`` Match prune job and verification job notifications.
+* ``match-field regex:datastore=^backup-.*$`` Match any datastore starting with ``backup``.
+
+If a notification does not have the matched field, the rule will **not** match.
+For instance, a ``match-field regex:datastore=.*`` directive will match any notification that has
+a ``datastore`` metadata field, but will not match if the field does not exist.
+
+Severity Matching Rules
+^^^^^^^^^^^^^^^^^^^^^^^
+A notification has a associated severity that can be matched.
+
+Examples:
+
+* ``match-severity error``: Only match errors
+* ``match-severity warning,error``: Match warnings and error
+
+The following severities are in use:
+``info``, ``notice``, ``warning``, ``error``, ``unknown``.
+
+.. _Notification Events:
+
+Notification Events
+-------------------
+
+The following table contains a list of all notification events in Proxmox Backup server, their
+type, severity and additional metadata fields. ``type`` as well as any other metadata field
+may be used in ``match-field`` match rules.
+
+================================ ==================== ========== ==============================================================
+Event                            ``type``             Severity   Metadata fields (in addition to ``type``)
+================================ ==================== ========== ==============================================================
+ACME certificate renewal failed  ``acme``             ``error``  ``hostname``
+Garbage collection failure       ``gc``               ``error``  ``datastore``, ``hostname``
+Garbage collection success       ``gc``               ``info``   ``datastore``, ``hostname``
+Package updates available        ``package-updates``  ``info``   ``hostname``
+Prune job failure                ``prune``            ``error``  ``datastore``, ``hostname``, ``job-id``
+Prune job success                ``prune``            ``info``   ``datastore``, ``hostname``, ``job-id``
+Remote sync failure              ``sync``             ``error``  ``datastore``, ``hostname``, ``job-id``
+Remote sync success              ``sync``             ``info``   ``datastore``, ``hostname``, ``job-id``
+Tape backup job failure          ``tape-backup``      ``error``  ``datastore``, ``hostname``, ``media-pool``, ``job-id``
+Tape backup job success          ``tape-backup``      ``info``   ``datastore``, ``hostname``, ``media-pool``, ``job-id``
+Tape loading request             ``tape-load``        ``notice`` ``hostname``
+Verification job failure         ``verification``     ``error``  ``datastore``, ``hostname``, ``job-id``
+Verification job success         ``verification``     ``info``   ``datastore``, ``hostname``, ``job-id``
+================================ ==================== ========== ==============================================================
+
+The following table contains a description of all use metadata fields. All of these
+can be used in ``match-field`` match rules.
+
+==================== ===================================
+Metadata field       Description
+==================== ===================================
+``datastore``        The name of the datastore
+``hostname``         The hostname of the backup server
+``job-id``           Job ID
+``media-pool``       The name of the tape media pool
+``type``             Notification event type
+==================== ===================================
+
+.. NOTE:: The daily task checking for any available system updates only sends
+   notifications if the node has an active subscription.
+
+System Mail Forwarding
+----------------------
+Certain local system daemons, such as ``smartd``, send notification emails
+to the local ``root`` user. Proxmox Backup Server will feed these mails
+into the notification system as a notification of type ``system-mail``
+and with severity ``unknown``.
+
+When the email is forwarded to a sendmail target, the mail's content and headers
+are forwarded as-is. For all other targets,
+the system tries to extract both a subject line and the main text body
+from the email content. In instances where emails solely consist of HTML
+content, they will be transformed into plain text format during this process.
+
+Permissions
+-----------
+In order to modify/view the configuration for notification targets,
+the ``Sys.Modify/Sys.Audit`` permissions are required for the
+``/system/notifications`` ACL node.
diff --git a/www/OnlineHelpInfo.js b/www/OnlineHelpInfo.js
index 2402c11d..639408b1 100644
--- a/www/OnlineHelpInfo.js
+++ b/www/OnlineHelpInfo.js
@@ -35,6 +35,10 @@ const proxmoxOnlineHelpInfo = {
     "link": "/docs/configuration-files.html#domains-cfg",
     "title": "``domains.cfg``"
   },
+  "notifications-cfg": {
+    "link": "/docs/configuration-files.html#notifications-cfg",
+    "title": "``notifications.cfg``"
+  },
   "faq-support-table": {
     "link": "/docs/faq.html#faq-support-table",
     "title": "How long will my Proxmox Backup Server version be supported?"
@@ -139,6 +143,26 @@ const proxmoxOnlineHelpInfo = {
     "link": "/docs/network-management.html#sysadmin-traffic-control",
     "title": "Traffic Control"
   },
+  "notification-targets-sendmail": {
+    "link": "/docs/notifications.html#notification-targets-sendmail",
+    "title": "Sendmail"
+  },
+  "notification-targets-smtp": {
+    "link": "/docs/notifications.html#notification-targets-smtp",
+    "title": "SMTP"
+  },
+  "notification-targets-gotify": {
+    "link": "/docs/notifications.html#notification-targets-gotify",
+    "title": "Gotify"
+  },
+  "notification-matchers": {
+    "link": "/docs/notifications.html#notification-matchers",
+    "title": "Notification Matchers"
+  },
+  "notification-events": {
+    "link": "/docs/notifications.html#notification-events",
+    "title": "Notification Events"
+  },
   "pve-integration": {
     "link": "/docs/pve-integration.html#pve-integration",
     "title": "`Proxmox VE`_ Integration"
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox-backup v4 42/43] ui: util: override default mail author for sendmail/smtp targets
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (40 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 41/43] docs: add documentation for notification system Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 43/43] ui: notifications: pull in UX improvements for match rules creation Lukas Wagner
                   ` (2 subsequent siblings)
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

Otherwise, 'Proxmox VE' is shown as the default author in the UI.

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

diff --git a/www/Utils.js b/www/Utils.js
index 69bc2bfd..37c9e16a 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -455,6 +455,28 @@ Ext.define('PBS.Utils', {
 		sync: false,
 	    },
 	});
+
+	// TODO: use `overrideEndpointTypes` later - not done right now to avoid
+	// breakage if widget-toolkit is not updated yet.
+	Proxmox.Schema.notificationEndpointTypes = {
+	    sendmail: {
+		name: 'Sendmail',
+		    ipanel: 'pmxSendmailEditPanel',
+		    iconCls: 'fa-envelope-o',
+		    defaultMailAuthor: 'Proxmox Backup Server ($hostname)',
+	    },
+	    smtp: {
+		name: 'SMTP',
+		    ipanel: 'pmxSmtpEditPanel',
+		    iconCls: 'fa-envelope-o',
+		    defaultMailAuthor: 'Proxmox Backup Server ($hostname)',
+	    },
+	    gotify: {
+		name: 'Gotify',
+		    ipanel: 'pmxGotifyEditPanel',
+		    iconCls: 'fa-bell-o',
+	    },
+	};
     },
 
     // Convert an ArrayBuffer to a base64url encoded string.
-- 
2.39.2



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


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

* [pbs-devel] [PATCH proxmox-backup v4 43/43] ui: notifications: pull in UX improvements for match rules creation
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (41 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 42/43] ui: util: override default mail author for sendmail/smtp targets Lukas Wagner
@ 2024-04-22 12:38 ` Lukas Wagner
  2024-04-22 13:58 ` [pbs-devel] [PATCH many v4 00/43] integrate notification system Maximiliano Sandoval
  2024-04-22 14:24 ` Fabian Grünbichler
  44 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-22 12:38 UTC (permalink / raw)
  To: pbs-devel

These changes have not been applied yet in widget toolkit, but
are very valuable for the initial integration in PBS.
We override modified components and replace them with the patched
variants.
The changes change the edit window such that known field names and
values are suggested in a combobox. Also, the 'exact' match mode
can now match multiple values.

This can and *should* be removed once the changes from [1] are
merged into the widget toolkit.

[1] https://lists.proxmox.com/pipermail/pve-devel/2024-April/063539.html

Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 www/Makefile                              |    1 +
 www/window/NotificationMatcherOverride.js | 1105 +++++++++++++++++++++
 2 files changed, 1106 insertions(+)
 create mode 100644 www/window/NotificationMatcherOverride.js

diff --git a/www/Makefile b/www/Makefile
index f3d90bc4..b1999711 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -75,6 +75,7 @@ JSSRC=							\
 	window/NamespaceEdit.js				\
 	window/MaintenanceOptions.js			\
 	window/NotesEdit.js				\
+	window/NotificationMatcherOverride.js		\
 	window/RemoteEdit.js				\
 	window/TrafficControlEdit.js			\
 	window/NotifyOptions.js				\
diff --git a/www/window/NotificationMatcherOverride.js b/www/window/NotificationMatcherOverride.js
new file mode 100644
index 00000000..8636653c
--- /dev/null
+++ b/www/window/NotificationMatcherOverride.js
@@ -0,0 +1,1105 @@
+// Override some components from widget toolkit.
+// This was done so that we can already use the improved UI for editing
+// match rules without waiting for the needed API calls in PVE to be merged
+//
+// This can and *should* be removed once these changes have landed in
+// widget toolkit:
+// https://lists.proxmox.com/pipermail/pve-devel/2024-April/063539.html
+
+
+Ext.define('pbs-notification-fields', {
+    extend: 'Ext.data.Model',
+    fields: ['name', 'description'],
+    idProperty: 'name',
+});
+
+Ext.define('pbs-notification-field-values', {
+    extend: 'Ext.data.Model',
+    fields: ['value', 'comment', 'field'],
+    idProperty: 'value',
+});
+
+Ext.define('PBS.panel.NotificationRulesEditPanel', {
+    override: 'Proxmox.panel.NotificationRulesEditPanel',
+    extend: 'Proxmox.panel.InputPanel',
+    xtype: 'pmxNotificationMatchRulesEditPanel',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	// we want to also set the empty value, but 'bind' does not do that so
+	// we have to set it then (and only then) to get the correct value in
+	// the tree
+	control: {
+	    'field': {
+		change: function(cmp) {
+		    let me = this;
+		    let vm = me.getViewModel();
+		    if (cmp.field) {
+			let record = vm.get('selectedRecord');
+			if (!record) {
+			    return;
+			}
+			let data = Ext.apply({}, record.get('data'));
+			let value = cmp.getValue();
+			// only update if the value is empty (or empty array)
+			if (!value || !value.length) {
+			    data[cmp.field] = value;
+			    record.set({ data });
+			}
+		    }
+		},
+	    },
+	},
+    },
+
+    viewModel: {
+	data: {
+	    selectedRecord: null,
+	    matchFieldType: 'exact',
+	    matchFieldField: '',
+	    matchFieldValue: '',
+	    rootMode: 'all',
+	},
+
+	formulas: {
+	    nodeType: {
+		get: function(get) {
+		    let record = get('selectedRecord');
+		    return record?.get('type');
+		},
+		set: function(value) {
+		    let me = this;
+		    let record = me.get('selectedRecord');
+
+		    let data;
+
+		    switch (value) {
+			case 'match-severity':
+			    data = {
+				value: ['info', 'notice', 'warning', 'error', 'unknown'],
+			    };
+			    break;
+			case 'match-field':
+			    data = {
+				type: 'exact',
+				field: '',
+				value: '',
+			    };
+			    break;
+			case 'match-calendar':
+			    data = {
+				value: '',
+			    };
+			    break;
+		    }
+
+		    let node = {
+			type: value,
+			data,
+		    };
+		    record.set(node);
+		},
+	    },
+	    showMatchingMode: function(get) {
+		let record = get('selectedRecord');
+		if (!record) {
+		    return false;
+		}
+		return record.isRoot();
+	    },
+	    showMatcherType: function(get) {
+		let record = get('selectedRecord');
+		if (!record) {
+		    return false;
+		}
+		return !record.isRoot();
+	    },
+
+	    rootMode: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		set: function(value) {
+		    let me = this;
+		    let record = me.get('selectedRecord');
+		    let currentData = record.get('data');
+		    let invert = false;
+		    if (value.startsWith('not')) {
+			value = value.substring(3);
+			invert = true;
+		    }
+		    record.set({
+			data: {
+			    ...currentData,
+			    value,
+			    invert,
+			},
+		    });
+		},
+		get: function(record) {
+		    let prefix = record?.get('data').invert ? 'not' : '';
+		    return prefix + record?.get('data')?.value;
+		},
+	    },
+	},
+    },
+
+    column1: [
+	{
+	    xtype: 'pbsNotificationMatchRuleTree',
+	    cbind: {
+		isCreate: '{isCreate}',
+	    },
+	},
+    ],
+    column2: [
+	{
+	    xtype: 'pbsNotificationMatchRuleSettings',
+	    cbind: {
+		baseUrl: '{baseUrl}',
+	    },
+	},
+
+    ],
+
+    onGetValues: function(values) {
+	let me = this;
+
+	let deleteArrayIfEmtpy = (field) => {
+	    if (Ext.isArray(values[field])) {
+		if (values[field].length === 0) {
+		    delete values[field];
+		    if (!me.isCreate) {
+			Proxmox.Utils.assemble_field_data(values, { 'delete': field });
+		    }
+		}
+	    }
+	};
+	deleteArrayIfEmtpy('match-field');
+	deleteArrayIfEmtpy('match-severity');
+	deleteArrayIfEmtpy('match-calendar');
+
+	return values;
+    },
+});
+
+Ext.define('PBS.panel.NotificationMatchRuleTree', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pbsNotificationMatchRuleTree',
+    mixins: ['Proxmox.Mixin.CBind'],
+    border: false,
+
+    getNodeTextAndIcon: function(type, data) {
+	let text;
+	let iconCls;
+
+	switch (type) {
+	    case 'match-severity': {
+		let v = data.value;
+		if (Ext.isArray(data.value)) {
+		    v = data.value.join(', ');
+		}
+		text = Ext.String.format(gettext("Match severity: {0}"), v);
+		iconCls = 'fa fa-exclamation';
+		if (!v) {
+		    iconCls += ' internal-error';
+		}
+	    } break;
+	    case 'match-field': {
+		let field = data.field;
+		let value = data.value;
+		text = Ext.String.format(gettext("Match field: {0}={1}"), field, value);
+		iconCls = 'fa fa-square-o';
+		if (!field || !value || (Ext.isArray(value) && !value.length)) {
+		    iconCls += ' internal-error';
+		}
+	    } break;
+	    case 'match-calendar': {
+		let v = data.value;
+		text = Ext.String.format(gettext("Match calendar: {0}"), v);
+		iconCls = 'fa fa-calendar-o';
+		if (!v || !v.length) {
+		    iconCls += ' internal-error';
+		}
+	    } break;
+	    case 'mode':
+		if (data.value === 'all') {
+		    text = gettext("All");
+		} else if (data.value === 'any') {
+		    text = gettext("Any");
+		}
+		if (data.invert) {
+		    text = `!${text}`;
+		}
+		iconCls = 'fa fa-filter';
+
+		break;
+	}
+
+	return [text, iconCls];
+    },
+
+    initComponent: function() {
+	let me = this;
+
+	let treeStore = Ext.create('Ext.data.TreeStore', {
+	    root: {
+		expanded: true,
+		expandable: false,
+		text: '',
+		type: 'mode',
+		data: {
+		    value: 'all',
+		    invert: false,
+		},
+		children: [],
+		iconCls: 'fa fa-filter',
+	    },
+	});
+
+	let realMatchFields = Ext.create({
+	    xtype: 'hiddenfield',
+	    setValue: function(value) {
+		this.value = value;
+		this.checkChange();
+	    },
+	    getValue: function() {
+		return this.value;
+	    },
+	    getErrors: function() {
+		for (const matcher of this.value ?? []) {
+		    let matches = matcher.match(/^([^:]+):([^=]+)=(.+)$/);
+		    if (!matches) {
+			return [""]; // fake error for validation
+		    }
+		}
+		return [];
+	    },
+	    getSubmitValue: function() {
+		let value = this.value;
+		if (!value) {
+		    value = [];
+		}
+		return value;
+	    },
+	    name: 'match-field',
+	});
+
+	let realMatchSeverity = Ext.create({
+	    xtype: 'hiddenfield',
+	    setValue: function(value) {
+		this.value = value;
+		this.checkChange();
+	    },
+	    getValue: function() {
+		return this.value;
+	    },
+	    getErrors: function() {
+		for (const severities of this.value ?? []) {
+		    if (!severities) {
+			return [""]; // fake error for validation
+		    }
+		}
+		return [];
+	    },
+	    getSubmitValue: function() {
+		let value = this.value;
+		if (!value) {
+		    value = [];
+		}
+		return value;
+	    },
+	    name: 'match-severity',
+	});
+
+	let realMode = Ext.create({
+	    xtype: 'hiddenfield',
+	    name: 'mode',
+	    setValue: function(value) {
+		this.value = value;
+		this.checkChange();
+	    },
+	    getValue: function() {
+		return this.value;
+	    },
+	    getSubmitValue: function() {
+		let value = this.value;
+		return value;
+	    },
+	});
+
+	let realMatchCalendar = Ext.create({
+	    xtype: 'hiddenfield',
+	    name: 'match-calendar',
+
+	    setValue: function(value) {
+		this.value = value;
+		this.checkChange();
+	    },
+	    getValue: function() {
+		return this.value;
+	    },
+	    getErrors: function() {
+		for (const timespan of this.value ?? []) {
+		    if (!timespan) {
+			return [""]; // fake error for validation
+		    }
+		}
+		return [];
+	    },
+	    getSubmitValue: function() {
+		let value = this.value;
+		return value;
+	    },
+	});
+
+	let realInvertMatch = Ext.create({
+	    xtype: 'proxmoxcheckbox',
+	    name: 'invert-match',
+	    hidden: true,
+	    deleteEmpty: !me.isCreate,
+	});
+
+	let storeChanged = function(store) {
+	    store.suspendEvent('datachanged');
+
+	    let matchFieldStmts = [];
+	    let matchSeverityStmts = [];
+	    let matchCalendarStmts = [];
+	    let modeStmt = 'all';
+	    let invertMatchStmt = false;
+
+	    store.each(function(model) {
+		let type = model.get('type');
+		let data = model.get('data');
+
+		switch (type) {
+		    case 'match-field':
+			matchFieldStmts.push(`${data.type}:${data.field ?? ''}=${data.value ?? ''}`);
+			break;
+		    case 'match-severity':
+			if (Ext.isArray(data.value)) {
+			    matchSeverityStmts.push(data.value.join(','));
+			} else {
+			    matchSeverityStmts.push(data.value);
+			}
+			break;
+		    case 'match-calendar':
+			matchCalendarStmts.push(data.value);
+			break;
+		    case 'mode':
+			modeStmt = data.value;
+			invertMatchStmt = data.invert;
+			break;
+		}
+
+		let [text, iconCls] = me.getNodeTextAndIcon(type, data);
+		model.set({
+		    text,
+		    iconCls,
+		});
+	    });
+
+	    realMatchFields.suspendEvent('change');
+	    realMatchFields.setValue(matchFieldStmts);
+	    realMatchFields.resumeEvent('change');
+
+	    realMatchCalendar.suspendEvent('change');
+	    realMatchCalendar.setValue(matchCalendarStmts);
+	    realMatchCalendar.resumeEvent('change');
+
+	    realMode.suspendEvent('change');
+	    realMode.setValue(modeStmt);
+	    realMode.resumeEvent('change');
+
+	    realInvertMatch.suspendEvent('change');
+	    realInvertMatch.setValue(invertMatchStmt);
+	    realInvertMatch.resumeEvent('change');
+
+	    realMatchSeverity.suspendEvent('change');
+	    realMatchSeverity.setValue(matchSeverityStmts);
+	    realMatchSeverity.resumeEvent('change');
+
+	    store.resumeEvent('datachanged');
+	};
+
+	realMatchFields.addListener('change', function(field, value) {
+	    let parseMatchField = function(filter) {
+		let [, type, matchedField, matchedValue] =
+		    filter.match(/^(?:(regex|exact):)?([A-Za-z0-9_][A-Za-z0-9._-]*)=(.+)$/);
+		if (type === undefined) {
+		    type = "exact";
+		}
+
+		if (type === 'exact') {
+		    matchedValue = matchedValue.split(',');
+		}
+
+		return {
+		    type: 'match-field',
+		    data: {
+			type,
+			field: matchedField,
+			value: matchedValue,
+		    },
+		    leaf: true,
+		};
+	    };
+
+	    for (let node of treeStore.queryBy(
+		record => record.get('type') === 'match-field',
+	    ).getRange()) {
+		node.remove(true);
+	    }
+
+	    if (!value) {
+		return;
+	    }
+	    let records = value.map(parseMatchField);
+
+	    let rootNode = treeStore.getRootNode();
+
+	    for (let record of records) {
+		rootNode.appendChild(record);
+	    }
+	});
+
+	realMatchSeverity.addListener('change', function(field, value) {
+	    let parseSeverity = function(severities) {
+		return {
+		    type: 'match-severity',
+		    data: {
+			value: severities.split(','),
+		    },
+		    leaf: true,
+		};
+	    };
+
+	    for (let node of treeStore.queryBy(
+		record => record.get('type') === 'match-severity').getRange()) {
+		node.remove(true);
+	    }
+
+	    let records = value.map(parseSeverity);
+	    let rootNode = treeStore.getRootNode();
+
+	    for (let record of records) {
+		rootNode.appendChild(record);
+	    }
+	});
+
+	realMatchCalendar.addListener('change', function(field, value) {
+	    let parseCalendar = function(timespan) {
+		return {
+		    type: 'match-calendar',
+		    data: {
+			value: timespan,
+		    },
+		    leaf: true,
+		};
+	    };
+
+	    for (let node of treeStore.queryBy(
+		record => record.get('type') === 'match-calendar').getRange()) {
+		node.remove(true);
+	    }
+
+	    let records = value.map(parseCalendar);
+	    let rootNode = treeStore.getRootNode();
+
+	    for (let record of records) {
+		rootNode.appendChild(record);
+	    }
+	});
+
+	realMode.addListener('change', function(field, value) {
+	    let data = treeStore.getRootNode().get('data');
+	    treeStore.getRootNode().set('data', {
+		...data,
+		value,
+	    });
+	});
+
+	realInvertMatch.addListener('change', function(field, value) {
+	    let data = treeStore.getRootNode().get('data');
+	    treeStore.getRootNode().set('data', {
+		...data,
+		invert: value,
+	    });
+	});
+
+	treeStore.addListener('datachanged', storeChanged);
+
+	let treePanel = Ext.create({
+	    xtype: 'treepanel',
+	    store: treeStore,
+	    minHeight: 300,
+	    maxHeight: 300,
+	    scrollable: true,
+
+	    bind: {
+		selection: '{selectedRecord}',
+	    },
+	});
+
+	let addNode = function() {
+	    let node = {
+		type: 'match-field',
+		data: {
+		    type: 'exact',
+		    field: '',
+		    value: '',
+		},
+		leaf: true,
+	    };
+	    treeStore.getRootNode().appendChild(node);
+	    treePanel.setSelection(treeStore.getRootNode().lastChild);
+	};
+
+	let deleteNode = function() {
+	    let selection = treePanel.getSelection();
+	    for (let selected of selection) {
+		if (!selected.isRoot()) {
+		    selected.remove(true);
+		}
+	    }
+	};
+
+	Ext.apply(me, {
+	    items: [
+		realMatchFields,
+		realMode,
+		realMatchSeverity,
+		realInvertMatch,
+		realMatchCalendar,
+		treePanel,
+		{
+		    xtype: 'button',
+		    margin: '5 5 5 0',
+		    text: gettext('Add'),
+		    iconCls: 'fa fa-plus-circle',
+		    handler: addNode,
+		},
+		{
+		    xtype: 'button',
+		    margin: '5 5 5 0',
+		    text: gettext('Remove'),
+		    iconCls: 'fa fa-minus-circle',
+		    handler: deleteNode,
+		},
+	    ],
+	});
+	me.callParent();
+    },
+});
+
+Ext.define('PBS.panel.NotificationMatchRuleSettings', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pbsNotificationMatchRuleSettings',
+    mixins: ['Proxmox.Mixin.CBind'],
+    border: false,
+    layout: 'anchor',
+
+    items: [
+	{
+	    xtype: 'proxmoxKVComboBox',
+	    name: 'mode',
+	    fieldLabel: gettext('Match if'),
+	    allowBlank: false,
+	    isFormField: false,
+
+	    matchFieldWidth: false,
+
+	    comboItems: [
+		['all', gettext('All rules match')],
+		['any', gettext('Any rule matches')],
+		['notall', gettext('At least one rule does not match')],
+		['notany', gettext('No rule matches')],
+	    ],
+	    // Hide initially to avoid glitches when opening the window
+	    hidden: true,
+	    bind: {
+		hidden: '{!showMatchingMode}',
+		disabled: '{!showMatchingMode}',
+		value: '{rootMode}',
+	    },
+	},
+	{
+	    xtype: 'proxmoxKVComboBox',
+	    fieldLabel: gettext('Node type'),
+	    isFormField: false,
+	    allowBlank: false,
+	    // Hide initially to avoid glitches when opening the window
+	    hidden: true,
+	    bind: {
+		value: '{nodeType}',
+		hidden: '{!showMatcherType}',
+		disabled: '{!showMatcherType}',
+	    },
+
+	    comboItems: [
+		['match-field', gettext('Match Field')],
+		['match-severity', gettext('Match Severity')],
+		['match-calendar', gettext('Match Calendar')],
+	    ],
+	},
+	{
+	    xtype: 'pbsNotificationMatchFieldSettings',
+	    cbind: {
+		baseUrl: '{baseUrl}',
+	    },
+	},
+	{
+	    xtype: 'pbsNotificationMatchSeveritySettings',
+	},
+	{
+	    xtype: 'pbsNotificationMatchCalendarSettings',
+	},
+    ],
+});
+
+Ext.define('PBS.panel.MatchCalendarSettings', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pbsNotificationMatchCalendarSettings',
+    border: false,
+    layout: 'anchor',
+    // Hide initially to avoid glitches when opening the window
+    hidden: true,
+    bind: {
+	hidden: '{!typeIsMatchCalendar}',
+    },
+    viewModel: {
+	// parent is set in `initComponents`
+	formulas: {
+	    typeIsMatchCalendar: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		get: function(record) {
+		    return record?.get('type') === 'match-calendar';
+		},
+	    },
+
+	    matchCalendarValue: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		set: function(value) {
+		    let me = this;
+		    let record = me.get('selectedRecord');
+		    let currentData = record.get('data');
+		    record.set({
+			data: {
+			    ...currentData,
+			    value: value,
+			},
+		    });
+		},
+		get: function(record) {
+		    return record?.get('data')?.value;
+		},
+	    },
+	},
+    },
+    items: [
+	{
+	    xtype: 'proxmoxKVComboBox',
+	    fieldLabel: gettext('Timespan to match'),
+	    isFormField: false,
+	    allowBlank: false,
+	    editable: true,
+	    displayField: 'key',
+	    field: 'value',
+	    bind: {
+		value: '{matchCalendarValue}',
+		disabled: '{!typeIsMatchCalender}',
+	    },
+
+	    comboItems: [
+		['mon 8-12', ''],
+		['tue..fri,sun 0:00-23:59', ''],
+	    ],
+	},
+    ],
+
+    initComponent: function() {
+	let me = this;
+	Ext.apply(me.viewModel, {
+	    parent: me.up('pmxNotificationMatchRulesEditPanel').getViewModel(),
+	});
+	me.callParent();
+    },
+});
+
+Ext.define('PBS.panel.MatchSeveritySettings', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pbsNotificationMatchSeveritySettings',
+    border: false,
+    layout: 'anchor',
+    // Hide initially to avoid glitches when opening the window
+    hidden: true,
+    bind: {
+	hidden: '{!typeIsMatchSeverity}',
+    },
+    viewModel: {
+	// parent is set in `initComponents`
+	formulas: {
+	    typeIsMatchSeverity: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		get: function(record) {
+		    return record?.get('type') === 'match-severity';
+		},
+	    },
+	    matchSeverityValue: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		set: function(value) {
+		    let record = this.get('selectedRecord');
+		    let currentData = record.get('data');
+		    record.set({
+			data: {
+			    ...currentData,
+			    value: value,
+			},
+		    });
+		},
+		get: function(record) {
+		    return record?.get('data')?.value;
+		},
+	    },
+	},
+    },
+    items: [
+	{
+	    xtype: 'proxmoxKVComboBox',
+	    fieldLabel: gettext('Severities to match'),
+	    isFormField: false,
+	    allowBlank: true,
+	    multiSelect: true,
+	    field: 'value',
+	    // Hide initially to avoid glitches when opening the window
+	    hidden: true,
+	    bind: {
+		value: '{matchSeverityValue}',
+		hidden: '{!typeIsMatchSeverity}',
+		disabled: '{!typeIsMatchSeverity}',
+	    },
+
+	    comboItems: [
+		['info', gettext('Info')],
+		['notice', gettext('Notice')],
+		['warning', gettext('Warning')],
+		['error', gettext('Error')],
+		['unknown', gettext('Unknown')],
+	    ],
+	},
+    ],
+
+    initComponent: function() {
+	let me = this;
+	Ext.apply(me.viewModel, {
+	    parent: me.up('pmxNotificationMatchRulesEditPanel').getViewModel(),
+	});
+	me.callParent();
+    },
+});
+
+Ext.define('PBS.panel.MatchFieldSettings', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pbsNotificationMatchFieldSettings',
+    border: false,
+    layout: 'anchor',
+    // Hide initially to avoid glitches when opening the window
+    hidden: true,
+    bind: {
+	hidden: '{!typeIsMatchField}',
+    },
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	control: {
+	    'field[reference=fieldSelector]': {
+		change: function(field) {
+		    let view = this.getView();
+		    let valueField = view.down('field[reference=valueSelector]');
+		    let store = valueField.getStore();
+		    let val = field.getValue();
+
+		    if (val) {
+			store.setFilters([
+			    {
+				property: 'field',
+				value: val,
+			    },
+			]);
+		    }
+		},
+	    },
+	},
+    },
+    viewModel: {
+	// parent is set in `initComponents`
+	formulas: {
+	    typeIsMatchField: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		get: function(record) {
+		    return record?.get('type') === 'match-field';
+		},
+	    },
+	    isRegex: function(get) {
+		return get('matchFieldType') === 'regex';
+	    },
+	    matchFieldType: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		set: function(value) {
+		    let record = this.get('selectedRecord');
+		    let currentData = record.get('data');
+
+		    let newValue = [];
+
+		    // Build equivalent regular expression if switching
+		    // to 'regex' mode
+		    if (value === 'regex') {
+			let regexVal = "^";
+			if (currentData.value) {
+			    regexVal += `(${currentData.value.join('|')})`;
+			}
+			regexVal += "$";
+			newValue.push(regexVal);
+		    }
+
+		    record.set({
+			data: {
+			    ...currentData,
+			    type: value,
+			    value: newValue,
+			},
+		    });
+		},
+		get: function(record) {
+		    return record?.get('data')?.type;
+		},
+	    },
+	    matchFieldField: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		set: function(value) {
+		    let record = this.get('selectedRecord');
+		    let currentData = record.get('data');
+
+		    record.set({
+			data: {
+			    ...currentData,
+			    field: value,
+			    // Reset value if field changes
+			    value: [],
+			},
+		    });
+		},
+		get: function(record) {
+		    return record?.get('data')?.field;
+		},
+	    },
+	    matchFieldValue: {
+		bind: {
+		    bindTo: '{selectedRecord}',
+		    deep: true,
+		},
+		set: function(value) {
+		    let record = this.get('selectedRecord');
+		    let currentData = record.get('data');
+		    record.set({
+			data: {
+			    ...currentData,
+			    value: value,
+			},
+		    });
+		},
+		get: function(record) {
+		    return record?.get('data')?.value;
+		},
+	    },
+	},
+    },
+
+    initComponent: function() {
+	let me = this;
+
+	let store = Ext.create('Ext.data.Store', {
+	    model: 'pbs-notification-fields',
+	    autoLoad: true,
+	    proxy: {
+		type: 'proxmox',
+		url: `/api2/json/${me.baseUrl}/matcher-fields`,
+	    },
+	    listeners: {
+		'load': function() {
+		    this.each(function(record) {
+			record.set({
+			    description:
+				Proxmox.Utils.formatNotificationFieldName(
+				    record.get('name'),
+				),
+			});
+		    });
+
+		    // Commit changes so that the description field is not marked
+		    // as dirty
+		    this.commitChanges();
+		},
+	    },
+	});
+
+	let valueStore = Ext.create('Ext.data.Store', {
+	    model: 'pbs-notification-field-values',
+	    autoLoad: true,
+	    proxy: {
+		type: 'proxmox',
+
+		url: `/api2/json/${me.baseUrl}/matcher-field-values`,
+	    },
+	    listeners: {
+		'load': function() {
+		    this.each(function(record) {
+			if (record.get('field') === 'type') {
+			    record.set({
+				comment:
+				    Proxmox.Utils.formatNotificationFieldValue(
+					record.get('value'),
+				    ),
+			    });
+			}
+		    }, this, true);
+
+		    // Commit changes so that the description field is not marked
+		    // as dirty
+		    this.commitChanges();
+		},
+	    },
+	});
+
+	Ext.apply(me.viewModel, {
+	    parent: me.up('pmxNotificationMatchRulesEditPanel').getViewModel(),
+	});
+	Ext.apply(me, {
+	    items: [
+		{
+		    fieldLabel: gettext('Match Type'),
+		    xtype: 'proxmoxKVComboBox',
+		    reference: 'type',
+		    isFormField: false,
+		    allowBlank: false,
+		    submitValue: false,
+		    field: 'type',
+
+		    bind: {
+			value: '{matchFieldType}',
+		    },
+
+		    comboItems: [
+			['exact', gettext('Exact')],
+			['regex', gettext('Regex')],
+		    ],
+		},
+		{
+		    fieldLabel: gettext('Field'),
+		    reference: 'fieldSelector',
+		    xtype: 'proxmoxComboGrid',
+		    isFormField: false,
+		    submitValue: false,
+		    allowBlank: false,
+		    editable: false,
+		    store: store,
+		    queryMode: 'local',
+		    valueField: 'name',
+		    displayField: 'description',
+		    field: 'field',
+		    bind: {
+			value: '{matchFieldField}',
+		    },
+		    listConfig: {
+			columns: [
+			    {
+				header: gettext('Description'),
+				dataIndex: 'description',
+				flex: 2,
+			    },
+			    {
+				header: gettext('Field Name'),
+				dataIndex: 'name',
+				flex: 1,
+			    },
+			],
+		    },
+		},
+		{
+		    fieldLabel: gettext('Value'),
+		    reference: 'valueSelector',
+		    xtype: 'proxmoxComboGrid',
+		    autoSelect: false,
+		    editable: false,
+		    isFormField: false,
+		    submitValue: false,
+		    allowBlank: false,
+		    showClearTrigger: true,
+		    field: 'value',
+		    store: valueStore,
+		    valueField: 'value',
+		    displayField: 'value',
+		    notFoundIsValid: false,
+		    multiSelect: true,
+		    bind: {
+			value: '{matchFieldValue}',
+			hidden: '{isRegex}',
+		    },
+		    listConfig: {
+			columns: [
+			    {
+				header: gettext('Value'),
+				dataIndex: 'value',
+				flex: 1,
+			    },
+			    {
+				header: gettext('Comment'),
+				dataIndex: 'comment',
+				flex: 2,
+			    },
+			],
+		    },
+		},
+		{
+		    fieldLabel: gettext('Regex'),
+		    xtype: 'proxmoxtextfield',
+		    editable: true,
+		    isFormField: false,
+		    submitValue: false,
+		    allowBlank: false,
+		    field: 'value',
+		    bind: {
+			value: '{matchFieldValue}',
+			hidden: '{!isRegex}',
+		    },
+		},
+	    ],
+	});
+	me.callParent();
+    },
+});
-- 
2.39.2



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


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

* Re: [pbs-devel] [PATCH many v4 00/43] integrate notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (42 preceding siblings ...)
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 43/43] ui: notifications: pull in UX improvements for match rules creation Lukas Wagner
@ 2024-04-22 13:58 ` Maximiliano Sandoval
  2024-04-23 11:44   ` Lukas Wagner
  2024-04-22 14:24 ` Fabian Grünbichler
  44 siblings, 1 reply; 51+ messages in thread
From: Maximiliano Sandoval @ 2024-04-22 13:58 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

Lukas Wagner <l.wagner@proxmox.com> writes:

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

I tested v3 of this patch series.

Tested that the following things work as expected:
 - Error notification sent for ACME accounts
 - GC notification
 - verification notification
 - Legacy vs Notification system
 - Adding a matcher for successful prunes and receiving a prune notification

I already discussed this with Lukas off-list: When a notification is
queued and it does not match any matcher it should be logged, e.g. with
a message

```
proxmox-backup-proxy[PID]: discarding notification (id=NOTIFICATION_ID).
it does not match any matcher
```

otherwise the journal will only show:

```
Apr 22 11:19:50 pbs proxmox-backup-proxy[21341]: queued notification (id=5ae8da0a-afa7-4635-9d67-1645e6c47310)
```

and no reason of why such notification was never sent.


Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>

-- 
Maximiliano


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


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

* Re: [pbs-devel] [PATCH many v4 00/43] integrate notification system
  2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
                   ` (43 preceding siblings ...)
  2024-04-22 13:58 ` [pbs-devel] [PATCH many v4 00/43] integrate notification system Maximiliano Sandoval
@ 2024-04-22 14:24 ` Fabian Grünbichler
  2024-04-23 11:13   ` Lukas Wagner
  44 siblings, 1 reply; 51+ messages in thread
From: Fabian Grünbichler @ 2024-04-22 14:24 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

On April 22, 2024 2:37 pm, 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/test endpoint) 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 the UI, we set the parameter to 'notification-system'
>     and opt in into the new system by default. For the CLI/API we don't
>     so that we don't cause problems with any automations/scripts -
>     there I'd change the default to the new system with the next major release
> 
> Rough edges:
>   - 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.

in general this series looks mostly good to me, some rough edges I
noticed while testing:

- if the user lacks Sys.Audit, the notifications part is empty, even
  though there are built-in targets/matchers, this might cause
  complications for one-off tasks that allow selecting notification
  things, or cause confusion on the user's side
- the datastore/job related checks when configuring notifications
  currently don't allow access to datastores with just Datastore.Backup,
  and not .Audit, even though such datastores/jobs are fully accessible
  by the user..
- docs/config/notifications/format.rst has partly invalid content
- Sys.Modify is only contained in the Admin role atm, which has a lot of
  other privs - maybe we want to have our own NotificationsAdmin? not
  sure whether that makes sense.. maybe it would be better to think
  about this for a bit and maybe add a priv if we want to make this
  configurable by "medium privileged" users, since Sys.Modify is quite
  the heavy hammer anyway..
- as you said, the docs part for synopsis (is still) broken, this is a
  pre-existing issue though

I pushed the collection of patches for PBS:
- proxmox-widget-toolkit
- proxmox/proxmox-notify (including a bump of the package)
- proxmox-backup (including a bump of the proxmox-notify dep)

to my staff repo(s), so that interested people don't have to redo all
the application work ;)

> 
> Prerequisites:
> 
>   - This patch series requires the patches for 'proxmox' from
>   https://lists.proxmox.com/pipermail/pve-devel/2024-April/063493.html
>   to be applied first
> 
>   - 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")
> 
> Changes since v3:
>   - Pull in UI changes from 'notification metadata matching improvments' as
>     component overrides - this can and should be removed once the
>     widget toolkit patches are applied.
>   - no other changes apart from that
> 
> Changes since v2:
>   - consolidate verify/prune/sync/tape-job to job-id
>   - add a note to docs that package update notifications
>     are only sent by nodes with an active subscription
> 
> Changes since v1:
>   - proxmox_notify: Change default-matcher to exclude 'prune success' notifications
>   - proxmox_notify: Move sendmail/forward functions from proxmox_sys
>   - proxmox_notify: Improve docs for API types
>   - proxmox-widget-toolkit: allow to override default mail author
>   - Add documentation, man pages, etc. (mostly copied from PVE, some
>     stylistic touchups, some rephrasing)
>   - Add additional matchable metadata fields, allowing to match on
>     individual jobs:
>     - verification-job
>     - prune-job
>     - sync-job
>     - tape-job
>   - Sort queued notifications by timestamp before sending
>   - Fix missing hostname in package-update notification
>   - Change default mail author in the UI from Proxmox VE to Proxmox Backup Server ($hostname)
> 
> proxmox:
> 
> Lukas Wagner (5):
>   notify: expose `config` module
>   notify: use std::sync::OnceCell instead of lazy_static!
>   notify: pbs-context: exclude successful prunes in default matcher
>   notify: endpoints: matcher: improve descriptions for API types
>   notify: add getter for notification timestamp
> 
>  proxmox-notify/Cargo.toml                |  1 -
>  proxmox-notify/src/config.rs             | 23 ++++++++++++-------
>  proxmox-notify/src/context/pbs.rs        |  5 ++++-
>  proxmox-notify/src/endpoints/gotify.rs   |  6 ++---
>  proxmox-notify/src/endpoints/sendmail.rs | 15 ++++++++-----
>  proxmox-notify/src/endpoints/smtp.rs     | 28 +++++++++++++++---------
>  proxmox-notify/src/lib.rs                | 11 ++++++----
>  proxmox-notify/src/matcher.rs            | 18 +++++++--------
>  8 files changed, 66 insertions(+), 41 deletions(-)
> 
> 
> proxmox-widget-toolkit:
> 
> Lukas Wagner (1):
>   sendmail: smtp: allow to overide default mail author
> 
>  src/Schema.js                  | 9 +++++++++
>  src/panel/SendmailEditPanel.js | 2 +-
>  src/panel/SmtpEditPanel.js     | 2 +-
>  src/window/EndpointEditBase.js | 1 +
>  4 files changed, 12 insertions(+), 2 deletions(-)
> 
> 
> proxmox-backup:
> 
> Lukas Wagner (37):
>   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
>   docgen: generate synopsis for notifications{-priv,}.cfg
>   docs: add documentation for notification system
>   ui: util: override default mail author for sendmail/smtp targets
>   ui: notifications: pull in UX improvements for match rules creation
> 
>  Cargo.toml                                    |    3 +
>  Makefile                                      |    5 +-
>  debian/proxmox-backup-server.install          |   31 +
>  docs/Makefile                                 |    6 +-
>  docs/conf.py                                  |    2 +
>  docs/config/notifications-priv/format.rst     |    1 +
>  docs/config/notifications-priv/man5.rst       |   24 +
>  docs/config/notifications/format.rst          |   28 +
>  docs/config/notifications/man5.rst            |   24 +
>  docs/configuration-files.rst                  |   30 +
>  docs/index.rst                                |    1 +
>  docs/notifications.rst                        |  213 ++++
>  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          |  210 ++++
>  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/docgen.rs                             |    4 +
>  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                   |  566 +++++++++
>  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                                  |    2 +
>  www/NavigationTree.js                         |    6 +
>  www/OnlineHelpInfo.js                         |   24 +
>  www/Utils.js                                  |   39 +
>  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/NotificationMatcherOverride.js     | 1105 +++++++++++++++++
>  www/window/NotifyOptions.js                   |   38 +
>  92 files changed, 4174 insertions(+), 869 deletions(-)
>  create mode 100644 docs/config/notifications-priv/format.rst
>  create mode 100644 docs/config/notifications-priv/man5.rst
>  create mode 100644 docs/config/notifications/format.rst
>  create mode 100644 docs/config/notifications/man5.rst
>  create mode 100644 docs/notifications.rst
>  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
>  create mode 100644 www/window/NotificationMatcherOverride.js
> 
> 
> Summary over all repositories:
>   104 files changed, 4252 insertions(+), 912 deletions(-)
> 
> -- 
> Generated by git-murpp 0.7.1
> 
> 
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
> 
> 
> 


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


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

* Re: [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode
  2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode Lukas Wagner
@ 2024-04-22 14:32   ` Dominik Csapak
  2024-04-23  8:14     ` Lukas Wagner
  0 siblings, 1 reply; 51+ messages in thread
From: Dominik Csapak @ 2024-04-22 14:32 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Lukas Wagner

question

do we really want to add
---
notification-mode notification-system
---

to every new job config/api call?

since it seems to be the default when none is set, couldn't we
just leave it out of the config (at least when setting from the gui?)

comment inline:

both the question and comment is valid for the next patches too

On 4/22/24 14:38, Lukas Wagner wrote:
> Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
> Tested-by: Gabriel Goller <g.goller@proxmox.com>
> Reviewed-by: Gabriel Goller <g.goller@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}',
> +			},

if you do this, an edit on an entry where no value is set will
enable the 'reset' button and reset the field to the empty value

normally we use '__default__' to represent this default value
(and set it by default too)

> +		    },
>   		    {
>   			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}",
> +			},
>   		    },
>   		],
>   



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


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

* Re: [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode
  2024-04-22 14:32   ` Dominik Csapak
@ 2024-04-23  8:14     ` Lukas Wagner
  2024-04-23  8:16       ` Dominik Csapak
  0 siblings, 1 reply; 51+ messages in thread
From: Lukas Wagner @ 2024-04-23  8:14 UTC (permalink / raw)
  To: Dominik Csapak, Proxmox Backup Server development discussion

Thanks for the feedback, greatly appreciated!

On  2024-04-22 16:32, Dominik Csapak wrote:
> question
> 
> do we really want to add
> ---
> notification-mode notification-system
> ---
> 
> to every new job config/api call?
> 
> since it seems to be the default when none is set, couldn't we
> just leave it out of the config (at least when setting from the gui?)

The default for this option is 'sendmail-legacy' as to not change behavior for 
existing datastores. I do not feel comfortable switching
over existing datastores from the old system (configured by 'notify-user' and 'notify' as
a per-datastore setting) to the new automatically (that would imply automatically
generating targets/matchers)

For new datastores created via the UI we opt in by default.

With the next major release, we could switch the default or even remove
the legacy behavior entirely.

Side note: In PVE we have this whole 'Auto' mode, that uses the legacy
system if an email is configured for a backup job, and the new system if not.
That created quite a bit confusion for our users, so I wanted to keep it
simpler in PBS.

> 
> comment inline:
> 
> both the question and comment is valid for the next patches too
> 
> On 4/22/24 14:38, Lukas Wagner wrote:
>> Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
>> Tested-by: Gabriel Goller <g.goller@proxmox.com>
>> Reviewed-by: Gabriel Goller <g.goller@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}',
>> +            },
> 
> if you do this, an edit on an entry where no value is set will
> enable the 'reset' button and reset the field to the empty value
> 
> normally we use '__default__' to represent this default value
> (and set it by default too)

Okay, thanks! Will introduce a __default__ choice here.
> 
>> +            },
>>               {
>>               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}",
>> +            },
>>               },
>>           ],
>>   
> 
> 

-- 
- 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] 51+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode
  2024-04-23  8:14     ` Lukas Wagner
@ 2024-04-23  8:16       ` Dominik Csapak
  0 siblings, 0 replies; 51+ messages in thread
From: Dominik Csapak @ 2024-04-23  8:16 UTC (permalink / raw)
  To: Lukas Wagner, Proxmox Backup Server development discussion

On 4/23/24 10:14, Lukas Wagner wrote:
> Thanks for the feedback, greatly appreciated!
> 
> On  2024-04-22 16:32, Dominik Csapak wrote:
>> question
>>
>> do we really want to add
>> ---
>> notification-mode notification-system
>> ---
>>
>> to every new job config/api call?
>>
>> since it seems to be the default when none is set, couldn't we
>> just leave it out of the config (at least when setting from the gui?)
> 
> The default for this option is 'sendmail-legacy' as to not change behavior for
> existing datastores. I do not feel comfortable switching
> over existing datastores from the old system (configured by 'notify-user' and 'notify' as
> a per-datastore setting) to the new automatically (that would imply automatically
> generating targets/matchers)
> 
> For new datastores created via the UI we opt in by default.

but as it is now, editing an existing value will automatically convert
to the notification system if one is not careful and simply presses
ok again after opening

so having the empty state explicitly in the kvcombobox with it's
meaning would be good

> 
> With the next major release, we could switch the default or even remove
> the legacy behavior entirely.
> 
> Side note: In PVE we have this whole 'Auto' mode, that uses the legacy
> system if an email is configured for a backup job, and the new system if not.
> That created quite a bit confusion for our users, so I wanted to keep it
> simpler in PBS.
> 
>>
>> comment inline:
>>
>> both the question and comment is valid for the next patches too
>>
>> On 4/22/24 14:38, Lukas Wagner wrote:
>>> Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
>>> Tested-by: Gabriel Goller <g.goller@proxmox.com>
>>> Reviewed-by: Gabriel Goller <g.goller@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}',
>>> +            },
>>
>> if you do this, an edit on an entry where no value is set will
>> enable the 'reset' button and reset the field to the empty value
>>
>> normally we use '__default__' to represent this default value
>> (and set it by default too)
> 
> Okay, thanks! Will introduce a __default__ choice here.
>>
>>> +            },
>>>                {
>>>                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}",
>>> +            },
>>>                },
>>>            ],
>>>    
>>
>>
> 



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

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

* Re: [pbs-devel] [PATCH many v4 00/43] integrate notification system
  2024-04-22 14:24 ` Fabian Grünbichler
@ 2024-04-23 11:13   ` Lukas Wagner
  0 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-23 11:13 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Fabian Grünbichler



On  2024-04-22 16:24, Fabian Grünbichler wrote:
>> Rough edges:
>>   - 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.
> 
> in general this series looks mostly good to me, some rough edges I
> noticed while testing:
> 
> - if the user lacks Sys.Audit, the notifications part is empty, even
>   though there are built-in targets/matchers, this might cause
>   complications for one-off tasks that allow selecting notification
>   things, or cause confusion on the user's side

One-of tasks/jobs only allow selecting the notification mode, so whether
to use the old 'email to some user' style or the new notification system,
but not any details. So I think it should be fine? Unless I'm missing something.

It would of course be nice to fail a bit more gracefully when privs are missing,
e.g. hide the notifications panel completely if .Audit is missing, or disable
the 'modify' buttons if .Modify is missing... but I think this should be done later,
this potentially requires coordination with PVE.

> - the datastore/job related checks when configuring notifications
>   currently don't allow access to datastores with just Datastore.Backup,
>   and not .Audit, even though such datastores/jobs are fully accessible
>   by the user..

Fixed - the API handler which returns the list of known metadata values now
uses the /admin/datastore API instead of /config/datastore.
The first one requires Datastore.Backup/Datastore.Audit, while the latter
only returns datastores for which the users has at least Datastore.Audit.

> - docs/config/notifications/format.rst has partly invalid content

Fixed - copy/paste mistake :)

> - Sys.Modify is only contained in the Admin role atm, which has a lot of
>   other privs - maybe we want to have our own NotificationsAdmin? not
>   sure whether that makes sense.. maybe it would be better to think
>   about this for a bit and maybe add a priv if we want to make this
>   configurable by "medium privileged" users, since Sys.Modify is quite
>   the heavy hammer anyway..

Good point. I mean on PVE we use Mapping.* and therefore can use
the PVEMapping* roles to to administer notifications, which is 
more of a 'medium sized hammer' ;) . On PBS I did not
find anything more fitting than Sys.*, so maybe it would make sense to
introduce something new, either only a new role or also new privs.

Not sure if we have to do this now or can do this at a later point?
Any way, I'm open for suggestions. :)


-- 
- 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] 51+ messages in thread

* Re: [pbs-devel] [PATCH many v4 00/43] integrate notification system
  2024-04-22 13:58 ` [pbs-devel] [PATCH many v4 00/43] integrate notification system Maximiliano Sandoval
@ 2024-04-23 11:44   ` Lukas Wagner
  0 siblings, 0 replies; 51+ messages in thread
From: Lukas Wagner @ 2024-04-23 11:44 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Maximiliano Sandoval

Thanks for testing!

On  2024-04-22 15:58, Maximiliano Sandoval wrote:
> 
> I already discussed this with Lukas off-list: When a notification is
> queued and it does not match any matcher it should be logged, e.g. with
> a message
> 
> ```
> proxmox-backup-proxy[PID]: discarding notification (id=NOTIFICATION_ID).
> it does not match any matcher
> ```

Agreed, but I think this is better suited for a follow up.


-- 
- 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] 51+ messages in thread

end of thread, other threads:[~2024-04-23 11:44 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-22 12:37 [pbs-devel] [PATCH many v4 00/43] integrate notification system Lukas Wagner
2024-04-22 12:37 ` [pbs-devel] [PATCH proxmox v4 01/43] notify: expose `config` module Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 02/43] notify: use std::sync::OnceCell instead of lazy_static! Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 03/43] notify: pbs-context: exclude successful prunes in default matcher Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 04/43] notify: endpoints: matcher: improve descriptions for API types Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox v4 05/43] notify: add getter for notification timestamp Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH widget-toolkit v4 06/43] sendmail: smtp: allow to overide default mail author Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 07/43] pbs-config: add module for loading notification config Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 08/43] server: rename email_notifications module to notifications Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 09/43] notifications: allow sending notifications via proxmox_notify Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 10/43] buildsys: install templates for test notifications Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 11/43] pbs-config: acl: add /system/notifications as known ACL path Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 12/43] api: add endpoints for querying/testing notification targets Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 13/43] api: add endpoints for notification matchers Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 14/43] api: add endpoints for sendmail targets Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 15/43] api: add endpoints for smtp targets Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 16/43] api: add endpoints for gotify targets Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 17/43] api: add endpoints for querying known notification values/fields Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 18/43] api-types: api: datatore: add notification-mode parameter Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 19/43] api-types: api: tape: " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 20/43] server: notifications: send GC notifications via notification system Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 21/43] server: notifications: send prune " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 22/43] server: notifications: send verify " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 23/43] server: notifications: send sync " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 24/43] server: notifications: send update " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 25/43] server: notifications: send acme " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 26/43] server: notifications: send tape " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 27/43] ui: add notification config panel Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 28/43] ui: tape backup job: add selector for notification-mode Lukas Wagner
2024-04-22 14:32   ` Dominik Csapak
2024-04-23  8:14     ` Lukas Wagner
2024-04-23  8:16       ` Dominik Csapak
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 29/43] ui: tape backup: add selector for 'notification-mode' Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 30/43] ui: tape restore: add 'notification-mode' parameter Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 31/43] ui: datastore options: " Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 32/43] ui: utils: add overrides for known notification metadata fields/values Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 33/43] ui: datastore edit: make new stores use notification system by default Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 34/43] ui: permissions paths: add /system/notifications to combobox Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 35/43] proxmox-backup-manager: add CLI for notification targets Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 36/43] proxmox-backup-manager: add CLI for notification matchers Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 37/43] proxmox-backup-manager: add CLI for gotify endpoints Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 38/43] proxmox-backup-manager: add CLI for sendmail endpoints Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 39/43] proxmox-backup-manager: add CLI for SMTP endpoints Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 40/43] docgen: generate synopsis for notifications{-priv, }.cfg Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 41/43] docs: add documentation for notification system Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 42/43] ui: util: override default mail author for sendmail/smtp targets Lukas Wagner
2024-04-22 12:38 ` [pbs-devel] [PATCH proxmox-backup v4 43/43] ui: notifications: pull in UX improvements for match rules creation Lukas Wagner
2024-04-22 13:58 ` [pbs-devel] [PATCH many v4 00/43] integrate notification system Maximiliano Sandoval
2024-04-23 11:44   ` Lukas Wagner
2024-04-22 14:24 ` Fabian Grünbichler
2024-04-23 11:13   ` 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