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