]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/changer.rs
complete_acl_path: add more paths
[proxmox-backup.git] / src / api2 / config / changer.rs
CommitLineData
50bf10ad 1use anyhow::{bail, Error};
38ae42b1 2use ::serde::{Deserialize, Serialize};
50bf10ad
DM
3use serde_json::Value;
4
38ae42b1
DM
5use proxmox::api::{
6 api,
7 Router,
8 RpcEnvironment,
9 schema::parse_property_string,
10};
50bf10ad
DM
11
12use crate::{
13 config,
14 api2::types::{
5ba83ed0 15 PROXMOX_CONFIG_DIGEST_SCHEMA,
7e1d4712 16 CHANGER_NAME_SCHEMA,
18ce01ca 17 SCSI_CHANGER_PATH_SCHEMA,
38ae42b1
DM
18 SLOT_ARRAY_SCHEMA,
19 EXPORT_SLOT_LIST_SCHEMA,
50bf10ad 20 ScsiTapeChanger,
43cfb3c3 21 LinuxTapeDrive,
50bf10ad 22 },
645a044b 23 tape::{
50bf10ad
DM
24 linux_tape_changer_list,
25 check_drive_path,
50bf10ad
DM
26 },
27};
28
29#[api(
314652a4 30 protected: true,
50bf10ad
DM
31 input: {
32 properties: {
33 name: {
7e1d4712 34 schema: CHANGER_NAME_SCHEMA,
50bf10ad
DM
35 },
36 path: {
18ce01ca 37 schema: SCSI_CHANGER_PATH_SCHEMA,
50bf10ad 38 },
38ae42b1
DM
39 "export-slots": {
40 schema: EXPORT_SLOT_LIST_SCHEMA,
41 optional: true,
42 },
50bf10ad
DM
43 },
44 },
45)]
46/// Create a new changer device
47pub fn create_changer(
48 name: String,
49 path: String,
38ae42b1 50 export_slots: Option<String>,
50bf10ad
DM
51) -> Result<(), Error> {
52
53 let _lock = config::drive::lock()?;
54
55 let (mut config, _digest) = config::drive::config()?;
56
57 let linux_changers = linux_tape_changer_list();
58
59 check_drive_path(&linux_changers, &path)?;
60
b03ec281
DC
61 let existing: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
62
63 for changer in existing {
64 if changer.name == name {
65 bail!("Entry '{}' already exists", name);
66 }
67
68 if changer.path == path {
69 bail!("Path '{}' already in use by '{}'", path, changer.name);
70 }
50bf10ad
DM
71 }
72
73 let item = ScsiTapeChanger {
74 name: name.clone(),
75 path,
38ae42b1 76 export_slots,
50bf10ad
DM
77 };
78
79 config.set_data(&name, "changer", &item)?;
80
81 config::drive::save_config(&config)?;
82
83 Ok(())
84}
85
86#[api(
87 input: {
88 properties: {
89 name: {
7e1d4712 90 schema: CHANGER_NAME_SCHEMA,
50bf10ad
DM
91 },
92 },
93 },
94 returns: {
95 type: ScsiTapeChanger,
96 },
97
98)]
99/// Get tape changer configuration
100pub fn get_config(
101 name: String,
102 _param: Value,
103 mut rpcenv: &mut dyn RpcEnvironment,
104) -> Result<ScsiTapeChanger, Error> {
105
106 let (config, digest) = config::drive::config()?;
107
108 let data: ScsiTapeChanger = config.lookup("changer", &name)?;
109
110 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
111
112 Ok(data)
113}
114
115#[api(
116 input: {
117 properties: {},
118 },
119 returns: {
120 description: "The list of configured changers (with config digest).",
121 type: Array,
122 items: {
b5b99a52 123 type: ScsiTapeChanger,
50bf10ad
DM
124 },
125 },
126)]
127/// List changers
128pub fn list_changers(
129 _param: Value,
130 mut rpcenv: &mut dyn RpcEnvironment,
b5b99a52 131) -> Result<Vec<ScsiTapeChanger>, Error> {
50bf10ad
DM
132
133 let (config, digest) = config::drive::config()?;
134
b5b99a52 135 let list: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
50bf10ad
DM
136
137 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
b5b99a52 138
50bf10ad
DM
139 Ok(list)
140}
38ae42b1
DM
141#[api()]
142#[derive(Serialize, Deserialize)]
143#[allow(non_camel_case_types)]
144#[serde(rename_all = "kebab-case")]
145/// Deletable property name
146pub enum DeletableProperty {
147 /// Delete export-slots.
148 export_slots,
149}
50bf10ad
DM
150
151#[api(
314652a4 152 protected: true,
50bf10ad
DM
153 input: {
154 properties: {
155 name: {
7e1d4712 156 schema: CHANGER_NAME_SCHEMA,
50bf10ad
DM
157 },
158 path: {
18ce01ca 159 schema: SCSI_CHANGER_PATH_SCHEMA,
50bf10ad
DM
160 optional: true,
161 },
38ae42b1
DM
162 "export-slots": {
163 schema: EXPORT_SLOT_LIST_SCHEMA,
164 optional: true,
165 },
166 delete: {
167 description: "List of properties to delete.",
168 type: Array,
169 optional: true,
170 items: {
171 type: DeletableProperty,
172 },
173 },
5ba83ed0
DM
174 digest: {
175 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
176 optional: true,
177 },
178 },
50bf10ad
DM
179 },
180)]
181/// Update a tape changer configuration
182pub fn update_changer(
183 name: String,
184 path: Option<String>,
38ae42b1
DM
185 export_slots: Option<String>,
186 delete: Option<Vec<DeletableProperty>>,
5ba83ed0 187 digest: Option<String>,
50bf10ad
DM
188 _param: Value,
189) -> Result<(), Error> {
190
191 let _lock = config::drive::lock()?;
192
5ba83ed0
DM
193 let (mut config, expected_digest) = config::drive::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 }
50bf10ad
DM
199
200 let mut data: ScsiTapeChanger = config.lookup("changer", &name)?;
201
38ae42b1
DM
202 if let Some(delete) = delete {
203 for delete_prop in delete {
204 match delete_prop {
205 DeletableProperty::export_slots => {
206 data.export_slots = None;
207 }
208 }
209 }
210 }
211
50bf10ad
DM
212 if let Some(path) = path {
213 let changers = linux_tape_changer_list();
214 check_drive_path(&changers, &path)?;
215 data.path = path;
216 }
217
38ae42b1
DM
218 if let Some(export_slots) = export_slots {
219 let slots: Value = parse_property_string(
220 &export_slots, &SLOT_ARRAY_SCHEMA
221 )?;
222 let mut slots: Vec<String> = slots
223 .as_array()
224 .unwrap()
225 .iter()
226 .map(|v| v.to_string())
227 .collect();
228 slots.sort();
229
230 if slots.is_empty() {
231 data.export_slots = None;
232 } else {
233 let slots = slots.join(",");
234 data.export_slots = Some(slots);
235 }
236 }
237
50bf10ad
DM
238 config.set_data(&name, "changer", &data)?;
239
240 config::drive::save_config(&config)?;
241
242 Ok(())
243}
244
245#[api(
314652a4 246 protected: true,
50bf10ad
DM
247 input: {
248 properties: {
249 name: {
7e1d4712 250 schema: CHANGER_NAME_SCHEMA,
50bf10ad
DM
251 },
252 },
253 },
254)]
255/// Delete a tape changer configuration
256pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
257
258 let _lock = config::drive::lock()?;
259
260 let (mut config, _digest) = config::drive::config()?;
261
262 match config.sections.get(&name) {
263 Some((section_type, _)) => {
264 if section_type != "changer" {
265 bail!("Entry '{}' exists, but is not a changer device", name);
266 }
267 config.sections.remove(&name);
268 },
269 None => bail!("Delete changer '{}' failed - no such entry", name),
270 }
271
43cfb3c3
DM
272 let drive_list: Vec<LinuxTapeDrive> = config.convert_to_typed_array("linux")?;
273 for drive in drive_list {
274 if let Some(changer) = drive.changer {
275 if changer == name {
276 bail!("Delete changer '{}' failed - used by drive '{}'", name, drive.name);
277 }
278 }
279 }
280
50bf10ad
DM
281 config::drive::save_config(&config)?;
282
283 Ok(())
284}
285
50bf10ad
DM
286const ITEM_ROUTER: Router = Router::new()
287 .get(&API_METHOD_GET_CONFIG)
288 .put(&API_METHOD_UPDATE_CHANGER)
289 .delete(&API_METHOD_DELETE_CHANGER);
290
291
292pub const ROUTER: Router = Router::new()
293 .get(&API_METHOD_LIST_CHANGERS)
294 .post(&API_METHOD_CREATE_CHANGER)
295 .match_all("name", &ITEM_ROUTER);