]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/tape_backup_job.rs
tape: add 'latest-only' property to backup job config
[proxmox-backup.git] / src / api2 / config / tape_backup_job.rs
1 use anyhow::{bail, format_err, Error};
2 use serde_json::Value;
3 use ::serde::{Deserialize, Serialize};
4
5 use proxmox::api::{api, Router, RpcEnvironment, schema::Updatable};
6 use proxmox::tools::fs::open_file_locked;
7
8 use crate::{
9 api2::types::{
10 JOB_ID_SCHEMA,
11 PROXMOX_CONFIG_DIGEST_SCHEMA,
12 },
13 config::{
14 self,
15 tape_job::{
16 TAPE_JOB_CFG_LOCKFILE,
17 TapeBackupJobConfig,
18 TapeBackupJobConfigUpdater,
19 }
20 },
21 };
22
23 #[api(
24 input: {
25 properties: {},
26 },
27 returns: {
28 description: "List configured jobs.",
29 type: Array,
30 items: { type: TapeBackupJobConfig },
31 },
32 )]
33 /// List all tape backup jobs
34 pub fn list_tape_backup_jobs(
35 _param: Value,
36 mut rpcenv: &mut dyn RpcEnvironment,
37 ) -> Result<Vec<TapeBackupJobConfig>, Error> {
38
39 let (config, digest) = config::tape_job::config()?;
40
41 let list = config.convert_to_typed_array("backup")?;
42
43 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
44
45 Ok(list)
46 }
47
48 #[api(
49 protected: true,
50 input: {
51 properties: {
52 job: {
53 type: TapeBackupJobConfig,
54 flatten: true,
55 },
56 },
57 },
58 )]
59 /// Create a new tape backup job.
60 pub fn create_tape_backup_job(
61 job: TapeBackupJobConfig,
62 _rpcenv: &mut dyn RpcEnvironment,
63 ) -> Result<(), Error> {
64
65 let _lock = open_file_locked(TAPE_JOB_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
66
67 let (mut config, _digest) = config::tape_job::config()?;
68
69 if config.sections.get(&job.id).is_some() {
70 bail!("job '{}' already exists.", job.id);
71 }
72
73 config.set_data(&job.id, "backup", &job)?;
74
75 config::tape_job::save_config(&config)?;
76
77 crate::server::jobstate::create_state_file("tape-backup-job", &job.id)?;
78
79 Ok(())
80 }
81
82 #[api(
83 input: {
84 properties: {
85 id: {
86 schema: JOB_ID_SCHEMA,
87 },
88 },
89 },
90 returns: { type: TapeBackupJobConfig },
91 )]
92 /// Read a tape backup job configuration.
93 pub fn read_tape_backup_job(
94 id: String,
95 mut rpcenv: &mut dyn RpcEnvironment,
96 ) -> Result<TapeBackupJobConfig, Error> {
97
98 let (config, digest) = config::tape_job::config()?;
99
100 let job = config.lookup("backup", &id)?;
101
102 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
103
104 Ok(job)
105 }
106
107 #[api()]
108 #[derive(Serialize, Deserialize)]
109 #[serde(rename_all="kebab-case")]
110 /// Deletable property name
111 pub enum DeletableProperty {
112 /// Delete the comment property.
113 Comment,
114 /// Delete the job schedule.
115 Schedule,
116 /// Delete the eject-media property
117 EjectMedia,
118 /// Delete the export-media-set property
119 ExportMediaSet,
120 /// Delete the 'latest-only' property
121 LatestOnly,
122 }
123
124 #[api(
125 protected: true,
126 input: {
127 properties: {
128 update: {
129 flatten: true,
130 type: TapeBackupJobConfigUpdater,
131 },
132 delete: {
133 description: "List of properties to delete.",
134 type: Array,
135 optional: true,
136 items: {
137 type: DeletableProperty,
138 }
139 },
140 digest: {
141 optional: true,
142 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
143 },
144 },
145 },
146 )]
147 /// Update the tape backup job
148 pub fn update_tape_backup_job(
149 mut update: TapeBackupJobConfigUpdater,
150 delete: Option<Vec<String>>,
151 digest: Option<String>,
152 ) -> Result<(), Error> {
153 let _lock = open_file_locked(TAPE_JOB_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
154
155 let id = update.id.take().ok_or_else(|| format_err!("no id given"))?;
156
157 let (mut config, expected_digest) = config::tape_job::config()?;
158
159 let mut job: TapeBackupJobConfig = config.lookup("backup", &id)?;
160
161 if let Some(ref digest) = digest {
162 let digest = proxmox::tools::hex_to_digest(digest)?;
163 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
164 }
165
166 job.update_from(update, &delete.unwrap_or(Vec::new()))?;
167
168 config.set_data(&job.id, "backup", &job)?;
169
170 config::tape_job::save_config(&config)?;
171
172 Ok(())
173 }
174
175 #[api(
176 protected: true,
177 input: {
178 properties: {
179 id: {
180 schema: JOB_ID_SCHEMA,
181 },
182 digest: {
183 optional: true,
184 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
185 },
186 },
187 },
188 )]
189 /// Remove a tape backup job configuration
190 pub fn delete_tape_backup_job(
191 id: String,
192 digest: Option<String>,
193 _rpcenv: &mut dyn RpcEnvironment,
194 ) -> Result<(), Error> {
195 let _lock = open_file_locked(TAPE_JOB_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
196
197 let (mut config, expected_digest) = config::tape_job::config()?;
198
199 if let Some(ref digest) = digest {
200 let digest = proxmox::tools::hex_to_digest(digest)?;
201 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
202 }
203
204 match config.lookup::<TapeBackupJobConfig>("backup", &id) {
205 Ok(_job) => {
206 config.sections.remove(&id);
207 },
208 Err(_) => { bail!("job '{}' does not exist.", id) },
209 };
210
211 config::tape_job::save_config(&config)?;
212
213 crate::server::jobstate::remove_state_file("tape-backup-job", &id)?;
214
215 Ok(())
216 }
217
218 const ITEM_ROUTER: Router = Router::new()
219 .get(&API_METHOD_READ_TAPE_BACKUP_JOB)
220 .put(&API_METHOD_UPDATE_TAPE_BACKUP_JOB)
221 .delete(&API_METHOD_DELETE_TAPE_BACKUP_JOB);
222
223 pub const ROUTER: Router = Router::new()
224 .get(&API_METHOD_LIST_TAPE_BACKUP_JOBS)
225 .post(&API_METHOD_CREATE_TAPE_BACKUP_JOB)
226 .match_all("id", &ITEM_ROUTER);