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