1 use anyhow
::{bail, Error}
;
2 use ::serde
::{Deserialize, Serialize}
;
9 schema
::parse_property_string
,
15 PROXMOX_CONFIG_DIGEST_SCHEMA
,
17 LINUX_DRIVE_PATH_SCHEMA
,
19 EXPORT_SLOT_LIST_SCHEMA
,
25 linux_tape_changer_list
,
35 schema
: CHANGER_NAME_SCHEMA
,
38 schema
: LINUX_DRIVE_PATH_SCHEMA
,
41 schema
: EXPORT_SLOT_LIST_SCHEMA
,
47 /// Create a new changer device
48 pub fn create_changer(
51 export_slots
: Option
<String
>,
52 ) -> Result
<(), Error
> {
54 let _lock
= config
::drive
::lock()?
;
56 let (mut config
, _digest
) = config
::drive
::config()?
;
58 let linux_changers
= linux_tape_changer_list();
60 check_drive_path(&linux_changers
, &path
)?
;
62 let existing
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
64 for changer
in existing
{
65 if changer
.name
== name
{
66 bail
!("Entry '{}' already exists", name
);
69 if changer
.path
== path
{
70 bail
!("Path '{}' already in use by '{}'", path
, changer
.name
);
74 let item
= ScsiTapeChanger
{
80 config
.set_data(&name
, "changer", &item
)?
;
82 config
::drive
::save_config(&config
)?
;
91 schema
: CHANGER_NAME_SCHEMA
,
96 type: ScsiTapeChanger
,
100 /// Get tape changer configuration
104 mut rpcenv
: &mut dyn RpcEnvironment
,
105 ) -> Result
<ScsiTapeChanger
, Error
> {
107 let (config
, digest
) = config
::drive
::config()?
;
109 let data
: ScsiTapeChanger
= config
.lookup("changer", &name
)?
;
111 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
121 description
: "The list of configured changers (with config digest).",
124 type: DriveListEntry
,
129 pub fn list_changers(
131 mut rpcenv
: &mut dyn RpcEnvironment
,
132 ) -> Result
<Vec
<DriveListEntry
>, Error
> {
134 let (config
, digest
) = config
::drive
::config()?
;
136 let changer_list
: Vec
<ScsiTapeChanger
> = config
.convert_to_typed_array("changer")?
;
138 let mut list
= Vec
::new();
140 for changer
in changer_list
{
141 list
.push(DriveListEntry
{
143 path
: changer
.path
.clone(),
145 changer_drivenum
: None
,
152 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
156 #[derive(Serialize, Deserialize)]
157 #[allow(non_camel_case_types)]
158 #[serde(rename_all = "kebab-case")]
159 /// Deletable property name
160 pub enum DeletableProperty
{
161 /// Delete export-slots.
170 schema
: CHANGER_NAME_SCHEMA
,
173 schema
: LINUX_DRIVE_PATH_SCHEMA
,
177 schema
: EXPORT_SLOT_LIST_SCHEMA
,
181 description
: "List of properties to delete.",
185 type: DeletableProperty
,
189 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
195 /// Update a tape changer configuration
196 pub fn update_changer(
198 path
: Option
<String
>,
199 export_slots
: Option
<String
>,
200 delete
: Option
<Vec
<DeletableProperty
>>,
201 digest
: Option
<String
>,
203 ) -> Result
<(), Error
> {
205 let _lock
= config
::drive
::lock()?
;
207 let (mut config
, expected_digest
) = config
::drive
::config()?
;
209 if let Some(ref digest
) = digest
{
210 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
211 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
214 let mut data
: ScsiTapeChanger
= config
.lookup("changer", &name
)?
;
216 if let Some(delete
) = delete
{
217 for delete_prop
in delete
{
219 DeletableProperty
::export_slots
=> {
220 data
.export_slots
= None
;
226 if let Some(path
) = path
{
227 let changers
= linux_tape_changer_list();
228 check_drive_path(&changers
, &path
)?
;
232 if let Some(export_slots
) = export_slots
{
233 let slots
: Value
= parse_property_string(
234 &export_slots
, &SLOT_ARRAY_SCHEMA
236 let mut slots
: Vec
<String
> = slots
240 .map(|v
| v
.to_string())
244 if slots
.is_empty() {
245 data
.export_slots
= None
;
247 let slots
= slots
.join(",");
248 data
.export_slots
= Some(slots
);
252 config
.set_data(&name
, "changer", &data
)?
;
254 config
::drive
::save_config(&config
)?
;
264 schema
: CHANGER_NAME_SCHEMA
,
269 /// Delete a tape changer configuration
270 pub fn delete_changer(name
: String
, _param
: Value
) -> Result
<(), Error
> {
272 let _lock
= config
::drive
::lock()?
;
274 let (mut config
, _digest
) = config
::drive
::config()?
;
276 match config
.sections
.get(&name
) {
277 Some((section_type
, _
)) => {
278 if section_type
!= "changer" {
279 bail
!("Entry '{}' exists, but is not a changer device", name
);
281 config
.sections
.remove(&name
);
283 None
=> bail
!("Delete changer '{}' failed - no such entry", name
),
286 let drive_list
: Vec
<LinuxTapeDrive
> = config
.convert_to_typed_array("linux")?
;
287 for drive
in drive_list
{
288 if let Some(changer
) = drive
.changer
{
290 bail
!("Delete changer '{}' failed - used by drive '{}'", name
, drive
.name
);
295 config
::drive
::save_config(&config
)?
;
300 const ITEM_ROUTER
: Router
= Router
::new()
301 .get(&API_METHOD_GET_CONFIG
)
302 .put(&API_METHOD_UPDATE_CHANGER
)
303 .delete(&API_METHOD_DELETE_CHANGER
);
306 pub const ROUTER
: Router
= Router
::new()
307 .get(&API_METHOD_LIST_CHANGERS
)
308 .post(&API_METHOD_CREATE_CHANGER
)
309 .match_all("name", &ITEM_ROUTER
);