1 use anyhow
::{bail, Error}
;
2 use ::serde
::{Deserialize, Serialize}
;
10 schema
::parse_property_string
,
14 Authid
, ScsiTapeChanger
, ScsiTapeChangerUpdater
, LtoTapeDrive
,
15 PROXMOX_CONFIG_DIGEST_SCHEMA
, CHANGER_NAME_SCHEMA
, SLOT_ARRAY_SCHEMA
,
16 PRIV_TAPE_AUDIT
, PRIV_TAPE_MODIFY
,
18 use pbs_config
::CachedUserInfo
;
19 use pbs_tape
::linux_list_drives
::{linux_tape_changer_list, check_drive_path}
;
26 type: ScsiTapeChanger
,
32 permission
: &Permission
::Privilege(&["tape", "device"], PRIV_TAPE_MODIFY
, false),
35 /// Create a new changer device
36 pub fn create_changer(config
: ScsiTapeChanger
) -> Result
<(), Error
> {
38 let _lock
= pbs_config
::drive
::lock()?
;
40 let (mut section_config
, _digest
) = pbs_config
::drive
::config()?
;
42 let linux_changers
= linux_tape_changer_list();
44 check_drive_path(&linux_changers
, &config
.path
)?
;
46 let existing
: Vec
<ScsiTapeChanger
> = section_config
.convert_to_typed_array("changer")?
;
48 for changer
in existing
{
49 if changer
.name
== config
.name
{
50 bail
!("Entry '{}' already exists", config
.name
);
53 if changer
.path
== config
.path
{
54 bail
!("Path '{}' already in use by '{}'", config
.path
, changer
.name
);
58 section_config
.set_data(&config
.name
, "changer", &config
)?
;
60 pbs_config
::drive
::save_config(§ion_config
)?
;
69 schema
: CHANGER_NAME_SCHEMA
,
74 type: ScsiTapeChanger
,
77 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT
, false),
80 /// Get tape changer configuration
84 mut rpcenv
: &mut dyn RpcEnvironment
,
85 ) -> Result
<ScsiTapeChanger
, Error
> {
87 let (config
, digest
) = pbs_config
::drive
::config()?
;
89 let data
: ScsiTapeChanger
= config
.lookup("changer", &name
)?
;
91 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
101 description
: "The list of configured changers (with config digest).",
104 type: ScsiTapeChanger
,
108 description
: "List configured tape changer filtered by Tape.Audit privileges",
109 permission
: &Permission
::Anybody
,
113 pub fn list_changers(
115 mut rpcenv
: &mut dyn RpcEnvironment
,
116 ) -> Result
<Vec
<ScsiTapeChanger
>, Error
> {
117 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
118 let user_info
= CachedUserInfo
::new()?
;
120 let (config
, digest
) = pbs_config
::drive
::config()?
;
122 let list
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
127 let privs
= user_info
.lookup_privs(&auth_id
, &["tape", "device", &changer
.name
]);
128 privs
& PRIV_TAPE_AUDIT
!= 0
132 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
137 #[derive(Serialize, Deserialize)]
138 #[allow(non_camel_case_types)]
139 #[serde(rename_all = "kebab-case")]
140 /// Deletable property name
141 pub enum DeletableProperty
{
142 /// Delete export-slots.
151 schema
: CHANGER_NAME_SCHEMA
,
154 type: ScsiTapeChangerUpdater
,
158 description
: "List of properties to delete.",
162 type: DeletableProperty
,
166 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
172 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY
, false),
175 /// Update a tape changer configuration
176 pub fn update_changer(
178 update
: ScsiTapeChangerUpdater
,
179 delete
: Option
<Vec
<DeletableProperty
>>,
180 digest
: Option
<String
>,
182 ) -> Result
<(), Error
> {
184 let _lock
= pbs_config
::drive
::lock()?
;
186 let (mut config
, expected_digest
) = pbs_config
::drive
::config()?
;
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
)?
;
193 let mut data
: ScsiTapeChanger
= config
.lookup("changer", &name
)?
;
195 if let Some(delete
) = delete
{
196 for delete_prop
in delete
{
198 DeletableProperty
::export_slots
=> {
199 data
.export_slots
= None
;
205 if let Some(path
) = update
.path
{
206 let changers
= linux_tape_changer_list();
207 check_drive_path(&changers
, &path
)?
;
211 if let Some(export_slots
) = update
.export_slots
{
212 let slots
: Value
= parse_property_string(
213 &export_slots
, &SLOT_ARRAY_SCHEMA
215 let mut slots
: Vec
<String
> = slots
219 .map(|v
| v
.to_string())
223 if slots
.is_empty() {
224 data
.export_slots
= None
;
226 let slots
= slots
.join(",");
227 data
.export_slots
= Some(slots
);
231 config
.set_data(&name
, "changer", &data
)?
;
233 pbs_config
::drive
::save_config(&config
)?
;
243 schema
: CHANGER_NAME_SCHEMA
,
248 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY
, false),
251 /// Delete a tape changer configuration
252 pub fn delete_changer(name
: String
, _param
: Value
) -> Result
<(), Error
> {
254 let _lock
= pbs_config
::drive
::lock()?
;
256 let (mut config
, _digest
) = pbs_config
::drive
::config()?
;
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
);
263 config
.sections
.remove(&name
);
265 None
=> bail
!("Delete changer '{}' failed - no such entry", name
),
268 let drive_list
: Vec
<LtoTapeDrive
> = config
.convert_to_typed_array("lto")?
;
269 for drive
in drive_list
{
270 if let Some(changer
) = drive
.changer
{
272 bail
!("Delete changer '{}' failed - used by drive '{}'", name
, drive
.name
);
277 pbs_config
::drive
::save_config(&config
)?
;
282 const ITEM_ROUTER
: Router
= Router
::new()
283 .get(&API_METHOD_GET_CONFIG
)
284 .put(&API_METHOD_UPDATE_CHANGER
)
285 .delete(&API_METHOD_DELETE_CHANGER
);
288 pub const ROUTER
: Router
= Router
::new()
289 .get(&API_METHOD_LIST_CHANGERS
)
290 .post(&API_METHOD_CREATE_CHANGER
)
291 .match_all("name", &ITEM_ROUTER
);