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