1 use anyhow
::{bail, Error}
;
2 use ::serde
::{Deserialize, Serialize}
;
5 use proxmox_router
::{Router, RpcEnvironment, Permission}
;
6 use proxmox_schema
::api
;
9 Authid
, LtoTapeDrive
, LtoTapeDriveUpdater
, ScsiTapeChanger
,
10 PROXMOX_CONFIG_DIGEST_SCHEMA
, DRIVE_NAME_SCHEMA
, PRIV_TAPE_AUDIT
, PRIV_TAPE_MODIFY
,
12 use pbs_config
::CachedUserInfo
;
14 use pbs_tape
::linux_list_drives
::{lto_tape_device_list, check_drive_path}
;
27 permission
: &Permission
::Privilege(&["tape", "device"], PRIV_TAPE_MODIFY
, false),
30 /// Create a new drive
31 pub fn create_drive(config
: LtoTapeDrive
) -> Result
<(), Error
> {
33 let _lock
= pbs_config
::drive
::lock()?
;
35 let (mut section_config
, _digest
) = pbs_config
::drive
::config()?
;
37 let lto_drives
= lto_tape_device_list();
39 check_drive_path(<o_drives
, &config
.path
)?
;
41 let existing
: Vec
<LtoTapeDrive
> = section_config
.convert_to_typed_array("lto")?
;
43 for drive
in existing
{
44 if drive
.name
== config
.name
{
45 bail
!("Entry '{}' already exists", config
.name
);
47 if drive
.path
== config
.path
{
48 bail
!("Path '{}' already used in drive '{}'", config
.path
, drive
.name
);
52 section_config
.set_data(&config
.name
, "lto", &config
)?
;
54 pbs_config
::drive
::save_config(§ion_config
)?
;
63 schema
: DRIVE_NAME_SCHEMA
,
71 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT
, false),
74 /// Get drive configuration
78 mut rpcenv
: &mut dyn RpcEnvironment
,
79 ) -> Result
<LtoTapeDrive
, Error
> {
81 let (config
, digest
) = pbs_config
::drive
::config()?
;
83 let data
: LtoTapeDrive
= config
.lookup("lto", &name
)?
;
85 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
95 description
: "The list of configured drives (with config digest).",
102 description
: "List configured tape drives filtered by Tape.Audit privileges",
103 permission
: &Permission
::Anybody
,
109 mut rpcenv
: &mut dyn RpcEnvironment
,
110 ) -> Result
<Vec
<LtoTapeDrive
>, Error
> {
111 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
112 let user_info
= CachedUserInfo
::new()?
;
114 let (config
, digest
) = pbs_config
::drive
::config()?
;
116 let drive_list
: Vec
<LtoTapeDrive
> = config
.convert_to_typed_array("lto")?
;
118 let drive_list
= drive_list
121 let privs
= user_info
.lookup_privs(&auth_id
, &["tape", "device", &drive
.name
]);
122 privs
& PRIV_TAPE_AUDIT
!= 0
126 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
132 #[derive(Serialize, Deserialize)]
133 #[allow(non_camel_case_types)]
134 #[serde(rename_all = "kebab-case")]
135 /// Deletable property name
136 pub enum DeletableProperty
{
137 /// Delete the changer property.
139 /// Delete the changer-drivenum property.
148 schema
: DRIVE_NAME_SCHEMA
,
151 type: LtoTapeDriveUpdater
,
155 description
: "List of properties to delete.",
159 type: DeletableProperty
,
163 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
169 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY
, false),
172 /// Update a drive configuration
175 update
: LtoTapeDriveUpdater
,
176 delete
: Option
<Vec
<DeletableProperty
>>,
177 digest
: Option
<String
>,
179 ) -> Result
<(), Error
> {
181 let _lock
= pbs_config
::drive
::lock()?
;
183 let (mut config
, expected_digest
) = pbs_config
::drive
::config()?
;
185 if let Some(ref digest
) = digest
{
186 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
187 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
190 let mut data
: LtoTapeDrive
= config
.lookup("lto", &name
)?
;
192 if let Some(delete
) = delete
{
193 for delete_prop
in delete
{
195 DeletableProperty
::changer
=> {
197 data
.changer_drivenum
= None
;
199 DeletableProperty
::changer_drivenum
=> { data.changer_drivenum = None; }
,
204 if let Some(path
) = update
.path
{
205 let lto_drives
= lto_tape_device_list();
206 check_drive_path(<o_drives
, &path
)?
;
210 if let Some(changer
) = update
.changer
{
211 let _
: ScsiTapeChanger
= config
.lookup("changer", &changer
)?
;
212 data
.changer
= Some(changer
);
215 if let Some(changer_drivenum
) = update
.changer_drivenum
{
216 if changer_drivenum
== 0 {
217 data
.changer_drivenum
= None
;
219 if data
.changer
.is_none() {
220 bail
!("Option 'changer-drivenum' requires option 'changer'.");
222 data
.changer_drivenum
= Some(changer_drivenum
);
226 config
.set_data(&name
, "lto", &data
)?
;
228 pbs_config
::drive
::save_config(&config
)?
;
238 schema
: DRIVE_NAME_SCHEMA
,
243 permission
: &Permission
::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY
, false),
246 /// Delete a drive configuration
247 pub fn delete_drive(name
: String
, _param
: Value
) -> Result
<(), Error
> {
249 let _lock
= pbs_config
::drive
::lock()?
;
251 let (mut config
, _digest
) = pbs_config
::drive
::config()?
;
253 match config
.sections
.get(&name
) {
254 Some((section_type
, _
)) => {
255 if section_type
!= "lto" {
256 bail
!("Entry '{}' exists, but is not a lto tape drive", name
);
258 config
.sections
.remove(&name
);
260 None
=> bail
!("Delete drive '{}' failed - no such drive", name
),
263 pbs_config
::drive
::save_config(&config
)?
;
268 const ITEM_ROUTER
: Router
= Router
::new()
269 .get(&API_METHOD_GET_CONFIG
)
270 .put(&API_METHOD_UPDATE_DRIVE
)
271 .delete(&API_METHOD_DELETE_DRIVE
);
274 pub const ROUTER
: Router
= Router
::new()
275 .get(&API_METHOD_LIST_DRIVES
)
276 .post(&API_METHOD_CREATE_DRIVE
)
277 .match_all("name", &ITEM_ROUTER
);