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