]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/changer.rs
config: rustfmt
[proxmox-backup.git] / src / api2 / config / changer.rs
CommitLineData
dcd1518e 1use anyhow::Error;
38ae42b1 2use ::serde::{Deserialize, Serialize};
50bf10ad 3use serde_json::Value;
25877d05 4use hex::FromHex;
50bf10ad 5
dcd1518e 6use proxmox_router::{http_bail, Router, RpcEnvironment, Permission};
8d6425aa 7use proxmox_schema::{api, param_bail};
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 45 if changer.name == config.name {
8d6425aa 46 param_bail!("name", "Entry '{}' already exists", config.name);
b03ec281
DC
47 }
48
5af3bcf0 49 if changer.path == config.path {
8d6425aa 50 param_bail!("path", "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 {
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> {
247
1ce8e905 248 let _lock = pbs_config::drive::lock()?;
50bf10ad 249
1ce8e905 250 let (mut config, _digest) = pbs_config::drive::config()?;
50bf10ad
DM
251
252 match config.sections.get(&name) {
253 Some((section_type, _)) => {
254 if section_type != "changer" {
8d6425aa 255 param_bail!("name", "Entry '{}' exists, but is not a changer device", name);
50bf10ad
DM
256 }
257 config.sections.remove(&name);
258 },
dcd1518e 259 None => http_bail!(NOT_FOUND, "Delete changer '{}' failed - no such entry", name),
50bf10ad
DM
260 }
261
a79082a0 262 let drive_list: Vec<LtoTapeDrive> = config.convert_to_typed_array("lto")?;
43cfb3c3
DM
263 for drive in drive_list {
264 if let Some(changer) = drive.changer {
265 if changer == name {
8d6425aa 266 param_bail!("name", "Delete changer '{}' failed - used by drive '{}'", name, drive.name);
43cfb3c3
DM
267 }
268 }
269 }
270
1ce8e905 271 pbs_config::drive::save_config(&config)?;
50bf10ad
DM
272
273 Ok(())
274}
275
50bf10ad
DM
276const ITEM_ROUTER: Router = Router::new()
277 .get(&API_METHOD_GET_CONFIG)
278 .put(&API_METHOD_UPDATE_CHANGER)
279 .delete(&API_METHOD_DELETE_CHANGER);
280
281
282pub const ROUTER: Router = Router::new()
283 .get(&API_METHOD_LIST_CHANGERS)
284 .post(&API_METHOD_CREATE_CHANGER)
285 .match_all("name", &ITEM_ROUTER);