]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/changer.rs
tape: check digest on config update
[proxmox-backup.git] / src / api2 / config / changer.rs
1 use anyhow::{bail, Error};
2 use serde_json::Value;
3
4 use proxmox::api::{api, Router, RpcEnvironment};
5
6 use crate::{
7 config,
8 api2::types::{
9 PROXMOX_CONFIG_DIGEST_SCHEMA,
10 CHANGER_ID_SCHEMA,
11 LINUX_DRIVE_PATH_SCHEMA,
12 DriveListEntry,
13 ScsiTapeChanger,
14 TapeDeviceInfo,
15 },
16 tape::{
17 linux_tape_changer_list,
18 check_drive_path,
19 lookup_drive,
20 },
21 };
22
23 #[api(
24 input: {
25 properties: {
26 name: {
27 schema: CHANGER_ID_SCHEMA,
28 },
29 path: {
30 schema: LINUX_DRIVE_PATH_SCHEMA,
31 },
32 },
33 },
34 )]
35 /// Create a new changer device
36 pub fn create_changer(
37 name: String,
38 path: String,
39 ) -> Result<(), Error> {
40
41 let _lock = config::drive::lock()?;
42
43 let (mut config, _digest) = config::drive::config()?;
44
45 let linux_changers = linux_tape_changer_list();
46
47 check_drive_path(&linux_changers, &path)?;
48
49 if config.sections.get(&name).is_some() {
50 bail!("Entry '{}' already exists", name);
51 }
52
53 let item = ScsiTapeChanger {
54 name: name.clone(),
55 path,
56 };
57
58 config.set_data(&name, "changer", &item)?;
59
60 config::drive::save_config(&config)?;
61
62 Ok(())
63 }
64
65 #[api(
66 input: {
67 properties: {
68 name: {
69 schema: CHANGER_ID_SCHEMA,
70 },
71 },
72 },
73 returns: {
74 type: ScsiTapeChanger,
75 },
76
77 )]
78 /// Get tape changer configuration
79 pub fn get_config(
80 name: String,
81 _param: Value,
82 mut rpcenv: &mut dyn RpcEnvironment,
83 ) -> Result<ScsiTapeChanger, Error> {
84
85 let (config, digest) = config::drive::config()?;
86
87 let data: ScsiTapeChanger = config.lookup("changer", &name)?;
88
89 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
90
91 Ok(data)
92 }
93
94 #[api(
95 input: {
96 properties: {},
97 },
98 returns: {
99 description: "The list of configured changers (with config digest).",
100 type: Array,
101 items: {
102 type: DriveListEntry,
103 },
104 },
105 )]
106 /// List changers
107 pub fn list_changers(
108 _param: Value,
109 mut rpcenv: &mut dyn RpcEnvironment,
110 ) -> Result<Vec<DriveListEntry>, Error> {
111
112 let (config, digest) = config::drive::config()?;
113
114 let linux_changers = linux_tape_changer_list();
115
116 let changer_list: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
117
118 let mut list = Vec::new();
119
120 for changer in changer_list {
121 let mut entry = DriveListEntry {
122 name: changer.name,
123 path: changer.path.clone(),
124 changer: None,
125 vendor: None,
126 model: None,
127 serial: None,
128 };
129 if let Some(info) = lookup_drive(&linux_changers, &changer.path) {
130 entry.vendor = Some(info.vendor.clone());
131 entry.model = Some(info.model.clone());
132 entry.serial = Some(info.serial.clone());
133 }
134
135 list.push(entry);
136 }
137
138 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
139 Ok(list)
140 }
141
142 #[api(
143 input: {
144 properties: {
145 name: {
146 schema: CHANGER_ID_SCHEMA,
147 },
148 path: {
149 schema: LINUX_DRIVE_PATH_SCHEMA,
150 optional: true,
151 },
152 digest: {
153 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
154 optional: true,
155 },
156 },
157 },
158 )]
159 /// Update a tape changer configuration
160 pub fn update_changer(
161 name: String,
162 path: Option<String>,
163 digest: Option<String>,
164 _param: Value,
165 ) -> Result<(), Error> {
166
167 let _lock = config::drive::lock()?;
168
169 let (mut config, expected_digest) = config::drive::config()?;
170
171 if let Some(ref digest) = digest {
172 let digest = proxmox::tools::hex_to_digest(digest)?;
173 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
174 }
175
176 let mut data: ScsiTapeChanger = config.lookup("changer", &name)?;
177
178 if let Some(path) = path {
179 let changers = linux_tape_changer_list();
180 check_drive_path(&changers, &path)?;
181 data.path = path;
182 }
183
184 config.set_data(&name, "changer", &data)?;
185
186 config::drive::save_config(&config)?;
187
188 Ok(())
189 }
190
191 #[api(
192 input: {
193 properties: {
194 name: {
195 schema: CHANGER_ID_SCHEMA,
196 },
197 },
198 },
199 )]
200 /// Delete a tape changer configuration
201 pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
202
203 let _lock = config::drive::lock()?;
204
205 let (mut config, _digest) = config::drive::config()?;
206
207 match config.sections.get(&name) {
208 Some((section_type, _)) => {
209 if section_type != "changer" {
210 bail!("Entry '{}' exists, but is not a changer device", name);
211 }
212 config.sections.remove(&name);
213 },
214 None => bail!("Delete changer '{}' failed - no such entry", name),
215 }
216
217 config::drive::save_config(&config)?;
218
219 Ok(())
220 }
221
222 #[api(
223 input: {
224 properties: {},
225 },
226 returns: {
227 description: "The list of autodetected tape changers.",
228 type: Array,
229 items: {
230 type: TapeDeviceInfo,
231 },
232 },
233 )]
234 /// Scan for SCSI tape changers
235 pub fn scan_changers(_param: Value) -> Result<Vec<TapeDeviceInfo>, Error> {
236
237 let list = linux_tape_changer_list();
238
239 Ok(list)
240 }
241
242 pub const SCAN_CHANGERS: Router = Router::new()
243 .get(&API_METHOD_SCAN_CHANGERS);
244
245
246 const ITEM_ROUTER: Router = Router::new()
247 .get(&API_METHOD_GET_CONFIG)
248 .put(&API_METHOD_UPDATE_CHANGER)
249 .delete(&API_METHOD_DELETE_CHANGER);
250
251
252 pub const ROUTER: Router = Router::new()
253 .get(&API_METHOD_LIST_CHANGERS)
254 .post(&API_METHOD_CREATE_CHANGER)
255 .match_all("name", &ITEM_ROUTER);