1 use std
::panic
::UnwindSafe
;
5 use anyhow
::{bail, format_err, Error}
;
11 list_subdirs_api_method
,
16 section_config
::SectionConfigData
,
29 cached_user_info
::CachedUserInfo
,
40 MEDIA_POOL_NAME_SCHEMA
,
47 LinuxDriveAndMediaStatus
,
49 tape
::restore
::restore_media
,
58 linux_tape_device_list
,
59 lookup_device_identification
,
68 open_linux_tape_device
,
70 required_media_changer
,
73 set_tape_device_state
,
74 get_tape_device_state
,
76 changer
::update_changer_online_status
,
80 fn run_drive_worker
<F
>(
81 rpcenv
: &dyn RpcEnvironment
,
84 job_id
: Option
<String
>,
86 ) -> Result
<String
, Error
>
91 + FnOnce(Arc
<WorkerTask
>, SectionConfigData
) -> Result
<(), Error
>,
93 // early check/lock before starting worker
94 let (config
, _digest
) = config
::drive
::config()?
;
95 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
97 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
98 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
100 WorkerTask
::new_thread(worker_type
, job_id
, auth_id
, to_stdout
, move |worker
| {
101 let _lock_guard
= lock_guard
;
102 set_tape_device_state(&drive
, &worker
.upid().to_string())
103 .map_err(|err
| format_err
!("could not set tape device state: {}", err
))?
;
105 let result
= f(worker
, config
);
106 set_tape_device_state(&drive
, "")
107 .map_err(|err
| format_err
!("could not unset tape device state: {}", err
))?
;
112 async
fn run_drive_blocking_task
<F
, R
>(drive
: String
, state
: String
, f
: F
) -> Result
<R
, Error
>
114 F
: Send
+ '
static + FnOnce(SectionConfigData
) -> Result
<R
, Error
>,
117 // early check/lock before starting worker
118 let (config
, _digest
) = config
::drive
::config()?
;
119 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
120 tokio
::task
::spawn_blocking(move || {
121 let _lock_guard
= lock_guard
;
122 set_tape_device_state(&drive
, &state
)
123 .map_err(|err
| format_err
!("could not set tape device state: {}", err
))?
;
124 let result
= f(config
);
125 set_tape_device_state(&drive
, "")
126 .map_err(|err
| format_err
!("could not unset tape device state: {}", err
))?
;
136 schema
: DRIVE_NAME_SCHEMA
,
139 schema
: MEDIA_LABEL_SCHEMA
,
147 /// Load media with specified label
149 /// Issue a media load request to the associated changer device.
153 rpcenv
: &mut dyn RpcEnvironment
,
154 ) -> Result
<Value
, Error
> {
155 let job_id
= format
!("{}:{}", drive
, label_text
);
157 let upid_str
= run_drive_worker(
162 move |worker
, config
| {
163 task_log
!(worker
, "loading media '{}' into drive '{}'", label_text
, drive
);
164 let (mut changer
, _
) = required_media_changer(&config
, &drive
)?
;
165 changer
.load_media(&label_text
)?
;
177 schema
: DRIVE_NAME_SCHEMA
,
180 description
: "Source slot number.",
186 /// Load media from the specified slot
188 /// Issue a media load request to the associated changer device.
189 pub async
fn load_slot(drive
: String
, source_slot
: u64) -> Result
<(), Error
> {
190 run_drive_blocking_task(
192 format
!("load from slot {}", source_slot
),
194 let (mut changer
, _
) = required_media_changer(&config
, &drive
)?
;
195 changer
.load_media_from_slot(source_slot
)?
;
206 schema
: DRIVE_NAME_SCHEMA
,
209 schema
: MEDIA_LABEL_SCHEMA
,
214 description
: "The import-export slot number the media was transfered to.",
219 /// Export media with specified label
220 pub async
fn export_media(drive
: String
, label_text
: String
) -> Result
<u64, Error
> {
221 run_drive_blocking_task(
223 format
!("export media {}", label_text
),
225 let (mut changer
, changer_name
) = required_media_changer(&config
, &drive
)?
;
226 match changer
.export_media(&label_text
)?
{
227 Some(slot
) => Ok(slot
),
229 "media '{}' is not online (via changer '{}')",
243 schema
: DRIVE_NAME_SCHEMA
,
246 description
: "Target slot number. If omitted, defaults to the slot that the drive was loaded from.",
256 /// Unload media via changer
259 target_slot
: Option
<u64>,
260 rpcenv
: &mut dyn RpcEnvironment
,
261 ) -> Result
<Value
, Error
> {
262 let upid_str
= run_drive_worker(
267 move |worker
, config
| {
268 task_log
!(worker
, "unloading media from drive '{}'", drive
);
270 let (mut changer
, _
) = required_media_changer(&config
, &drive
)?
;
271 changer
.unload_media(target_slot
)?
;
283 schema
: DRIVE_NAME_SCHEMA
,
286 description
: "Use fast erase.",
292 schema
: MEDIA_LABEL_SCHEMA
,
301 /// Erase media. Check for label-text if given (cancels if wrong media).
305 label_text
: Option
<String
>,
306 rpcenv
: &mut dyn RpcEnvironment
,
307 ) -> Result
<Value
, Error
> {
308 let upid_str
= run_drive_worker(
313 move |worker
, config
| {
314 if let Some(ref label
) = label_text
{
315 task_log
!(worker
, "try to load media '{}'", label
);
316 if let Some((mut changer
, _
)) = media_changer(&config
, &drive
)?
{
317 changer
.load_media(label
)?
;
321 let mut handle
= open_drive(&config
, &drive
)?
;
323 match handle
.read_label() {
325 if let Some(label
) = label_text
{
326 bail
!("expected label '{}', found unrelated data", label
);
328 /* assume drive contains no or unrelated data */
329 task_log
!(worker
, "unable to read media label: {}", err
);
330 task_log
!(worker
, "erase anyways");
331 handle
.erase_media(fast
.unwrap_or(true))?
;
334 if let Some(label
) = label_text
{
335 bail
!("expected label '{}', found empty tape", label
);
337 task_log
!(worker
, "found empty media - erase anyways");
338 handle
.erase_media(fast
.unwrap_or(true))?
;
340 Ok((Some(media_id
), _key_config
)) => {
341 if let Some(label_text
) = label_text
{
342 if media_id
.label
.label_text
!= label_text
{
344 "expected label '{}', found '{}', aborting",
346 media_id
.label
.label_text
353 "found media '{}' with uuid '{}'",
354 media_id
.label
.label_text
, media_id
.label
.uuid
,
357 let status_path
= Path
::new(TAPE_STATUS_DIR
);
358 let mut inventory
= Inventory
::load(status_path
)?
;
360 MediaCatalog
::destroy(status_path
, &media_id
.label
.uuid
)?
;
361 inventory
.remove_media(&media_id
.label
.uuid
)?
;
362 handle
.erase_media(fast
.unwrap_or(true))?
;
377 schema
: DRIVE_NAME_SCHEMA
,
388 rpcenv
: &mut dyn RpcEnvironment
,
389 ) -> Result
<Value
, Error
> {
390 let upid_str
= run_drive_worker(
395 move |_worker
, config
| {
396 let mut drive
= open_drive(&config
, &drive
)?
;
409 schema
: DRIVE_NAME_SCHEMA
,
417 /// Eject/Unload drive media
420 rpcenv
: &mut dyn RpcEnvironment
,
421 ) -> Result
<Value
, Error
> {
422 let upid_str
= run_drive_worker(
427 move |_worker
, config
| {
428 if let Some((mut changer
, _
)) = media_changer(&config
, &drive
)?
{
429 changer
.unload_media(None
)?
;
431 let mut drive
= open_drive(&config
, &drive
)?
;
432 drive
.eject_media()?
;
445 schema
: DRIVE_NAME_SCHEMA
,
448 schema
: MEDIA_LABEL_SCHEMA
,
451 schema
: MEDIA_POOL_NAME_SCHEMA
,
462 /// Write a new media label to the media in 'drive'. The media is
463 /// assigned to the specified 'pool', or else to the free media pool.
465 /// Note: The media need to be empty (you may want to erase it first).
468 pool
: Option
<String
>,
470 rpcenv
: &mut dyn RpcEnvironment
,
471 ) -> Result
<Value
, Error
> {
472 if let Some(ref pool
) = pool
{
473 let (pool_config
, _digest
) = config
::media_pool
::config()?
;
475 if pool_config
.sections
.get(pool
).is_none() {
476 bail
!("no such pool ('{}')", pool
);
479 let upid_str
= run_drive_worker(
484 move |worker
, config
| {
485 let mut drive
= open_drive(&config
, &drive
)?
;
489 match drive
.read_next_file() {
490 Ok(Some(_file
)) => bail
!("media is not empty (erase first)"),
491 Ok(None
) => { /* EOF mark at BOT, assume tape is empty */ }
,
493 if err
.is_errno(nix
::errno
::Errno
::ENOSPC
) || err
.is_errno(nix
::errno
::Errno
::EIO
) {
494 /* assume tape is empty */
496 bail
!("media read error - {}", err
);
501 let ctime
= proxmox
::tools
::time
::epoch_i64();
502 let label
= MediaLabel
{
503 label_text
: label_text
.to_string(),
504 uuid
: Uuid
::generate(),
508 write_media_label(worker
, &mut drive
, label
, pool
)
515 fn write_media_label(
516 worker
: Arc
<WorkerTask
>,
517 drive
: &mut Box
<dyn TapeDriver
>,
519 pool
: Option
<String
>,
520 ) -> Result
<(), Error
> {
522 drive
.label_tape(&label
)?
;
524 let mut media_set_label
= None
;
526 if let Some(ref pool
) = pool
{
527 // assign media to pool by writing special media set label
528 worker
.log(format
!("Label media '{}' for pool '{}'", label
.label_text
, pool
));
529 let set
= MediaSetLabel
::with_data(&pool
, [0u8; 16].into(), 0, label
.ctime
, None
);
531 drive
.write_media_set_label(&set
, None
)?
;
532 media_set_label
= Some(set
);
534 worker
.log(format
!("Label media '{}' (no pool assignment)", label
.label_text
));
537 let media_id
= MediaId { label, media_set_label }
;
539 let status_path
= Path
::new(TAPE_STATUS_DIR
);
541 // Create the media catalog
542 MediaCatalog
::overwrite(status_path
, &media_id
, false)?
;
544 let mut inventory
= Inventory
::load(status_path
)?
;
545 inventory
.store(media_id
.clone(), false)?
;
549 match drive
.read_label() {
550 Ok((Some(info
), _
)) => {
551 if info
.label
.uuid
!= media_id
.label
.uuid
{
552 bail
!("verify label failed - got wrong label uuid");
554 if let Some(ref pool
) = pool
{
555 match info
.media_set_label
{
557 if set
.uuid
!= [0u8; 16].into() {
558 bail
!("verify media set label failed - got wrong set uuid");
560 if &set
.pool
!= pool
{
561 bail
!("verify media set label failed - got wrong pool");
565 bail
!("verify media set label failed (missing set label)");
570 Ok((None
, _
)) => bail
!("verify label failed (got empty media)"),
571 Err(err
) => bail
!("verify label failed - {}", err
),
584 schema
: DRIVE_NAME_SCHEMA
,
587 description
: "Encryption key password.",
592 /// Try to restore a tape encryption key
593 pub async
fn restore_key(
596 ) -> Result
<(), Error
> {
597 run_drive_blocking_task(
599 "restore key".to_string(),
601 let mut drive
= open_drive(&config
, &drive
)?
;
603 let (_media_id
, key_config
) = drive
.read_label()?
;
605 if let Some(key_config
) = key_config
{
606 let password_fn
= || { Ok(password.as_bytes().to_vec()) }
;
607 let (key
, ..) = key_config
.decrypt(&password_fn
)?
;
608 config
::tape_encryption_keys
::insert_key(key
, key_config
, true)?
;
610 bail
!("media does not contain any encryption key configuration");
623 schema
: DRIVE_NAME_SCHEMA
,
626 description
: "Inventorize media",
635 /// Read media label (optionally inventorize media)
636 pub async
fn read_label(
638 inventorize
: Option
<bool
>,
639 ) -> Result
<MediaIdFlat
, Error
> {
640 run_drive_blocking_task(
642 "reading label".to_string(),
644 let mut drive
= open_drive(&config
, &drive
)?
;
646 let (media_id
, _key_config
) = drive
.read_label()?
;
648 let media_id
= match media_id
{
650 let mut flat
= MediaIdFlat
{
651 uuid
: media_id
.label
.uuid
.clone(),
652 label_text
: media_id
.label
.label_text
.clone(),
653 ctime
: media_id
.label
.ctime
,
654 media_set_ctime
: None
,
655 media_set_uuid
: None
,
656 encryption_key_fingerprint
: None
,
660 if let Some(ref set
) = media_id
.media_set_label
{
661 flat
.pool
= Some(set
.pool
.clone());
662 flat
.seq_nr
= Some(set
.seq_nr
);
663 flat
.media_set_uuid
= Some(set
.uuid
.clone());
664 flat
.media_set_ctime
= Some(set
.ctime
);
665 flat
.encryption_key_fingerprint
= set
666 .encryption_key_fingerprint
668 .map(|fp
| crate::tools
::format
::as_fingerprint(fp
.bytes()));
670 let encrypt_fingerprint
= set
.encryption_key_fingerprint
.clone()
671 .map(|fp
| (fp
, set
.uuid
.clone()));
673 if let Err(err
) = drive
.set_encryption(encrypt_fingerprint
) {
674 // try, but ignore errors. just log to stderr
675 eprintln
!("uable to load encryption key: {}", err
);
679 if let Some(true) = inventorize
{
680 let state_path
= Path
::new(TAPE_STATUS_DIR
);
681 let mut inventory
= Inventory
::load(state_path
)?
;
682 inventory
.store(media_id
, false)?
;
688 bail
!("Media is empty (no label).");
702 schema
: DRIVE_NAME_SCHEMA
,
713 rpcenv
: &mut dyn RpcEnvironment
,
714 ) -> Result
<Value
, Error
> {
715 let upid_str
= run_drive_worker(
720 move |worker
, config
| {
721 let (mut changer
, _changer_name
) = required_media_changer(&config
, &drive
)?
;
723 worker
.log("Starting drive clean");
725 changer
.clean_drive()?
;
727 worker
.log("Drive cleaned sucessfully");
740 schema
: DRIVE_NAME_SCHEMA
,
745 description
: "The list of media labels with associated media Uuid (if any).",
752 /// List known media labels (Changer Inventory)
754 /// Note: Only useful for drives with associated changer device.
756 /// This method queries the changer to get a list of media labels.
758 /// Note: This updates the media online status.
759 pub async
fn inventory(
761 ) -> Result
<Vec
<LabelUuidMap
>, Error
> {
762 run_drive_blocking_task(
764 "inventorize".to_string(),
766 let (mut changer
, changer_name
) = required_media_changer(&config
, &drive
)?
;
768 let label_text_list
= changer
.online_media_label_texts()?
;
770 let state_path
= Path
::new(TAPE_STATUS_DIR
);
772 let mut inventory
= Inventory
::load(state_path
)?
;
774 update_changer_online_status(
781 let mut list
= Vec
::new();
783 for label_text
in label_text_list
.iter() {
784 if label_text
.starts_with("CLN") {
785 // skip cleaning unit
789 let label_text
= label_text
.to_string();
791 if let Some(media_id
) = inventory
.find_media_by_label_text(&label_text
) {
792 list
.push(LabelUuidMap { label_text, uuid: Some(media_id.label.uuid.clone()) }
);
794 list
.push(LabelUuidMap { label_text, uuid: None }
);
808 schema
: DRIVE_NAME_SCHEMA
,
811 description
: "Load all tapes and try read labels (even if already inventoried)",
823 /// Note: Only useful for drives with associated changer device.
825 /// This method queries the changer to get a list of media labels. It
826 /// then loads any unknown media into the drive, reads the label, and
827 /// store the result to the media database.
829 /// Note: This updates the media online status.
830 pub fn update_inventory(
832 read_all_labels
: Option
<bool
>,
833 rpcenv
: &mut dyn RpcEnvironment
,
834 ) -> Result
<Value
, Error
> {
835 let upid_str
= run_drive_worker(
840 move |worker
, config
| {
841 let (mut changer
, changer_name
) = required_media_changer(&config
, &drive
)?
;
843 let label_text_list
= changer
.online_media_label_texts()?
;
844 if label_text_list
.is_empty() {
845 worker
.log("changer device does not list any media labels".to_string());
848 let state_path
= Path
::new(TAPE_STATUS_DIR
);
850 let mut inventory
= Inventory
::load(state_path
)?
;
852 update_changer_online_status(&config
, &mut inventory
, &changer_name
, &label_text_list
)?
;
854 for label_text
in label_text_list
.iter() {
855 if label_text
.starts_with("CLN") {
856 worker
.log(format
!("skip cleaning unit '{}'", label_text
));
860 let label_text
= label_text
.to_string();
862 if !read_all_labels
.unwrap_or(false) && inventory
.find_media_by_label_text(&label_text
).is_some() {
863 worker
.log(format
!("media '{}' already inventoried", label_text
));
867 if let Err(err
) = changer
.load_media(&label_text
) {
868 worker
.warn(format
!("unable to load media '{}' - {}", label_text
, err
));
872 let mut drive
= open_drive(&config
, &drive
)?
;
873 match drive
.read_label() {
875 worker
.warn(format
!("unable to read label form media '{}' - {}", label_text
, err
));
878 worker
.log(format
!("media '{}' is empty", label_text
));
880 Ok((Some(media_id
), _key_config
)) => {
881 if label_text
!= media_id
.label
.label_text
{
882 worker
.warn(format
!("label text missmatch ({} != {})", label_text
, media_id
.label
.label_text
));
885 worker
.log(format
!("inventorize media '{}' with uuid '{}'", label_text
, media_id
.label
.uuid
));
886 inventory
.store(media_id
, false)?
;
889 changer
.unload_media(None
)?
;
903 schema
: DRIVE_NAME_SCHEMA
,
906 schema
: MEDIA_POOL_NAME_SCHEMA
,
915 /// Label media with barcodes from changer device
916 pub fn barcode_label_media(
918 pool
: Option
<String
>,
919 rpcenv
: &mut dyn RpcEnvironment
,
920 ) -> Result
<Value
, Error
> {
921 if let Some(ref pool
) = pool
{
922 let (pool_config
, _digest
) = config
::media_pool
::config()?
;
924 if pool_config
.sections
.get(pool
).is_none() {
925 bail
!("no such pool ('{}')", pool
);
929 let upid_str
= run_drive_worker(
932 "barcode-label-media",
934 move |worker
, config
| barcode_label_media_worker(worker
, drive
, &config
, pool
),
940 fn barcode_label_media_worker(
941 worker
: Arc
<WorkerTask
>,
943 drive_config
: &SectionConfigData
,
944 pool
: Option
<String
>,
945 ) -> Result
<(), Error
> {
946 let (mut changer
, changer_name
) = required_media_changer(drive_config
, &drive
)?
;
948 let label_text_list
= changer
.online_media_label_texts()?
;
950 let state_path
= Path
::new(TAPE_STATUS_DIR
);
952 let mut inventory
= Inventory
::load(state_path
)?
;
954 update_changer_online_status(drive_config
, &mut inventory
, &changer_name
, &label_text_list
)?
;
956 if label_text_list
.is_empty() {
957 bail
!("changer device does not list any media labels");
960 for label_text
in label_text_list
{
961 if label_text
.starts_with("CLN") { continue; }
964 if inventory
.find_media_by_label_text(&label_text
).is_some() {
965 worker
.log(format
!("media '{}' already inventoried (already labeled)", label_text
));
969 worker
.log(format
!("checking/loading media '{}'", label_text
));
971 if let Err(err
) = changer
.load_media(&label_text
) {
972 worker
.warn(format
!("unable to load media '{}' - {}", label_text
, err
));
976 let mut drive
= open_drive(drive_config
, &drive
)?
;
979 match drive
.read_next_file() {
981 worker
.log(format
!("media '{}' is not empty (erase first)", label_text
));
984 Ok(None
) => { /* EOF mark at BOT, assume tape is empty */ }
,
986 if err
.is_errno(nix
::errno
::Errno
::ENOSPC
) || err
.is_errno(nix
::errno
::Errno
::EIO
) {
987 /* assume tape is empty */
989 worker
.warn(format
!("media '{}' read error (maybe not empty - erase first)", label_text
));
995 let ctime
= proxmox
::tools
::time
::epoch_i64();
996 let label
= MediaLabel
{
997 label_text
: label_text
.to_string(),
998 uuid
: Uuid
::generate(),
1002 write_media_label(worker
.clone(), &mut drive
, label
, pool
.clone())?
1012 schema
: DRIVE_NAME_SCHEMA
,
1017 description
: "A List of medium auxiliary memory attributes.",
1024 /// Read Cartridge Memory (Medium auxiliary memory attributes)
1025 pub async
fn cartridge_memory(drive
: String
) -> Result
<Vec
<MamAttribute
>, Error
> {
1026 run_drive_blocking_task(
1028 "reading cartridge memory".to_string(),
1030 let drive_config
: LinuxTapeDrive
= config
.lookup("linux", &drive
)?
;
1031 let mut handle
= drive_config
.open()?
;
1033 handle
.cartridge_memory()
1043 schema
: DRIVE_NAME_SCHEMA
,
1048 type: Lp17VolumeStatistics
,
1051 /// Read Volume Statistics (SCSI log page 17h)
1052 pub async
fn volume_statistics(drive
: String
) -> Result
<Lp17VolumeStatistics
, Error
> {
1053 run_drive_blocking_task(
1055 "reading volume statistics".to_string(),
1057 let drive_config
: LinuxTapeDrive
= config
.lookup("linux", &drive
)?
;
1058 let mut handle
= drive_config
.open()?
;
1060 handle
.volume_statistics()
1070 schema
: DRIVE_NAME_SCHEMA
,
1075 type: LinuxDriveAndMediaStatus
,
1078 /// Get drive/media status
1079 pub async
fn status(drive
: String
) -> Result
<LinuxDriveAndMediaStatus
, Error
> {
1080 run_drive_blocking_task(
1082 "reading drive status".to_string(),
1084 let drive_config
: LinuxTapeDrive
= config
.lookup("linux", &drive
)?
;
1086 // Note: use open_linux_tape_device, because this also works if no medium loaded
1087 let file
= open_linux_tape_device(&drive_config
.path
)?
;
1089 let mut handle
= LinuxTapeHandle
::new(file
);
1091 handle
.get_drive_and_media_status()
1101 schema
: DRIVE_NAME_SCHEMA
,
1104 description
: "Force overriding existing index.",
1109 description
: "Verbose mode - log all found chunks.",
1116 schema
: UPID_SCHEMA
,
1119 /// Scan media and record content
1120 pub fn catalog_media(
1122 force
: Option
<bool
>,
1123 verbose
: Option
<bool
>,
1124 rpcenv
: &mut dyn RpcEnvironment
,
1125 ) -> Result
<Value
, Error
> {
1126 let verbose
= verbose
.unwrap_or(false);
1127 let force
= force
.unwrap_or(false);
1129 let upid_str
= run_drive_worker(
1133 Some(drive
.clone()),
1134 move |worker
, config
| {
1135 let mut drive
= open_drive(&config
, &drive
)?
;
1139 let media_id
= match drive
.read_label()?
{
1140 (Some(media_id
), key_config
) => {
1142 "found media label: {}",
1143 serde_json
::to_string_pretty(&serde_json
::to_value(&media_id
)?
)?
1145 if key_config
.is_some() {
1147 "encryption key config: {}",
1148 serde_json
::to_string_pretty(&serde_json
::to_value(&key_config
)?
)?
1153 (None
, _
) => bail
!("media is empty (no media label found)"),
1156 let status_path
= Path
::new(TAPE_STATUS_DIR
);
1158 let mut inventory
= Inventory
::load(status_path
)?
;
1159 inventory
.store(media_id
.clone(), false)?
;
1161 let pool
= match media_id
.media_set_label
{
1163 worker
.log("media is empty");
1164 MediaCatalog
::destroy(status_path
, &media_id
.label
.uuid
)?
;
1168 if set
.uuid
.as_ref() == [0u8;16] { // media is empty
1169 worker
.log("media is empty");
1170 MediaCatalog
::destroy(status_path
, &media_id
.label
.uuid
)?
;
1173 let encrypt_fingerprint
= set
.encryption_key_fingerprint
.clone()
1174 .map(|fp
| (fp
, set
.uuid
.clone()));
1176 drive
.set_encryption(encrypt_fingerprint
)?
;
1182 let _lock
= MediaPool
::lock(status_path
, &pool
)?
;
1184 if MediaCatalog
::exists(status_path
, &media_id
.label
.uuid
) && !force
{
1185 bail
!("media catalog exists (please use --force to overwrite)");
1188 restore_media(&worker
, &mut drive
, &media_id
, None
, verbose
)?
;
1201 schema
: CHANGER_NAME_SCHEMA
,
1207 description
: "The list of configured drives with model information.",
1210 type: DriveListEntry
,
1214 description
: "List configured tape drives filtered by Tape.Audit privileges",
1215 permission
: &Permission
::Anybody
,
1220 changer
: Option
<String
>,
1222 rpcenv
: &mut dyn RpcEnvironment
,
1223 ) -> Result
<Vec
<DriveListEntry
>, Error
> {
1224 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
1225 let user_info
= CachedUserInfo
::new()?
;
1227 let (config
, _
) = config
::drive
::config()?
;
1229 let linux_drives
= linux_tape_device_list();
1231 let drive_list
: Vec
<LinuxTapeDrive
> = config
.convert_to_typed_array("linux")?
;
1233 let mut list
= Vec
::new();
1235 for drive
in drive_list
{
1236 if changer
.is_some() && drive
.changer
!= changer
{
1240 let privs
= user_info
.lookup_privs(&auth_id
, &["tape", "drive", &drive
.name
]);
1241 if (privs
& PRIV_TAPE_AUDIT
) == 0 {
1245 let info
= lookup_device_identification(&linux_drives
, &drive
.path
);
1246 let state
= get_tape_device_state(&config
, &drive
.name
)?
;
1247 let entry
= DriveListEntry { config: drive, info, state }
;
1255 pub const SUBDIRS
: SubdirMap
= &sorted
!([
1257 "barcode-label-media",
1259 .post(&API_METHOD_BARCODE_LABEL_MEDIA
)
1264 .post(&API_METHOD_CATALOG_MEDIA
)
1269 .put(&API_METHOD_CLEAN_DRIVE
)
1274 .post(&API_METHOD_EJECT_MEDIA
)
1279 .post(&API_METHOD_ERASE_MEDIA
)
1284 .put(&API_METHOD_EXPORT_MEDIA
)
1289 .get(&API_METHOD_INVENTORY
)
1290 .put(&API_METHOD_UPDATE_INVENTORY
)
1295 .post(&API_METHOD_LABEL_MEDIA
)
1300 .post(&API_METHOD_LOAD_MEDIA
)
1305 .put(&API_METHOD_LOAD_SLOT
)
1310 .get(&API_METHOD_CARTRIDGE_MEMORY
)
1313 "volume-statistics",
1315 .get(&API_METHOD_VOLUME_STATISTICS
)
1320 .get(&API_METHOD_READ_LABEL
)
1325 .post(&API_METHOD_RESTORE_KEY
)
1330 .post(&API_METHOD_REWIND
)
1335 .get(&API_METHOD_STATUS
)
1340 .post(&API_METHOD_UNLOAD
)
1344 const ITEM_ROUTER
: Router
= Router
::new()
1345 .get(&list_subdirs_api_method
!(SUBDIRS
))
1348 pub const ROUTER
: Router
= Router
::new()
1349 .get(&API_METHOD_LIST_DRIVES
)
1350 .match_all("drive", &ITEM_ROUTER
);