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