]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/drive.rs
2840fe8f6123db6d0a36cd3b6fdfd0792c2dde0a
[proxmox-backup.git] / src / api2 / config / drive.rs
1 use anyhow::{bail, Error};
2 use ::serde::{Deserialize, Serialize};
3 use serde_json::Value;
4
5 use proxmox_router::{Router, RpcEnvironment, Permission};
6 use proxmox_schema::api;
7
8 use pbs_api_types::{
9 Authid, LtoTapeDrive, LtoTapeDriveUpdater, ScsiTapeChanger,
10 PROXMOX_CONFIG_DIGEST_SCHEMA, DRIVE_NAME_SCHEMA, PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
11 };
12 use pbs_config::CachedUserInfo;
13
14 use pbs_tape::linux_list_drives::{lto_tape_device_list, check_drive_path};
15
16 #[api(
17 protected: true,
18 input: {
19 properties: {
20 config: {
21 type: LtoTapeDrive,
22 flatten: true,
23 },
24 },
25 },
26 access: {
27 permission: &Permission::Privilege(&["tape", "device"], PRIV_TAPE_MODIFY, false),
28 },
29 )]
30 /// Create a new drive
31 pub fn create_drive(config: LtoTapeDrive) -> Result<(), Error> {
32
33 let _lock = pbs_config::drive::lock()?;
34
35 let (mut section_config, _digest) = pbs_config::drive::config()?;
36
37 let lto_drives = lto_tape_device_list();
38
39 check_drive_path(&lto_drives, &config.path)?;
40
41 let existing: Vec<LtoTapeDrive> = section_config.convert_to_typed_array("lto")?;
42
43 for drive in existing {
44 if drive.name == config.name {
45 bail!("Entry '{}' already exists", config.name);
46 }
47 if drive.path == config.path {
48 bail!("Path '{}' already used in drive '{}'", config.path, drive.name);
49 }
50 }
51
52 section_config.set_data(&config.name, "lto", &config)?;
53
54 pbs_config::drive::save_config(&section_config)?;
55
56 Ok(())
57 }
58
59 #[api(
60 input: {
61 properties: {
62 name: {
63 schema: DRIVE_NAME_SCHEMA,
64 },
65 },
66 },
67 returns: {
68 type: LtoTapeDrive,
69 },
70 access: {
71 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT, false),
72 },
73 )]
74 /// Get drive configuration
75 pub fn get_config(
76 name: String,
77 _param: Value,
78 mut rpcenv: &mut dyn RpcEnvironment,
79 ) -> Result<LtoTapeDrive, Error> {
80
81 let (config, digest) = pbs_config::drive::config()?;
82
83 let data: LtoTapeDrive = config.lookup("lto", &name)?;
84
85 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
86
87 Ok(data)
88 }
89
90 #[api(
91 input: {
92 properties: {},
93 },
94 returns: {
95 description: "The list of configured drives (with config digest).",
96 type: Array,
97 items: {
98 type: LtoTapeDrive,
99 },
100 },
101 access: {
102 description: "List configured tape drives filtered by Tape.Audit privileges",
103 permission: &Permission::Anybody,
104 },
105 )]
106 /// List drives
107 pub fn list_drives(
108 _param: Value,
109 mut rpcenv: &mut dyn RpcEnvironment,
110 ) -> Result<Vec<LtoTapeDrive>, Error> {
111 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
112 let user_info = CachedUserInfo::new()?;
113
114 let (config, digest) = pbs_config::drive::config()?;
115
116 let drive_list: Vec<LtoTapeDrive> = config.convert_to_typed_array("lto")?;
117
118 let drive_list = drive_list
119 .into_iter()
120 .filter(|drive| {
121 let privs = user_info.lookup_privs(&auth_id, &["tape", "device", &drive.name]);
122 privs & PRIV_TAPE_AUDIT != 0
123 })
124 .collect();
125
126 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
127
128 Ok(drive_list)
129 }
130
131 #[api()]
132 #[derive(Serialize, Deserialize)]
133 #[allow(non_camel_case_types)]
134 #[serde(rename_all = "kebab-case")]
135 /// Deletable property name
136 pub enum DeletableProperty {
137 /// Delete the changer property.
138 changer,
139 /// Delete the changer-drivenum property.
140 changer_drivenum,
141 }
142
143 #[api(
144 protected: true,
145 input: {
146 properties: {
147 name: {
148 schema: DRIVE_NAME_SCHEMA,
149 },
150 update: {
151 type: LtoTapeDriveUpdater,
152 flatten: true,
153 },
154 delete: {
155 description: "List of properties to delete.",
156 type: Array,
157 optional: true,
158 items: {
159 type: DeletableProperty,
160 }
161 },
162 digest: {
163 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
164 optional: true,
165 },
166 },
167 },
168 access: {
169 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY, false),
170 },
171 )]
172 /// Update a drive configuration
173 pub fn update_drive(
174 name: String,
175 update: LtoTapeDriveUpdater,
176 delete: Option<Vec<DeletableProperty>>,
177 digest: Option<String>,
178 _param: Value,
179 ) -> Result<(), Error> {
180
181 let _lock = pbs_config::drive::lock()?;
182
183 let (mut config, expected_digest) = pbs_config::drive::config()?;
184
185 if let Some(ref digest) = digest {
186 let digest = proxmox::tools::hex_to_digest(digest)?;
187 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
188 }
189
190 let mut data: LtoTapeDrive = config.lookup("lto", &name)?;
191
192 if let Some(delete) = delete {
193 for delete_prop in delete {
194 match delete_prop {
195 DeletableProperty::changer => {
196 data.changer = None;
197 data.changer_drivenum = None;
198 },
199 DeletableProperty::changer_drivenum => { data.changer_drivenum = None; },
200 }
201 }
202 }
203
204 if let Some(path) = update.path {
205 let lto_drives = lto_tape_device_list();
206 check_drive_path(&lto_drives, &path)?;
207 data.path = path;
208 }
209
210 if let Some(changer) = update.changer {
211 let _: ScsiTapeChanger = config.lookup("changer", &changer)?;
212 data.changer = Some(changer);
213 }
214
215 if let Some(changer_drivenum) = update.changer_drivenum {
216 if changer_drivenum == 0 {
217 data.changer_drivenum = None;
218 } else {
219 if data.changer.is_none() {
220 bail!("Option 'changer-drivenum' requires option 'changer'.");
221 }
222 data.changer_drivenum = Some(changer_drivenum);
223 }
224 }
225
226 config.set_data(&name, "lto", &data)?;
227
228 pbs_config::drive::save_config(&config)?;
229
230 Ok(())
231 }
232
233 #[api(
234 protected: true,
235 input: {
236 properties: {
237 name: {
238 schema: DRIVE_NAME_SCHEMA,
239 },
240 },
241 },
242 access: {
243 permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY, false),
244 },
245 )]
246 /// Delete a drive configuration
247 pub fn delete_drive(name: String, _param: Value) -> Result<(), Error> {
248
249 let _lock = pbs_config::drive::lock()?;
250
251 let (mut config, _digest) = pbs_config::drive::config()?;
252
253 match config.sections.get(&name) {
254 Some((section_type, _)) => {
255 if section_type != "lto" {
256 bail!("Entry '{}' exists, but is not a lto tape drive", name);
257 }
258 config.sections.remove(&name);
259 },
260 None => bail!("Delete drive '{}' failed - no such drive", name),
261 }
262
263 pbs_config::drive::save_config(&config)?;
264
265 Ok(())
266 }
267
268 const ITEM_ROUTER: Router = Router::new()
269 .get(&API_METHOD_GET_CONFIG)
270 .put(&API_METHOD_UPDATE_DRIVE)
271 .delete(&API_METHOD_DELETE_DRIVE);
272
273
274 pub const ROUTER: Router = Router::new()
275 .get(&API_METHOD_LIST_DRIVES)
276 .post(&API_METHOD_CREATE_DRIVE)
277 .match_all("name", &ITEM_ROUTER);