From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 42EF51FF0AA for ; Tue, 22 Sep 2026 15:45:18 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 0F302214E5; Tue, 22 Sep 2026 15:45:13 +0200 (CEST) Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Tue, 22 Sep 2026 15:45:06 +0200 Message-Id: Subject: Re: [PATCH proxmox 14/29] notify: migrate PBS's and PVE's default matcher to expression syntax From: "Lukas Wagner" To: "Lukas Wagner" , "Arthur Bied-Charreton" X-Mailer: aerc 0.21.0-0-g5549850facc2-dirty References: <20260709115716.299836-1-l.wagner@proxmox.com> <20260709115716.299836-15-l.wagner@proxmox.com> In-Reply-To: X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1790084706791 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.461 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 3YIWONZVZB5RJKHM6AFWSRPHZUR3ABZI X-Message-ID-Hash: 3YIWONZVZB5RJKHM6AFWSRPHZUR3ABZI X-MailFrom: l.wagner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: pbs-devel@lists.proxmox.com, pve-devel@lists.proxmox.com X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On Tue Sep 22, 2026 at 3:40 PM CEST, Lukas Wagner wrote: > On Fri Jul 24, 2026 at 8:47 AM CEST, Arthur Bied-Charreton wrote: >> On Thu, Jul 09, 2026 at 01:57:01PM +0200, Lukas Wagner wrote: >>> Instead of providing the default matcher expression as a JSON blob, we >>> programmatically construct the appropricate SectionConfigData entity. >>> This required a change of the Context trait, now the default_config >>> method returns &'static SectionConfigData instead of &'static str. >>>=20 >>> The actual SectionConfigData entity is only constructed once and then >>> stored in a OnceLock. >>>=20 >>> A nice side-effect of this change is that the config-parsing code path >>> is more efficient, since we don't have to deserialize the default confi= g >>> over and over again. >>>=20 >> one comment inline=20 >>> Signed-off-by: Lukas Wagner >>> --- >>> proxmox-notify/src/context/mod.rs | 4 +- >>> proxmox-notify/src/context/pbs.rs | 87 ++++++++++++++++++++------ >>> proxmox-notify/src/context/pve.rs | 67 +++++++++++++++----- >>> proxmox-notify/src/context/test.rs | 9 ++- >>> proxmox-notify/src/lib.rs | 6 +- >>> proxmox-notify/src/matcher/calendar.rs | 9 ++- >>> proxmox-notify/src/matcher/field.rs | 11 +++- >>> proxmox-notify/src/matcher/severity.rs | 10 ++- >>> 8 files changed, 157 insertions(+), 46 deletions(-) >>>=20 >>> diff --git a/proxmox-notify/src/context/mod.rs b/proxmox-notify/src/con= text/mod.rs >>> index 87a2a716..70c3a567 100644 >>> --- a/proxmox-notify/src/context/mod.rs >>> +++ b/proxmox-notify/src/context/mod.rs >>> @@ -1,6 +1,8 @@ >>> use std::fmt::Debug; >>> use std::sync::Mutex; >>> =20 >>> +use proxmox_section_config::SectionConfigData; >>> + >>> use crate::Error; >>> use crate::renderer::TemplateSource; >>> =20 >>> @@ -24,7 +26,7 @@ pub trait Context: Send + Sync + Debug { >>> /// Proxy configuration for the current node >>> fn http_proxy_config(&self) -> Option; >>> /// Return default config for built-in targets/matchers. >>> - fn default_config(&self) -> &'static str; >>> + fn default_config(&self) -> &'static SectionConfigData; >>> /// Return the path of `filename` from `source` and a certain (opt= ional) `namespace` >>> fn lookup_template( >>> &self, >>> diff --git a/proxmox-notify/src/context/pbs.rs b/proxmox-notify/src/con= text/pbs.rs >>> index a9121548..b2561ad1 100644 >>> --- a/proxmox-notify/src/context/pbs.rs >>> +++ b/proxmox-notify/src/context/pbs.rs >>> @@ -1,14 +1,19 @@ >>> use std::path::Path; >>> +use std::sync::OnceLock; >>> =20 >>> use serde::Deserialize; >>> use tracing::error; >>> =20 >>> use proxmox_schema::{ObjectSchema, Schema, StringSchema}; >>> -use proxmox_section_config::{SectionConfig, SectionConfigPlugin}; >>> +use proxmox_section_config::{SectionConfig, SectionConfigData, Section= ConfigPlugin}; >>> =20 >>> -use crate::Error; >>> use crate::context::{Context, common}; >>> +use crate::endpoints::sendmail::{SENDMAIL_TYPENAME, SendmailConfig}; >>> +use crate::matcher::field::FieldMatcher; >>> +use crate::matcher::severity::SeverityMatcher; >>> +use crate::matcher::{MATCHER_TYPENAME, MatcherConfig}; >>> use crate::renderer::TemplateSource; >>> +use crate::{Error, Severity}; >>> =20 >>> const PBS_USER_CFG_FILENAME: &str =3D "/etc/proxmox-backup/user.cfg"; >>> const PBS_NODE_CFG_FILENAME: &str =3D "/etc/proxmox-backup/node.cfg"; >>> @@ -60,21 +65,6 @@ fn lookup_mail_address(content: &str, username: &str= ) -> Option { >>> } >>> } >>> =20 >>> -const DEFAULT_CONFIG: &str =3D "\ >>> -sendmail: mail-to-root >>> - comment Send mails to root@pam's email address >>> - mailto-user root@pam >>> - >>> - >>> -matcher: default-matcher >>> - mode all >>> - invert-match true >>> - match-field exact:type=3Dprune >>> - match-severity info >>> - target mail-to-root >>> - comment Route everything but successful prune job notifications to= mail-to-root >>> -"; >>> - >>> #[derive(Debug)] >>> pub struct PBSContext; >>> =20 >>> @@ -102,8 +92,67 @@ impl Context for PBSContext { >>> content.and_then(|content| common::lookup_datacenter_config_ke= y(&content, "http-proxy")) >>> } >>> =20 >>> - fn default_config(&self) -> &'static str { >>> - DEFAULT_CONFIG >>> + fn default_config(&self) -> &'static SectionConfigData { >>> + static DEFAULT_CONFIG: OnceLock =3D OnceLoc= k::new(); >>> + DEFAULT_CONFIG.get_or_init(|| { >>> + // FIXME: Long-term we want to move the trait implementati= on to the product, >>> + // maybe add some nice builder to construct the default co= nfig. >>> + // Moving this as-is to the product would expose a lot of = internals >>> + // to the product. >>> + >>> + let mut config =3D SectionConfigData::default(); >>> + config >>> + .set_data( >>> + "mail-to-root", >>> + SENDMAIL_TYPENAME, >>> + SendmailConfig { >>> + name: "mail-to-root".into(), >>> + mailto_user: vec!["root@pam".into()], >>> + comment: Some("Send mails to root@pam's email = address".into()), >>> + ..Default::default() >>> + }, >>> + ) >>> + .expect("failed to set 'mail-to-root' in default confi= g"); >>> + >>> + use proxmox_match_expression::{not, any_of, all_of}; >>> + >>> + let expression =3D any_of![ >>> + not!( >>> + FieldMatcher::Exact { >>> + field: "type".into(), >>> + values: vec!["prune".into()], >>> + }.into() >>> + ), >>> + all_of![ >>> + FieldMatcher::Exact { >>> + field: "type".into(), >>> + values: vec!["prune".into()], >>> + }.into(), >>> + SeverityMatcher { >>> + severities: vec![Severity::Error, Severity::Wa= rning], >>> + }.into(), >>> + ], >>> + ]; >>> + >>> + let expression =3D serde_json::to_string(&expression) >>> + .expect("failed serialize expression for 'default-matc= her'"); >>> + >>> + config >>> + .set_data( >>> + "default-matcher", >>> + MATCHER_TYPENAME, >>> + MatcherConfig { >>> + name: "default-matcher".into(), >>> + expression: Some(expression), >>> + target: vec!["mail-to-root".into()], >>> + comment: Some("Route everything but successful= prune job notifications to mail-to-root".into()), >>> + ..Default::default() >>> + }, >>> + ) >>> + .expect("failed to set 'default-matcher' in default co= nfig"); >>> + >>> + config >>> + }) >>> } >> afaict the default changes from 'suppress prune info notifications' >> to 'for prune notifications, only notify on error or warning'. not >> very familiar with the notifications sent by PBS, i assume prune jobs >> never send notice-level notifications?=20 > > Indeed, we actually don't use 'notice' at all at the moment. I think > including 'notice' in the default policy is actually the right thing to > do, even this is a change from the previous behavior. Now, as I said, > this does not change anything for any existing notifications. Got confused for a second, the change here actually *excludes* notice now, which is not what we want. I'll change the policy in v2. Then again, it does not have any effect right now, since we never emit this severity anyhow... > > I will document the slight change in the commit message! >>> =20 >>> fn lookup_template( >> [...]