]> git.proxmox.com Git - proxmox.git/blame - proxmox-notify/src/context/pbs.rs
notify: pbs-context: exclude successful prunes in default matcher
[proxmox.git] / proxmox-notify / src / context / pbs.rs
CommitLineData
c1a3505e 1use serde::Deserialize;
1516cc26 2use std::path::Path;
c1a3505e
LW
3
4use proxmox_schema::{ObjectSchema, Schema, StringSchema};
5use proxmox_section_config::{SectionConfig, SectionConfigPlugin};
6
7use crate::context::{common, Context};
1516cc26 8use crate::Error;
c1a3505e
LW
9
10const PBS_USER_CFG_FILENAME: &str = "/etc/proxmox-backup/user.cfg";
11const PBS_NODE_CFG_FILENAME: &str = "/etc/proxmox-backup/node.cfg";
12
13// FIXME: Switch to the actual schema when possible in terms of dependency.
14// It's safe to assume that the config was written with the actual schema restrictions, so parsing
15// it with the less restrictive schema should be enough for the purpose of getting the mail address.
16const DUMMY_ID_SCHEMA: Schema = StringSchema::new("dummy ID").min_length(3).schema();
17const DUMMY_EMAIL_SCHEMA: Schema = StringSchema::new("dummy email").schema();
18const DUMMY_USER_SCHEMA: ObjectSchema = ObjectSchema {
19 description: "minimal PBS user",
20 properties: &[
21 ("userid", false, &DUMMY_ID_SCHEMA),
22 ("email", true, &DUMMY_EMAIL_SCHEMA),
23 ],
24 additional_properties: true,
25 default_key: None,
26};
27
28#[derive(Deserialize)]
29struct DummyPbsUser {
30 pub email: Option<String>,
31}
32
33/// Extract the root user's email address from the PBS user config.
34fn lookup_mail_address(content: &str, username: &str) -> Option<String> {
35 let mut config = SectionConfig::new(&DUMMY_ID_SCHEMA).allow_unknown_sections(true);
36 let user_plugin = SectionConfigPlugin::new(
37 "user".to_string(),
38 Some("userid".to_string()),
39 &DUMMY_USER_SCHEMA,
40 );
41 config.register_plugin(user_plugin);
42
43 match config.parse(PBS_USER_CFG_FILENAME, content) {
44 Ok(parsed) => {
45 parsed.sections.get(username)?;
46 match parsed.lookup::<DummyPbsUser>("user", username) {
47 Ok(user) => common::normalize_for_return(user.email.as_deref()),
48 Err(err) => {
49 log::error!("unable to parse {PBS_USER_CFG_FILENAME}: {err}");
50 None
51 }
52 }
53 }
54 Err(err) => {
55 log::error!("unable to parse {PBS_USER_CFG_FILENAME}: {err}");
56 None
57 }
58 }
59}
60
9bea76c6
LW
61const DEFAULT_CONFIG: &str = "\
62sendmail: mail-to-root
63 comment Send mails to root@pam's email address
64 mailto-user root@pam
65
66
67matcher: default-matcher
68 mode all
b2000d1f
LW
69 invert-match true
70 match-field exact:type=prune
71 match-severity info
9bea76c6 72 target mail-to-root
b2000d1f 73 comment Route everything but successful prune job notifications to mail-to-root
9bea76c6
LW
74";
75
c1a3505e
LW
76#[derive(Debug)]
77pub struct PBSContext;
78
79pub static PBS_CONTEXT: PBSContext = PBSContext;
80
81impl Context for PBSContext {
82 fn lookup_email_for_user(&self, user: &str) -> Option<String> {
83 let content = common::attempt_file_read(PBS_USER_CFG_FILENAME);
84 content.and_then(|content| lookup_mail_address(&content, user))
85 }
86
87 fn default_sendmail_author(&self) -> String {
803bf7cd 88 format!("Proxmox Backup Server - {}", proxmox_sys::nodename())
c1a3505e
LW
89 }
90
91 fn default_sendmail_from(&self) -> String {
92 let content = common::attempt_file_read(PBS_NODE_CFG_FILENAME);
93 content
94 .and_then(|content| common::lookup_datacenter_config_key(&content, "email-from"))
95 .unwrap_or_else(|| String::from("root"))
96 }
97
98 fn http_proxy_config(&self) -> Option<String> {
99 let content = common::attempt_file_read(PBS_NODE_CFG_FILENAME);
100 content.and_then(|content| common::lookup_datacenter_config_key(&content, "http-proxy"))
101 }
9bea76c6
LW
102
103 fn default_config(&self) -> &'static str {
104 return DEFAULT_CONFIG;
105 }
1516cc26
LW
106
107 fn lookup_template(
108 &self,
109 filename: &str,
110 namespace: Option<&str>,
111 ) -> Result<Option<String>, Error> {
112 let path = Path::new("/usr/share/proxmox-backup/templates")
113 .join(namespace.unwrap_or("default"))
114 .join(filename);
115
116 let template_string = proxmox_sys::fs::file_read_optional_string(path)
117 .map_err(|err| Error::Generic(format!("could not load template: {err}")))?;
118 Ok(template_string)
119 }
c1a3505e
LW
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 const USER_CONFIG: &str = "
127user: root@pam
128 email root@example.com
129
130user: test@pbs
131 enable true
132 expire 0
133 ";
134
135 #[test]
136 fn test_parse_mail() {
137 assert_eq!(
138 lookup_mail_address(USER_CONFIG, "root@pam"),
139 Some("root@example.com".to_string())
140 );
141 assert_eq!(lookup_mail_address(USER_CONFIG, "test@pbs"), None);
142 }
143
144 const NODE_CONFIG: &str = "
145default-lang: de
146email-from: root@example.com
147http-proxy: http://localhost:1234
148 ";
149
150 #[test]
151 fn test_parse_node_config() {
152 assert_eq!(
153 common::lookup_datacenter_config_key(NODE_CONFIG, "email-from"),
154 Some("root@example.com".to_string())
155 );
156 assert_eq!(
157 common::lookup_datacenter_config_key(NODE_CONFIG, "http-proxy"),
158 Some("http://localhost:1234".to_string())
159 );
160 assert_eq!(
161 common::lookup_datacenter_config_key(NODE_CONFIG, "foo"),
162 None
163 );
164 }
165}