]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/sync.rs
depend on proxmox 0.4.2
[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), true)?;
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 crate::config::jobstate::create_state_file("syncjob", &sync_job.id)?;
87
88 Ok(())
89 }
90
91 #[api(
92 input: {
93 properties: {
94 id: {
95 schema: JOB_ID_SCHEMA,
96 },
97 },
98 },
99 returns: {
100 description: "The sync job configuration.",
101 type: sync::SyncJobConfig,
102 },
103 )]
104 /// Read a sync job configuration.
105 pub fn read_sync_job(
106 id: String,
107 mut rpcenv: &mut dyn RpcEnvironment,
108 ) -> Result<SyncJobConfig, Error> {
109 let (config, digest) = sync::config()?;
110
111 let sync_job = config.lookup("sync", &id)?;
112 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
113
114 Ok(sync_job)
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,
160 schema: SYNC_SCHEDULE_SCHEMA,
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 )]
177 /// Update sync job config.
178 pub fn update_sync_job(
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
190 let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
191
192 // pass/compare digest
193 let (mut config, expected_digest) = sync::config()?;
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
200 let mut data: sync::SyncJobConfig = config.lookup("sync", &id)?;
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
229 config.set_data(&id, "sync", &data)?;
230
231 sync::save_config(&config)?;
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 )]
250 /// Remove a sync job configuration
251 pub fn delete_sync_job(id: String, digest: Option<String>) -> Result<(), Error> {
252
253 let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
254
255 let (mut config, expected_digest) = sync::config()?;
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
267 sync::save_config(&config)?;
268
269 crate::config::jobstate::remove_state_file("syncjob", &id)?;
270
271 Ok(())
272 }
273
274 const ITEM_ROUTER: Router = Router::new()
275 .get(&API_METHOD_READ_SYNC_JOB)
276 .put(&API_METHOD_UPDATE_SYNC_JOB)
277 .delete(&API_METHOD_DELETE_SYNC_JOB);
278
279 pub const ROUTER: Router = Router::new()
280 .get(&API_METHOD_LIST_SYNC_JOBS)
281 .post(&API_METHOD_CREATE_SYNC_JOB)
282 .match_all("id", &ITEM_ROUTER);