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