Skip to main content

isb_apps/volume_backup/
schedule.rs

1//! Scheduled snapshots on the daemon's one scheduler thread.
2
3use crate::jobs::RunTrigger;
4use crate::jobs::scheduler::{Entry, Scheduled};
5
6use super::{SnapshotName, VolumeBackups};
7
8impl Scheduled for VolumeBackups {
9    fn entries(&self) -> Vec<Entry> {
10        let mut out = Vec::new();
11        for org in crate::jobs::orgs(&self.inner.state) {
12            for name in self.configured(&org) {
13                let Ok(Some(r)) = self.record(&org, &name) else {
14                    continue;
15                };
16                if !r.settings.enabled {
17                    continue;
18                }
19                let (Ok(Some(schedule)), Ok(grace)) = (
20                    r.settings.schedule(),
21                    crate::jobs::parse_grace(&r.settings.missed_grace),
22                ) else {
23                    continue;
24                };
25                out.push(Entry {
26                    org: org.clone(),
27                    name,
28                    schedule,
29                    anchor: r.anchor,
30                    grace,
31                });
32            }
33        }
34        out
35    }
36
37    fn fire(&self, e: &Entry, slot: i64, late: bool) {
38        {
39            let _g = self.inner.edit.lock().unwrap();
40            let Ok(Some(mut r)) = self.record(&e.org, &e.name) else {
41                return;
42            };
43            if r.anchor >= slot {
44                return;
45            }
46            r.anchor = slot;
47            if let Err(err) = self.save(&e.org, &e.name, &r) {
48                eprintln!("isb serve: volume {}: {err}", e.name);
49                return;
50            }
51        }
52        let trigger = if late {
53            RunTrigger::Missed
54        } else {
55            RunTrigger::Schedule
56        };
57        let r = self.snapshot(
58            &e.org,
59            &e.name,
60            SnapshotName::Auto,
61            (trigger, "schedule", Some(slot)),
62        );
63        if let Err(err) = r {
64            // The volume is gone or incus refused: on the record, not lost.
65            let store = self.runs(&e.org, &e.name);
66            if let Ok((mut run, mut log)) =
67                store.start("snapshot", trigger, "schedule", Some(slot), 50)
68            {
69                log.line(&format!("isb: {err}"));
70                run.error = Some(err.to_string());
71                run.finish(crate::jobs::RunStatus::Failed);
72                let _ = store.finish(&mut run, &mut log);
73            }
74            self.event(
75                &e.org,
76                &e.name,
77                "volume.snapshot.failed",
78                "error",
79                format!("snapshot of {} failed: {err}", e.name),
80            );
81        }
82    }
83
84    fn advance(&self, e: &Entry, to: i64) {
85        let _g = self.inner.edit.lock().unwrap();
86        if let Ok(Some(mut r)) = self.record(&e.org, &e.name) {
87            r.anchor = to;
88            let _ = self.save(&e.org, &e.name, &r);
89        }
90    }
91}