]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/sync.rs
api2/admin/sync: use JobState for faster access to state info
[proxmox-backup.git] / src / api2 / config / sync.rs
1 use anyhow::{bail, Error};
2 use serde_json::Value;
3 use ::serde::{Deserialize, Serialize};
4
5 use proxmox::api::{api, Router, RpcEnvironment};
6 use proxmox::tools::fs::open_file_locked;
7
8 use crate::api2::types::*;
9 use crate::config::sync::{self, SyncJobConfig};
10
11 // fixme: add access permissions
12
13 #[api(
14 input: {
15 properties: {},
16 },
17 returns: {
18 description: "List configured jobs.",
19 type: Array,
20 items: { type: sync::SyncJobConfig },
21 },
22 )]
23 /// List all sync jobs
24 pub fn list_sync_jobs(
25 _param: Value,
26 mut rpcenv: &mut dyn RpcEnvironment,
27 ) -> Result<Vec<SyncJobConfig>, Error> {
28
29 let (config, digest) = sync::config()?;
30
31 let list = config.convert_to_typed_array("sync")?;
32
33 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
34
35 Ok(list)
36 }
37
38 #[api(
39 protected: true,
40 input: {
41 properties: {
42 id: {
43 schema: JOB_ID_SCHEMA,
44 },
45 store: {
46 schema: DATASTORE_SCHEMA,
47 },
48 remote: {
49 schema: REMOTE_ID_SCHEMA,
50 },
51 "remote-store": {
52 schema: DATASTORE_SCHEMA,
53 },
54 "remove-vanished": {
55 schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
56 optional: true,
57 },
58 comment: {
59 optional: true,
60 schema: SINGLE_LINE_COMMENT_SCHEMA,
61 },
62 schedule: {
63 optional: true,
64 schema: SYNC_SCHEDULE_SCHEMA,
65 },
66 },
67 },
68 )]
69 /// Create a new sync job.
70 pub fn create_sync_job(param: Value) -> Result<(), Error> {
71
72 let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
73
74 let sync_job: sync::SyncJobConfig = serde_json::from_value(param.clone())?;
75
76 let (mut config, _digest) = sync::config()?;
77
78 if let Some(_) = config.sections.get(&sync_job.id) {
79 bail!("job '{}' already exists.", sync_job.id);
80 }
81
82 config.set_data(&sync_job.id, "sync", &sync_job)?;
83
84 sync::save_config(&config)?;
85
86 Ok(())
87 }
88
89 #[api(
90 input: {
91 properties: {
92 id: {
93 schema: JOB_ID_SCHEMA,
94 },
95 },
96 },
97 returns: {
98 description: "The sync job configuration.",
99 type: sync::SyncJobConfig,
100 },
101 )]
102 /// Read a sync job configuration.
103 pub fn read_sync_job(
104 id: String,
105 mut rpcenv: &mut dyn RpcEnvironment,
106 ) -> Result<SyncJobConfig, Error> {
107 let (config, digest) = sync::config()?;
108
109 let sync_job = config.lookup("sync", &id)?;
110 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
111
112 Ok(sync_job)
113 }
114
115 #[api()]
116 #[derive(Serialize, Deserialize)]
117 #[serde(rename_all="kebab-case")]
118 #[allow(non_camel_case_types)]
119 /// Deletable property name
120 pub enum DeletableProperty {
121 /// Delete the comment property.
122 comment,
123 /// Delete the job schedule.
124 schedule,
125 /// Delete the remove-vanished flag.
126 remove_vanished,
127 }
128
129 #[api(
130 protected: true,
131 input: {
132 properties: {
133 id: {
134 schema: JOB_ID_SCHEMA,
135 },
136 store: {
137 schema: DATASTORE_SCHEMA,
138 optional: true,
139 },
140 remote: {
141 schema: REMOTE_ID_SCHEMA,
142 optional: true,
143 },
144 "remote-store": {
145 schema: DATASTORE_SCHEMA,
146 optional: true,
147 },
148 "remove-vanished": {
149 schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
150 optional: true,
151 },
152 comment: {
153 optional: true,
154 schema: SINGLE_LINE_COMMENT_SCHEMA,
155 },
156 schedule: {
157 optional: true,
158 schema: SYNC_SCHEDULE_SCHEMA,
159 },
160 delete: {
161 description: "List of properties to delete.",
162 type: Array,
163 optional: true,
164 items: {
165 type: DeletableProperty,
166 }
167 },
168 digest: {
169 optional: true,
170 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
171 },
172 },
173 },
174 )]
175 /// Update sync job config.
176 pub fn update_sync_job(
177 id: String,
178 store: Option<String>,
179 remote: Option<String>,
180 remote_store: Option<String>,
181 remove_vanished: Option<bool>,
182 comment: Option<String>,
183 schedule: Option<String>,
184 delete: Option<Vec<DeletableProperty>>,
185 digest: Option<String>,
186 ) -> Result<(), Error> {
187
188 let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
189
190 // pass/compare digest
191 let (mut config, expected_digest) = sync::config()?;
192
193 if let Some(ref digest) = digest {
194 let digest = proxmox::tools::hex_to_digest(digest)?;
195 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
196 }
197
198 let mut data: sync::SyncJobConfig = config.lookup("sync", &id)?;
199
200 if let Some(delete) = delete {
201 for delete_prop in delete {
202 match delete_prop {
203 DeletableProperty::comment => { data.comment = None; },
204 DeletableProperty::schedule => { data.schedule = None; },
205 DeletableProperty::remove_vanished => { data.remove_vanished = None; },
206 }
207 }
208 }
209
210 if let Some(comment) = comment {
211 let comment = comment.trim().to_string();
212 if comment.is_empty() {
213 data.comment = None;
214 } else {
215 data.comment = Some(comment);
216 }
217 }
218
219 if let Some(store) = store { data.store = store; }
220 if let Some(remote) = remote { data.remote = remote; }
221 if let Some(remote_store) = remote_store { data.remote_store = remote_store; }
222
223
224 if schedule.is_some() { data.schedule = schedule; }
225 if remove_vanished.is_some() { data.remove_vanished = remove_vanished; }
226
227 config.set_data(&id, "sync", &data)?;
228
229 sync::save_config(&config)?;
230
231 Ok(())
232 }
233
234 #[api(
235 protected: true,
236 input: {
237 properties: {
238 id: {
239 schema: JOB_ID_SCHEMA,
240 },
241 digest: {
242 optional: true,
243 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
244 },
245 },
246 },
247 )]
248 /// Remove a sync job configuration
249 pub fn delete_sync_job(id: String, digest: Option<String>) -> Result<(), Error> {
250
251 let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
252
253 let (mut config, expected_digest) = sync::config()?;
254
255 if let Some(ref digest) = digest {
256 let digest = proxmox::tools::hex_to_digest(digest)?;
257 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
258 }
259
260 match config.sections.get(&id) {
261 Some(_) => { config.sections.remove(&id); },
262 None => bail!("job '{}' does not exist.", id),
263 }
264
265 sync::save_config(&config)?;
266
267 crate::config::jobstate::remove_state_file("syncjob", &id)?;
268
269 Ok(())
270 }
271
272 const ITEM_ROUTER: Router = Router::new()
273 .get(&API_METHOD_READ_SYNC_JOB)
274 .put(&API_METHOD_UPDATE_SYNC_JOB)
275 .delete(&API_METHOD_DELETE_SYNC_JOB);
276
277 pub const ROUTER: Router = Router::new()
278 .get(&API_METHOD_LIST_SYNC_JOBS)
279 .post(&API_METHOD_CREATE_SYNC_JOB)
280 .match_all("id", &ITEM_ROUTER);