1 use anyhow
::{bail, Error}
;
2 use ::serde
::{Deserialize, Serialize}
;
10 schema
::parse_property_string
,
15 PROXMOX_CONFIG_DIGEST_SCHEMA
,
17 SCSI_CHANGER_PATH_SCHEMA
,
19 EXPORT_SLOT_LIST_SCHEMA
,
26 cached_user_info
::CachedUserInfo
,
33 linux_tape_changer_list
,
43 schema
: CHANGER_NAME_SCHEMA
,
46 schema
: SCSI_CHANGER_PATH_SCHEMA
,
49 schema
: EXPORT_SLOT_LIST_SCHEMA
,
55 permission
: &Permission
::Privilege(&["tape", "device"], PRIV_TAPE_MODIFY
, false),
58 /// Create a new changer device
59 pub fn create_changer(
62 export_slots
: Option
<String
>,
63 ) -> Result
<(), Error
> {
65 let _lock
= pbs_config
::drive
::lock()?
;
67 let (mut config
, _digest
) = pbs_config
::drive
::config()?
;
69 let linux_changers
= linux_tape_changer_list();
71 check_drive_path(&linux_changers
, &path
)?
;
73 let existing
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
75 for changer
in existing
{
76 if changer
.name
== name
{
77 bail
!("Entry '{}' already exists", name
);
80 if changer
.path
== path
{
81 bail
!("Path '{}' already in use by '{}'", path
, changer
.name
);
85 let item
= ScsiTapeChanger
{
91 config
.set_data(&name
, "changer", &item
)?
;
93 pbs_config
::drive
::save_config(&config
)?
;
102 schema
: CHANGER_NAME_SCHEMA
,
107 type: ScsiTapeChanger
,
110 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT
, false),
113 /// Get tape changer configuration
117 mut rpcenv
: &mut dyn RpcEnvironment
,
118 ) -> Result
<ScsiTapeChanger
, Error
> {
120 let (config
, digest
) = pbs_config
::drive
::config()?
;
122 let data
: ScsiTapeChanger
= config
.lookup("changer", &name
)?
;
124 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
134 description
: "The list of configured changers (with config digest).",
137 type: ScsiTapeChanger
,
141 description
: "List configured tape changer filtered by Tape.Audit privileges",
142 permission
: &Permission
::Anybody
,
146 pub fn list_changers(
148 mut rpcenv
: &mut dyn RpcEnvironment
,
149 ) -> Result
<Vec
<ScsiTapeChanger
>, Error
> {
150 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
151 let user_info
= CachedUserInfo
::new()?
;
153 let (config
, digest
) = pbs_config
::drive
::config()?
;
155 let list
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
160 let privs
= user_info
.lookup_privs(&auth_id
, &["tape", "device", &changer
.name
]);
161 privs
& PRIV_TAPE_AUDIT
!= 0
165 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
170 #[derive(Serialize, Deserialize)]
171 #[allow(non_camel_case_types)]
172 #[serde(rename_all = "kebab-case")]
173 /// Deletable property name
174 pub enum DeletableProperty
{
175 /// Delete export-slots.
184 schema
: CHANGER_NAME_SCHEMA
,
187 schema
: SCSI_CHANGER_PATH_SCHEMA
,
191 schema
: EXPORT_SLOT_LIST_SCHEMA
,
195 description
: "List of properties to delete.",
199 type: DeletableProperty
,
203 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
209 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY
, false),
212 /// Update a tape changer configuration
213 pub fn update_changer(
215 path
: Option
<String
>,
216 export_slots
: Option
<String
>,
217 delete
: Option
<Vec
<DeletableProperty
>>,
218 digest
: Option
<String
>,
220 ) -> Result
<(), Error
> {
222 let _lock
= pbs_config
::drive
::lock()?
;
224 let (mut config
, expected_digest
) = pbs_config
::drive
::config()?
;
226 if let Some(ref digest
) = digest
{
227 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
228 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
231 let mut data
: ScsiTapeChanger
= config
.lookup("changer", &name
)?
;
233 if let Some(delete
) = delete
{
234 for delete_prop
in delete
{
236 DeletableProperty
::export_slots
=> {
237 data
.export_slots
= None
;
243 if let Some(path
) = path
{
244 let changers
= linux_tape_changer_list();
245 check_drive_path(&changers
, &path
)?
;
249 if let Some(export_slots
) = export_slots
{
250 let slots
: Value
= parse_property_string(
251 &export_slots
, &SLOT_ARRAY_SCHEMA
253 let mut slots
: Vec
<String
> = slots
257 .map(|v
| v
.to_string())
261 if slots
.is_empty() {
262 data
.export_slots
= None
;
264 let slots
= slots
.join(",");
265 data
.export_slots
= Some(slots
);
269 config
.set_data(&name
, "changer", &data
)?
;
271 pbs_config
::drive
::save_config(&config
)?
;
281 schema
: CHANGER_NAME_SCHEMA
,
286 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY
, false),
289 /// Delete a tape changer configuration
290 pub fn delete_changer(name
: String
, _param
: Value
) -> Result
<(), Error
> {
292 let _lock
= pbs_config
::drive
::lock()?
;
294 let (mut config
, _digest
) = pbs_config
::drive
::config()?
;
296 match config
.sections
.get(&name
) {
297 Some((section_type
, _
)) => {
298 if section_type
!= "changer" {
299 bail
!("Entry '{}' exists, but is not a changer device", name
);
301 config
.sections
.remove(&name
);
303 None
=> bail
!("Delete changer '{}' failed - no such entry", name
),
306 let drive_list
: Vec
<LtoTapeDrive
> = config
.convert_to_typed_array("lto")?
;
307 for drive
in drive_list
{
308 if let Some(changer
) = drive
.changer
{
310 bail
!("Delete changer '{}' failed - used by drive '{}'", name
, drive
.name
);
315 pbs_config
::drive
::save_config(&config
)?
;
320 const ITEM_ROUTER
: Router
= Router
::new()
321 .get(&API_METHOD_GET_CONFIG
)
322 .put(&API_METHOD_UPDATE_CHANGER
)
323 .delete(&API_METHOD_DELETE_CHANGER
);
326 pub const ROUTER
: Router
= Router
::new()
327 .get(&API_METHOD_LIST_CHANGERS
)
328 .post(&API_METHOD_CREATE_CHANGER
)
329 .match_all("name", &ITEM_ROUTER
);