4 use handlebars
::{Handlebars, Helper, Context, RenderError, RenderContext, Output, HelperResult}
;
6 use proxmox
::tools
::email
::sendmail
;
9 config
::verify
::VerificationJobConfig
,
10 config
::sync
::SyncJobConfig
,
13 GarbageCollectionStatus
,
15 tools
::format
::HumanByte
,
18 const GC_OK_TEMPLATE
: &str = r
###"
20 Datastore: {{datastore}}
21 Task ID: {{status.upid}}
22 Index file count: {{status.index-file-count}}
24 Removed garbage: {{human-bytes status.removed-bytes}}
25 Removed chunks: {{status.removed-chunks}}
26 Remove bad files: {{status.removed-bad}}
28 Bad files: {{status.still-bad}}
29 Pending removals: {{human-bytes status.pending-bytes}} (in {{status.pending-chunks}} chunks)
31 Original Data usage: {{human-bytes status.index-data-bytes}}
32 On Disk usage: {{human-bytes status.disk-bytes}} ({{relative-percentage status.disk-bytes status.index-data-bytes}})
33 On Disk chunks: {{status.disk-chunks}}
35 Deduplication Factor: {{deduplication-factor}}
37 Garbage collection successful.
42 const GC_ERR_TEMPLATE
: &str = r
###"
44 Datastore: {{datastore}}
46 Garbage collection failed: {{error}}
50 const VERIFY_OK_TEMPLATE
: &str = r
###"
53 Datastore: {{job.store}}
55 Verification successful.
59 const VERIFY_ERR_TEMPLATE
: &str = r
###"
62 Datastore: {{job.store}}
64 Verification failed on these snapshots:
72 const SYNC_OK_TEMPLATE
: &str = r
###"
75 Datastore: {{job.store}}
76 Remote: {{job.remote}}
77 Remote Store: {{job.remote-store}}
79 Synchronization successful.
83 const SYNC_ERR_TEMPLATE
: &str = r
###"
86 Datastore: {{job.store}}
87 Remote: {{job.remote}}
88 Remote Store: {{job.remote-store}}
90 Synchronization failed: {{error}}
94 lazy_static
::lazy_static
!{
96 static ref HANDLEBARS
: Handlebars
<'
static> = {
97 let mut hb
= Handlebars
::new();
99 hb
.set_strict_mode(true);
101 hb
.register_helper("human-bytes", Box
::new(handlebars_humam_bytes_helper
));
102 hb
.register_helper("relative-percentage", Box
::new(handlebars_relative_percentage_helper
));
104 hb
.register_template_string("gc_ok_template", GC_OK_TEMPLATE
).unwrap();
105 hb
.register_template_string("gc_err_template", GC_ERR_TEMPLATE
).unwrap();
107 hb
.register_template_string("verify_ok_template", VERIFY_OK_TEMPLATE
).unwrap();
108 hb
.register_template_string("verify_err_template", VERIFY_ERR_TEMPLATE
).unwrap();
110 hb
.register_template_string("sync_ok_template", SYNC_OK_TEMPLATE
).unwrap();
111 hb
.register_template_string("sync_err_template", SYNC_ERR_TEMPLATE
).unwrap();
117 fn send_job_status_mail(
121 ) -> Result
<(), Error
> {
123 // Note: OX has serious problems displaying text mails,
124 // so we include html as well
125 let html
= format
!("<html><body><pre>\n{}\n<pre>", handlebars
::html_escape(text
));
127 let nodename
= proxmox
::tools
::nodename();
129 let author
= format
!("Proxmox Backup Server - {}", nodename
);
143 pub fn send_gc_status(
146 status
: &GarbageCollectionStatus
,
147 result
: &Result
<(), Error
>,
148 ) -> Result
<(), Error
> {
150 let text
= match result
{
152 let deduplication_factor
= if status
.disk_bytes
> 0 {
153 (status
.index_data_bytes
as f64)/(status
.disk_bytes
as f64)
160 "datastore": datastore
,
161 "deduplication-factor": format
!("{:.2}", deduplication_factor
),
164 HANDLEBARS
.render("gc_ok_template", &data
)?
168 "error": err
.to_string(),
169 "datastore": datastore
,
171 HANDLEBARS
.render("gc_err_template", &data
)?
175 let subject
= match result
{
177 "Garbage Collect Datastore '{}' successful",
181 "Garbage Collect Datastore '{}' failed",
186 send_job_status_mail(email
, &subject
, &text
)?
;
191 pub fn send_verify_status(
193 job
: VerificationJobConfig
,
194 result
: &Result
<Vec
<String
>, Error
>,
195 ) -> Result
<(), Error
> {
198 let text
= match result
{
199 Ok(errors
) if errors
.is_empty() => {
200 let data
= json
!({ "job": job }
);
201 HANDLEBARS
.render("verify_ok_template", &data
)?
204 let data
= json
!({ "job": job, "errors": errors }
);
205 HANDLEBARS
.render("verify_err_template", &data
)?
208 // aboreted job - do not send any email
213 let subject
= match result
{
214 Ok(errors
) if errors
.is_empty() => format
!(
215 "Verify Datastore '{}' successful",
219 "Verify Datastore '{}' failed",
224 send_job_status_mail(email
, &subject
, &text
)?
;
229 pub fn send_sync_status(
232 result
: &Result
<(), Error
>,
233 ) -> Result
<(), Error
> {
235 let text
= match result
{
237 let data
= json
!({ "job": job }
);
238 HANDLEBARS
.render("sync_ok_template", &data
)?
241 let data
= json
!({ "job": job, "error": err.to_string() }
);
242 HANDLEBARS
.render("sync_err_template", &data
)?
246 let subject
= match result
{
248 "Sync remote '{}' datastore '{}' successful",
253 "Sync remote '{}' datastore '{}' failed",
259 send_job_status_mail(email
, &subject
, &text
)?
;
264 /// Lookup users email address
266 /// For "backup@pam", this returns the address from "root@pam".
267 pub fn lookup_user_email(userid
: &Userid
) -> Option
<String
> {
269 use crate::config
::user
::{self, User}
;
271 if userid
== Userid
::backup_userid() {
272 return lookup_user_email(Userid
::root_userid());
275 if let Ok(user_config
) = user
::cached_config() {
276 if let Ok(user
) = user_config
.lookup
::<User
>("user", userid
.as_str()) {
277 return user
.email
.clone();
284 // Handlerbar helper functions
286 fn handlebars_humam_bytes_helper(
290 _rc
: &mut RenderContext
,
293 let param
= h
.param(0).map(|v
| v
.value().as_u64())
295 .ok_or(RenderError
::new("human-bytes: param not found"))?
;
297 out
.write(&HumanByte
::from(param
).to_string())?
;
302 fn handlebars_relative_percentage_helper(
306 _rc
: &mut RenderContext
,
309 let param0
= h
.param(0).map(|v
| v
.value().as_f64())
311 .ok_or(RenderError
::new("relative-percentage: param0 not found"))?
;
312 let param1
= h
.param(1).map(|v
| v
.value().as_f64())
314 .ok_or(RenderError
::new("relative-percentage: param1 not found"))?
;
319 out
.write(&format
!("{:.2}%", (param0
*100.0)/param1
))?
;