]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/admin/sync.rs
clippy: remove unnecessary closures
[proxmox-backup.git] / src / api2 / admin / sync.rs
CommitLineData
59af9ca9 1use anyhow::{bail, format_err, Error};
d43f86f3 2use serde_json::Value;
d43f86f3 3
59af9ca9 4use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment};
d43f86f3
DC
5use proxmox::api::router::SubdirMap;
6use proxmox::{list_subdirs_api_method, sortable};
7
8use crate::api2::types::*;
42b68f72 9use crate::api2::pull::do_sync_job;
59af9ca9
FG
10use crate::api2::config::sync::{check_sync_job_modify_access, check_sync_job_read_access};
11
12use crate::config::cached_user_info::CachedUserInfo;
d43f86f3 13use crate::config::sync::{self, SyncJobStatus, SyncJobConfig};
664d8a27 14use crate::server::UPID;
1298618a 15use crate::server::jobstate::{Job, JobState};
d43f86f3
DC
16use crate::tools::systemd::time::{
17 parse_calendar_event, compute_next_event};
18
19#[api(
20 input: {
d58e6313
DC
21 properties: {
22 store: {
23 schema: DATASTORE_SCHEMA,
24 optional: true,
25 },
26 },
d43f86f3
DC
27 },
28 returns: {
29 description: "List configured jobs and their status.",
30 type: Array,
31 items: { type: sync::SyncJobStatus },
32 },
59af9ca9
FG
33 access: {
34 description: "Limited to sync jobs where user has Datastore.Audit on target datastore, and Remote.Audit on source remote.",
35 permission: &Permission::Anybody,
36 },
d43f86f3
DC
37)]
38/// List all sync jobs
39pub fn list_sync_jobs(
d58e6313 40 store: Option<String>,
d43f86f3
DC
41 _param: Value,
42 mut rpcenv: &mut dyn RpcEnvironment,
43) -> Result<Vec<SyncJobStatus>, Error> {
44
59af9ca9
FG
45 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
46 let user_info = CachedUserInfo::new()?;
47
d43f86f3
DC
48 let (config, digest) = sync::config()?;
49
d58e6313
DC
50 let mut list: Vec<SyncJobStatus> = config
51 .convert_to_typed_array("sync")?
52 .into_iter()
53 .filter(|job: &SyncJobStatus| {
54 if let Some(store) = &store {
55 &job.store == store
56 } else {
57 true
58 }
59af9ca9
FG
59 })
60 .filter(|job: &SyncJobStatus| {
44288184 61 let as_config: SyncJobConfig = job.into();
59af9ca9 62 check_sync_job_read_access(&user_info, &auth_id, &as_config)
d58e6313 63 }).collect();
d43f86f3 64
664d8a27
DC
65 for job in &mut list {
66 let last_state = JobState::load("syncjob", &job.id)
67 .map_err(|err| format_err!("could not open statefile for {}: {}", &job.id, err))?;
68 let (upid, endtime, state, starttime) = match last_state {
69 JobState::Created { time } => (None, None, None, time),
70 JobState::Started { upid } => {
71 let parsed_upid: UPID = upid.parse()?;
72 (Some(upid), None, None, parsed_upid.starttime)
73 },
77bd2a46 74 JobState::Finished { upid, state } => {
664d8a27 75 let parsed_upid: UPID = upid.parse()?;
77bd2a46 76 (Some(upid), Some(state.endtime()), Some(state.to_string()), parsed_upid.starttime)
664d8a27 77 },
d43f86f3 78 };
d43f86f3 79
664d8a27
DC
80 job.last_run_upid = upid;
81 job.last_run_state = state;
82 job.last_run_endtime = endtime;
83
22a9189e 84 let last = job.last_run_endtime.unwrap_or(starttime);
8d785899
DC
85
86 job.next_run = (|| -> Option<i64> {
87 let schedule = job.schedule.as_ref()?;
88 let event = parse_calendar_event(&schedule).ok()?;
15ec790a 89 // ignore errors
22a9189e 90 compute_next_event(&event, last, false).unwrap_or(None)
8d785899 91 })();
d43f86f3
DC
92 }
93
94 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
95
96 Ok(list)
97}
98
99#[api(
100 input: {
101 properties: {
102 id: {
103 schema: JOB_ID_SCHEMA,
104 }
105 }
59af9ca9
FG
106 },
107 access: {
108 description: "User needs Datastore.Backup on target datastore, and Remote.Read on source remote. Additionally, remove_vanished requires Datastore.Prune, and any owner other than the user themselves requires Datastore.Modify",
109 permission: &Permission::Anybody,
110 },
d43f86f3
DC
111)]
112/// Runs the sync jobs manually.
42b68f72 113fn run_sync_job(
d43f86f3
DC
114 id: String,
115 _info: &ApiMethod,
116 rpcenv: &mut dyn RpcEnvironment,
117) -> Result<String, Error> {
59af9ca9
FG
118 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
119 let user_info = CachedUserInfo::new()?;
d43f86f3
DC
120
121 let (config, _digest) = sync::config()?;
122 let sync_job: SyncJobConfig = config.lookup("sync", &id)?;
123
59af9ca9
FG
124 if !check_sync_job_modify_access(&user_info, &auth_id, &sync_job) {
125 bail!("permission check failed");
126 }
d43f86f3 127
93bb51fe 128 let job = Job::new("syncjob", &id)?;
02543a5c 129
e6dc35ac 130 let upid_str = do_sync_job(job, sync_job, &auth_id, None)?;
d43f86f3
DC
131
132 Ok(upid_str)
133}
134
135#[sortable]
136const SYNC_INFO_SUBDIRS: SubdirMap = &[
137 (
138 "run",
139 &Router::new()
140 .post(&API_METHOD_RUN_SYNC_JOB)
141 ),
142];
143
144const SYNC_INFO_ROUTER: Router = Router::new()
145 .get(&list_subdirs_api_method!(SYNC_INFO_SUBDIRS))
146 .subdirs(SYNC_INFO_SUBDIRS);
147
148
149pub const ROUTER: Router = Router::new()
150 .get(&API_METHOD_LIST_SYNC_JOBS)
151 .match_all("id", &SYNC_INFO_ROUTER);