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