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