]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/changer.rs
ui: tfa: drop useless extjs state save handling
[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,
137 vendor: None,
138 model: None,
139 serial: None,
740dc9d1 140 });
50bf10ad
DM
141 }
142
143 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
144 Ok(list)
145}
38ae42b1
DM
146#[api()]
147#[derive(Serialize, Deserialize)]
148#[allow(non_camel_case_types)]
149#[serde(rename_all = "kebab-case")]
150/// Deletable property name
151pub enum DeletableProperty {
152 /// Delete export-slots.
153 export_slots,
154}
50bf10ad
DM
155
156#[api(
314652a4 157 protected: true,
50bf10ad
DM
158 input: {
159 properties: {
160 name: {
7e1d4712 161 schema: CHANGER_NAME_SCHEMA,
50bf10ad
DM
162 },
163 path: {
164 schema: LINUX_DRIVE_PATH_SCHEMA,
165 optional: true,
166 },
38ae42b1
DM
167 "export-slots": {
168 schema: EXPORT_SLOT_LIST_SCHEMA,
169 optional: true,
170 },
171 delete: {
172 description: "List of properties to delete.",
173 type: Array,
174 optional: true,
175 items: {
176 type: DeletableProperty,
177 },
178 },
5ba83ed0
DM
179 digest: {
180 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
181 optional: true,
182 },
183 },
50bf10ad
DM
184 },
185)]
186/// Update a tape changer configuration
187pub fn update_changer(
188 name: String,
189 path: Option<String>,
38ae42b1
DM
190 export_slots: Option<String>,
191 delete: Option<Vec<DeletableProperty>>,
5ba83ed0 192 digest: Option<String>,
50bf10ad
DM
193 _param: Value,
194) -> Result<(), Error> {
195
196 let _lock = config::drive::lock()?;
197
5ba83ed0
DM
198 let (mut config, expected_digest) = config::drive::config()?;
199
200 if let Some(ref digest) = digest {
201 let digest = proxmox::tools::hex_to_digest(digest)?;
202 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
203 }
50bf10ad
DM
204
205 let mut data: ScsiTapeChanger = config.lookup("changer", &name)?;
206
38ae42b1
DM
207 if let Some(delete) = delete {
208 for delete_prop in delete {
209 match delete_prop {
210 DeletableProperty::export_slots => {
211 data.export_slots = None;
212 }
213 }
214 }
215 }
216
50bf10ad
DM
217 if let Some(path) = path {
218 let changers = linux_tape_changer_list();
219 check_drive_path(&changers, &path)?;
220 data.path = path;
221 }
222
38ae42b1
DM
223 if let Some(export_slots) = export_slots {
224 let slots: Value = parse_property_string(
225 &export_slots, &SLOT_ARRAY_SCHEMA
226 )?;
227 let mut slots: Vec<String> = slots
228 .as_array()
229 .unwrap()
230 .iter()
231 .map(|v| v.to_string())
232 .collect();
233 slots.sort();
234
235 if slots.is_empty() {
236 data.export_slots = None;
237 } else {
238 let slots = slots.join(",");
239 data.export_slots = Some(slots);
240 }
241 }
242
50bf10ad
DM
243 config.set_data(&name, "changer", &data)?;
244
245 config::drive::save_config(&config)?;
246
247 Ok(())
248}
249
250#[api(
314652a4 251 protected: true,
50bf10ad
DM
252 input: {
253 properties: {
254 name: {
7e1d4712 255 schema: CHANGER_NAME_SCHEMA,
50bf10ad
DM
256 },
257 },
258 },
259)]
260/// Delete a tape changer configuration
261pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
262
263 let _lock = config::drive::lock()?;
264
265 let (mut config, _digest) = config::drive::config()?;
266
267 match config.sections.get(&name) {
268 Some((section_type, _)) => {
269 if section_type != "changer" {
270 bail!("Entry '{}' exists, but is not a changer device", name);
271 }
272 config.sections.remove(&name);
273 },
274 None => bail!("Delete changer '{}' failed - no such entry", name),
275 }
276
43cfb3c3
DM
277 let drive_list: Vec<LinuxTapeDrive> = config.convert_to_typed_array("linux")?;
278 for drive in drive_list {
279 if let Some(changer) = drive.changer {
280 if changer == name {
281 bail!("Delete changer '{}' failed - used by drive '{}'", name, drive.name);
282 }
283 }
284 }
285
50bf10ad
DM
286 config::drive::save_config(&config)?;
287
288 Ok(())
289}
290
50bf10ad
DM
291const ITEM_ROUTER: Router = Router::new()
292 .get(&API_METHOD_GET_CONFIG)
293 .put(&API_METHOD_UPDATE_CHANGER)
294 .delete(&API_METHOD_DELETE_CHANGER);
295
296
297pub const ROUTER: Router = Router::new()
298 .get(&API_METHOD_LIST_CHANGERS)
299 .post(&API_METHOD_CREATE_CHANGER)
300 .match_all("name", &ITEM_ROUTER);