1 use anyhow
::{bail, Error}
;
3 use ::serde
::{Deserialize, Serialize}
;
5 use proxmox
::api
::{api, Router, RpcEnvironment}
;
6 use proxmox
::tools
::fs
::open_file_locked
;
8 use crate::api2
::types
::*;
9 use crate::config
::sync
::{self, SyncJobConfig}
;
11 // fixme: add access permissions
18 description
: "List configured jobs.",
20 items
: { type: sync::SyncJobConfig }
,
23 /// List all sync jobs
24 pub fn list_sync_jobs(
26 mut rpcenv
: &mut dyn RpcEnvironment
,
27 ) -> Result
<Vec
<SyncJobConfig
>, Error
> {
29 let (config
, digest
) = sync
::config()?
;
31 let list
= config
.convert_to_typed_array("sync")?
;
33 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
43 schema
: JOB_ID_SCHEMA
,
46 schema
: DATASTORE_SCHEMA
,
49 schema
: REMOTE_ID_SCHEMA
,
52 schema
: DATASTORE_SCHEMA
,
55 schema
: REMOVE_VANISHED_BACKUPS_SCHEMA
,
60 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
64 schema
: SYNC_SCHEDULE_SCHEMA
,
69 /// Create a new sync job.
70 pub fn create_sync_job(param
: Value
) -> Result
<(), Error
> {
72 let _lock
= open_file_locked(sync
::SYNC_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
74 let sync_job
: sync
::SyncJobConfig
= serde_json
::from_value(param
.clone())?
;
76 let (mut config
, _digest
) = sync
::config()?
;
78 if let Some(_
) = config
.sections
.get(&sync_job
.id
) {
79 bail
!("job '{}' already exists.", sync_job
.id
);
82 config
.set_data(&sync_job
.id
, "sync", &sync_job
)?
;
84 sync
::save_config(&config
)?
;
86 crate::config
::jobstate
::create_state_file("syncjob", &sync_job
.id
)?
;
95 schema
: JOB_ID_SCHEMA
,
100 description
: "The sync job configuration.",
101 type: sync
::SyncJobConfig
,
104 /// Read a sync job configuration.
105 pub fn read_sync_job(
107 mut rpcenv
: &mut dyn RpcEnvironment
,
108 ) -> Result
<SyncJobConfig
, Error
> {
109 let (config
, digest
) = sync
::config()?
;
111 let sync_job
= config
.lookup("sync", &id
)?
;
112 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
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.
125 /// Delete the job schedule.
127 /// Delete the remove-vanished flag.
136 schema
: JOB_ID_SCHEMA
,
139 schema
: DATASTORE_SCHEMA
,
143 schema
: REMOTE_ID_SCHEMA
,
147 schema
: DATASTORE_SCHEMA
,
151 schema
: REMOVE_VANISHED_BACKUPS_SCHEMA
,
156 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
160 schema
: SYNC_SCHEDULE_SCHEMA
,
163 description
: "List of properties to delete.",
167 type: DeletableProperty
,
172 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
177 /// Update sync job config.
178 pub fn update_sync_job(
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
> {
190 let _lock
= open_file_locked(sync
::SYNC_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
192 // pass/compare digest
193 let (mut config
, expected_digest
) = sync
::config()?
;
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
)?
;
200 let mut data
: sync
::SyncJobConfig
= config
.lookup("sync", &id
)?
;
202 if let Some(delete
) = delete
{
203 for delete_prop
in delete
{
205 DeletableProperty
::comment
=> { data.comment = None; }
,
206 DeletableProperty
::schedule
=> { data.schedule = None; }
,
207 DeletableProperty
::remove_vanished
=> { data.remove_vanished = None; }
,
212 if let Some(comment
) = comment
{
213 let comment
= comment
.trim().to_string();
214 if comment
.is_empty() {
217 data
.comment
= Some(comment
);
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; }
226 if schedule
.is_some() { data.schedule = schedule; }
227 if remove_vanished
.is_some() { data.remove_vanished = remove_vanished; }
229 config
.set_data(&id
, "sync", &data
)?
;
231 sync
::save_config(&config
)?
;
241 schema
: JOB_ID_SCHEMA
,
245 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
250 /// Remove a sync job configuration
251 pub fn delete_sync_job(id
: String
, digest
: Option
<String
>) -> Result
<(), Error
> {
253 let _lock
= open_file_locked(sync
::SYNC_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
255 let (mut config
, expected_digest
) = sync
::config()?
;
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
)?
;
262 match config
.sections
.get(&id
) {
263 Some(_
) => { config.sections.remove(&id); }
,
264 None
=> bail
!("job '{}' does not exist.", id
),
267 sync
::save_config(&config
)?
;
269 crate::config
::jobstate
::remove_state_file("syncjob", &id
)?
;
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
);
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
);