4 use anyhow
::{bail, Error}
;
10 list_subdirs_api_method
,
15 section_config
::SectionConfigData
,
31 MEDIA_POOL_NAME_SCHEMA
,
38 LinuxDriveAndMediaStatus
,
40 tape
::restore
::restore_media
,
49 linux_tape_device_list
,
50 lookup_device_identification
,
59 open_linux_tape_device
,
61 required_media_changer
,
65 changer
::update_changer_online_status
,
73 schema
: DRIVE_NAME_SCHEMA
,
76 schema
: MEDIA_LABEL_SCHEMA
,
81 /// Load media with specified label
83 /// Issue a media load request to the associated changer device.
84 pub async
fn load_media(drive
: String
, label_text
: String
) -> Result
<(), Error
> {
86 let (config
, _digest
) = config
::drive
::config()?
;
87 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
89 tokio
::task
::spawn_blocking(move || {
90 let _lock_guard
= lock_guard
; // keep lock guard
92 let (mut changer
, _
) = required_media_changer(&config
, &drive
)?
;
93 changer
.load_media(&label_text
)
101 schema
: DRIVE_NAME_SCHEMA
,
104 description
: "Source slot number.",
110 /// Load media from the specified slot
112 /// Issue a media load request to the associated changer device.
113 pub async
fn load_slot(drive
: String
, source_slot
: u64) -> Result
<(), Error
> {
115 let (config
, _digest
) = config
::drive
::config()?
;
116 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
118 tokio
::task
::spawn_blocking(move || {
119 let _lock_guard
= lock_guard
; // keep lock guard
121 let (mut changer
, _
) = required_media_changer(&config
, &drive
)?
;
122 changer
.load_media_from_slot(source_slot
)
130 schema
: DRIVE_NAME_SCHEMA
,
133 schema
: MEDIA_LABEL_SCHEMA
,
138 description
: "The import-export slot number the media was transfered to.",
143 /// Export media with specified label
144 pub async
fn export_media(drive
: String
, label_text
: String
) -> Result
<u64, Error
> {
146 let (config
, _digest
) = config
::drive
::config()?
;
147 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
149 tokio
::task
::spawn_blocking(move || {
150 let _lock_guard
= lock_guard
; // keep lock guard
152 let (mut changer
, changer_name
) = required_media_changer(&config
, &drive
)?
;
153 match changer
.export_media(&label_text
)?
{
154 Some(slot
) => Ok(slot
),
155 None
=> bail
!("media '{}' is not online (via changer '{}')", label_text
, changer_name
),
164 schema
: DRIVE_NAME_SCHEMA
,
167 description
: "Target slot number. If omitted, defaults to the slot that the drive was loaded from.",
174 /// Unload media via changer
177 target_slot
: Option
<u64>,
179 ) -> Result
<(), Error
> {
181 let (config
, _digest
) = config
::drive
::config()?
;
182 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
184 tokio
::task
::spawn_blocking(move || {
185 let _lock_guard
= lock_guard
; // keep lock guard
187 let (mut changer
, _
) = required_media_changer(&config
, &drive
)?
;
188 changer
.unload_media(target_slot
)
196 schema
: DRIVE_NAME_SCHEMA
,
199 description
: "Use fast erase.",
214 rpcenv
: &mut dyn RpcEnvironment
,
215 ) -> Result
<Value
, Error
> {
217 let (config
, _digest
) = config
::drive
::config()?
;
219 // early check/lock before starting worker
220 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
222 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
224 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
226 let upid_str
= WorkerTask
::new_thread(
232 let _lock_guard
= lock_guard
; // keep lock guard
234 let mut drive
= open_drive(&config
, &drive
)?
;
235 drive
.erase_media(fast
.unwrap_or(true))?
;
247 schema
: DRIVE_NAME_SCHEMA
,
258 rpcenv
: &mut dyn RpcEnvironment
,
259 ) -> Result
<Value
, Error
> {
261 let (config
, _digest
) = config
::drive
::config()?
;
263 // early check/lock before starting worker
264 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
266 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
268 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
270 let upid_str
= WorkerTask
::new_thread(
276 let _lock_guard
= lock_guard
; // keep lock guard
277 let mut drive
= open_drive(&config
, &drive
)?
;
290 schema
: DRIVE_NAME_SCHEMA
,
298 /// Eject/Unload drive media
301 rpcenv
: &mut dyn RpcEnvironment
,
302 ) -> Result
<Value
, Error
> {
304 let (config
, _digest
) = config
::drive
::config()?
;
306 // early check/lock before starting worker
307 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
309 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
311 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
313 let upid_str
= WorkerTask
::new_thread(
319 let _lock_guard
= lock_guard
; // keep lock guard
321 if let Some((mut changer
, _
)) = media_changer(&config
, &drive
)?
{
322 changer
.unload_media(None
)?
;
324 let mut drive
= open_drive(&config
, &drive
)?
;
325 drive
.eject_media()?
;
337 schema
: DRIVE_NAME_SCHEMA
,
340 schema
: MEDIA_LABEL_SCHEMA
,
343 schema
: MEDIA_POOL_NAME_SCHEMA
,
354 /// Write a new media label to the media in 'drive'. The media is
355 /// assigned to the specified 'pool', or else to the free media pool.
357 /// Note: The media need to be empty (you may want to erase it first).
360 pool
: Option
<String
>,
362 rpcenv
: &mut dyn RpcEnvironment
,
363 ) -> Result
<Value
, Error
> {
365 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
367 if let Some(ref pool
) = pool
{
368 let (pool_config
, _digest
) = config
::media_pool
::config()?
;
370 if pool_config
.sections
.get(pool
).is_none() {
371 bail
!("no such pool ('{}')", pool
);
375 let (config
, _digest
) = config
::drive
::config()?
;
377 // early check/lock before starting worker
378 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
380 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
382 let upid_str
= WorkerTask
::new_thread(
388 let _lock_guard
= lock_guard
; // keep lock guard
390 let mut drive
= open_drive(&config
, &drive
)?
;
394 match drive
.read_next_file() {
395 Ok(Some(_file
)) => bail
!("media is not empty (erase first)"),
396 Ok(None
) => { /* EOF mark at BOT, assume tape is empty */ }
,
398 if err
.is_errno(nix
::errno
::Errno
::ENOSPC
) || err
.is_errno(nix
::errno
::Errno
::EIO
) {
399 /* assume tape is empty */
401 bail
!("media read error - {}", err
);
406 let ctime
= proxmox
::tools
::time
::epoch_i64();
407 let label
= MediaLabel
{
408 label_text
: label_text
.to_string(),
409 uuid
: Uuid
::generate(),
413 write_media_label(worker
, &mut drive
, label
, pool
)
420 fn write_media_label(
421 worker
: Arc
<WorkerTask
>,
422 drive
: &mut Box
<dyn TapeDriver
>,
424 pool
: Option
<String
>,
425 ) -> Result
<(), Error
> {
427 drive
.label_tape(&label
)?
;
429 let mut media_set_label
= None
;
431 if let Some(ref pool
) = pool
{
432 // assign media to pool by writing special media set label
433 worker
.log(format
!("Label media '{}' for pool '{}'", label
.label_text
, pool
));
434 let set
= MediaSetLabel
::with_data(&pool
, [0u8; 16].into(), 0, label
.ctime
, None
);
436 drive
.write_media_set_label(&set
, None
)?
;
437 media_set_label
= Some(set
);
439 worker
.log(format
!("Label media '{}' (no pool assignment)", label
.label_text
));
442 let media_id
= MediaId { label, media_set_label }
;
444 let status_path
= Path
::new(TAPE_STATUS_DIR
);
446 // Create the media catalog
447 MediaCatalog
::overwrite(status_path
, &media_id
, false)?
;
449 let mut inventory
= Inventory
::load(status_path
)?
;
450 inventory
.store(media_id
.clone(), false)?
;
454 match drive
.read_label() {
455 Ok((Some(info
), _
)) => {
456 if info
.label
.uuid
!= media_id
.label
.uuid
{
457 bail
!("verify label failed - got wrong label uuid");
459 if let Some(ref pool
) = pool
{
460 match info
.media_set_label
{
462 if set
.uuid
!= [0u8; 16].into() {
463 bail
!("verify media set label failed - got wrong set uuid");
465 if &set
.pool
!= pool
{
466 bail
!("verify media set label failed - got wrong pool");
470 bail
!("verify media set label failed (missing set label)");
475 Ok((None
, _
)) => bail
!("verify label failed (got empty media)"),
476 Err(err
) => bail
!("verify label failed - {}", err
),
489 schema
: DRIVE_NAME_SCHEMA
,
492 description
: "Encryption key password.",
497 /// Try to restore a tape encryption key
498 pub async
fn restore_key(
501 ) -> Result
<(), Error
> {
503 let (config
, _digest
) = config
::drive
::config()?
;
505 // early check/lock before starting worker
506 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
508 tokio
::task
::spawn_blocking(move || {
509 let _lock_guard
= lock_guard
; // keep lock guard
511 let mut drive
= open_drive(&config
, &drive
)?
;
513 let (_media_id
, key_config
) = drive
.read_label()?
;
515 if let Some(key_config
) = key_config
{
516 let password_fn
= || { Ok(password.as_bytes().to_vec()) }
;
517 let (key
, ..) = key_config
.decrypt(&password_fn
)?
;
518 config
::tape_encryption_keys
::insert_key(key
, key_config
, true)?
;
520 bail
!("media does not contain any encryption key configuration");
531 schema
: DRIVE_NAME_SCHEMA
,
534 description
: "Inventorize media",
543 /// Read media label (optionally inventorize media)
544 pub async
fn read_label(
546 inventorize
: Option
<bool
>,
547 ) -> Result
<MediaIdFlat
, Error
> {
549 let (config
, _digest
) = config
::drive
::config()?
;
551 // early check/lock before starting worker
552 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
554 tokio
::task
::spawn_blocking(move || {
555 let _lock_guard
= lock_guard
; // keep lock guard
557 let mut drive
= open_drive(&config
, &drive
)?
;
559 let (media_id
, _key_config
) = drive
.read_label()?
;
561 let media_id
= match media_id
{
563 let mut flat
= MediaIdFlat
{
564 uuid
: media_id
.label
.uuid
.clone(),
565 label_text
: media_id
.label
.label_text
.clone(),
566 ctime
: media_id
.label
.ctime
,
567 media_set_ctime
: None
,
568 media_set_uuid
: None
,
569 encryption_key_fingerprint
: None
,
573 if let Some(ref set
) = media_id
.media_set_label
{
574 flat
.pool
= Some(set
.pool
.clone());
575 flat
.seq_nr
= Some(set
.seq_nr
);
576 flat
.media_set_uuid
= Some(set
.uuid
.clone());
577 flat
.media_set_ctime
= Some(set
.ctime
);
578 flat
.encryption_key_fingerprint
= set
579 .encryption_key_fingerprint
581 .map(|fp
| crate::tools
::format
::as_fingerprint(fp
.bytes()));
583 let encrypt_fingerprint
= set
.encryption_key_fingerprint
.clone()
584 .map(|fp
| (fp
, set
.uuid
.clone()));
586 if let Err(err
) = drive
.set_encryption(encrypt_fingerprint
) {
587 // try, but ignore errors. just log to stderr
588 eprintln
!("uable to load encryption key: {}", err
);
592 if let Some(true) = inventorize
{
593 let state_path
= Path
::new(TAPE_STATUS_DIR
);
594 let mut inventory
= Inventory
::load(state_path
)?
;
595 inventory
.store(media_id
, false)?
;
601 bail
!("Media is empty (no label).");
613 schema
: DRIVE_NAME_SCHEMA
,
624 rpcenv
: &mut dyn RpcEnvironment
,
625 ) -> Result
<Value
, Error
> {
627 let (config
, _digest
) = config
::drive
::config()?
;
629 // early check/lock before starting worker
630 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
632 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
634 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
636 let upid_str
= WorkerTask
::new_thread(
642 let _lock_guard
= lock_guard
; // keep lock guard
644 let (mut changer
, _changer_name
) = required_media_changer(&config
, &drive
)?
;
646 worker
.log("Starting drive clean");
648 changer
.clean_drive()?
;
650 worker
.log("Drive cleaned sucessfully");
662 schema
: DRIVE_NAME_SCHEMA
,
667 description
: "The list of media labels with associated media Uuid (if any).",
674 /// List known media labels (Changer Inventory)
676 /// Note: Only useful for drives with associated changer device.
678 /// This method queries the changer to get a list of media labels.
680 /// Note: This updates the media online status.
681 pub async
fn inventory(
683 ) -> Result
<Vec
<LabelUuidMap
>, Error
> {
685 let (config
, _digest
) = config
::drive
::config()?
;
687 // early check/lock before starting worker
688 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
690 tokio
::task
::spawn_blocking(move || {
691 let _lock_guard
= lock_guard
; // keep lock guard
693 let (mut changer
, changer_name
) = required_media_changer(&config
, &drive
)?
;
695 let label_text_list
= changer
.online_media_label_texts()?
;
697 let state_path
= Path
::new(TAPE_STATUS_DIR
);
699 let mut inventory
= Inventory
::load(state_path
)?
;
701 update_changer_online_status(
708 let mut list
= Vec
::new();
710 for label_text
in label_text_list
.iter() {
711 if label_text
.starts_with("CLN") {
712 // skip cleaning unit
716 let label_text
= label_text
.to_string();
718 if let Some(media_id
) = inventory
.find_media_by_label_text(&label_text
) {
719 list
.push(LabelUuidMap { label_text, uuid: Some(media_id.label.uuid.clone()) }
);
721 list
.push(LabelUuidMap { label_text, uuid: None }
);
733 schema
: DRIVE_NAME_SCHEMA
,
736 description
: "Load all tapes and try read labels (even if already inventoried)",
748 /// Note: Only useful for drives with associated changer device.
750 /// This method queries the changer to get a list of media labels. It
751 /// then loads any unknown media into the drive, reads the label, and
752 /// store the result to the media database.
754 /// Note: This updates the media online status.
755 pub fn update_inventory(
757 read_all_labels
: Option
<bool
>,
758 rpcenv
: &mut dyn RpcEnvironment
,
759 ) -> Result
<Value
, Error
> {
761 let (config
, _digest
) = config
::drive
::config()?
;
763 // early check/lock before starting worker
764 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
766 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
768 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
770 let upid_str
= WorkerTask
::new_thread(
776 let _lock_guard
= lock_guard
; // keep lock guard
778 let (mut changer
, changer_name
) = required_media_changer(&config
, &drive
)?
;
780 let label_text_list
= changer
.online_media_label_texts()?
;
781 if label_text_list
.is_empty() {
782 worker
.log("changer device does not list any media labels".to_string());
785 let state_path
= Path
::new(TAPE_STATUS_DIR
);
787 let mut inventory
= Inventory
::load(state_path
)?
;
789 update_changer_online_status(&config
, &mut inventory
, &changer_name
, &label_text_list
)?
;
791 for label_text
in label_text_list
.iter() {
792 if label_text
.starts_with("CLN") {
793 worker
.log(format
!("skip cleaning unit '{}'", label_text
));
797 let label_text
= label_text
.to_string();
799 if !read_all_labels
.unwrap_or(false) && inventory
.find_media_by_label_text(&label_text
).is_some() {
800 worker
.log(format
!("media '{}' already inventoried", label_text
));
804 if let Err(err
) = changer
.load_media(&label_text
) {
805 worker
.warn(format
!("unable to load media '{}' - {}", label_text
, err
));
809 let mut drive
= open_drive(&config
, &drive
)?
;
810 match drive
.read_label() {
812 worker
.warn(format
!("unable to read label form media '{}' - {}", label_text
, err
));
815 worker
.log(format
!("media '{}' is empty", label_text
));
817 Ok((Some(media_id
), _key_config
)) => {
818 if label_text
!= media_id
.label
.label_text
{
819 worker
.warn(format
!("label text missmatch ({} != {})", label_text
, media_id
.label
.label_text
));
822 worker
.log(format
!("inventorize media '{}' with uuid '{}'", label_text
, media_id
.label
.uuid
));
823 inventory
.store(media_id
, false)?
;
826 changer
.unload_media(None
)?
;
840 schema
: DRIVE_NAME_SCHEMA
,
843 schema
: MEDIA_POOL_NAME_SCHEMA
,
852 /// Label media with barcodes from changer device
853 pub fn barcode_label_media(
855 pool
: Option
<String
>,
856 rpcenv
: &mut dyn RpcEnvironment
,
857 ) -> Result
<Value
, Error
> {
859 if let Some(ref pool
) = pool
{
860 let (pool_config
, _digest
) = config
::media_pool
::config()?
;
862 if pool_config
.sections
.get(pool
).is_none() {
863 bail
!("no such pool ('{}')", pool
);
867 let (drive_config
, _digest
) = config
::drive
::config()?
;
869 // early check/lock before starting worker
870 let lock_guard
= lock_tape_device(&drive_config
, &drive
)?
;
872 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
874 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
876 let upid_str
= WorkerTask
::new_thread(
877 "barcode-label-media",
882 let _lock_guard
= lock_guard
; // keep lock guard
883 barcode_label_media_worker(worker
, drive
, &drive_config
, pool
)
890 fn barcode_label_media_worker(
891 worker
: Arc
<WorkerTask
>,
893 drive_config
: &SectionConfigData
,
894 pool
: Option
<String
>,
895 ) -> Result
<(), Error
> {
897 let (mut changer
, changer_name
) = required_media_changer(drive_config
, &drive
)?
;
899 let label_text_list
= changer
.online_media_label_texts()?
;
901 let state_path
= Path
::new(TAPE_STATUS_DIR
);
903 let mut inventory
= Inventory
::load(state_path
)?
;
905 update_changer_online_status(drive_config
, &mut inventory
, &changer_name
, &label_text_list
)?
;
907 if label_text_list
.is_empty() {
908 bail
!("changer device does not list any media labels");
911 for label_text
in label_text_list
{
912 if label_text
.starts_with("CLN") { continue; }
915 if inventory
.find_media_by_label_text(&label_text
).is_some() {
916 worker
.log(format
!("media '{}' already inventoried (already labeled)", label_text
));
920 worker
.log(format
!("checking/loading media '{}'", label_text
));
922 if let Err(err
) = changer
.load_media(&label_text
) {
923 worker
.warn(format
!("unable to load media '{}' - {}", label_text
, err
));
927 let mut drive
= open_drive(drive_config
, &drive
)?
;
930 match drive
.read_next_file() {
932 worker
.log(format
!("media '{}' is not empty (erase first)", label_text
));
935 Ok(None
) => { /* EOF mark at BOT, assume tape is empty */ }
,
937 if err
.is_errno(nix
::errno
::Errno
::ENOSPC
) || err
.is_errno(nix
::errno
::Errno
::EIO
) {
938 /* assume tape is empty */
940 worker
.warn(format
!("media '{}' read error (maybe not empty - erase first)", label_text
));
946 let ctime
= proxmox
::tools
::time
::epoch_i64();
947 let label
= MediaLabel
{
948 label_text
: label_text
.to_string(),
949 uuid
: Uuid
::generate(),
953 write_media_label(worker
.clone(), &mut drive
, label
, pool
.clone())?
963 schema
: DRIVE_NAME_SCHEMA
,
968 description
: "A List of medium auxiliary memory attributes.",
975 /// Read Cartridge Memory (Medium auxiliary memory attributes)
976 pub fn cartridge_memory(drive
: String
) -> Result
<Vec
<MamAttribute
>, Error
> {
978 let (config
, _digest
) = config
::drive
::config()?
;
980 let _lock_guard
= lock_tape_device(&config
, &drive
)?
;
982 let drive_config
: LinuxTapeDrive
= config
.lookup("linux", &drive
)?
;
983 let mut handle
= drive_config
.open()?
;
985 handle
.cartridge_memory()
992 schema
: DRIVE_NAME_SCHEMA
,
997 type: Lp17VolumeStatistics
,
1000 /// Read Volume Statistics (SCSI log page 17h)
1001 pub fn volume_statistics(drive
: String
) -> Result
<Lp17VolumeStatistics
, Error
> {
1003 let (config
, _digest
) = config
::drive
::config()?
;
1005 let _lock_guard
= lock_tape_device(&config
, &drive
)?
;
1007 let drive_config
: LinuxTapeDrive
= config
.lookup("linux", &drive
)?
;
1008 let mut handle
= drive_config
.open()?
;
1010 handle
.volume_statistics()
1017 schema
: DRIVE_NAME_SCHEMA
,
1022 type: LinuxDriveAndMediaStatus
,
1025 /// Get drive/media status
1026 pub fn status(drive
: String
) -> Result
<LinuxDriveAndMediaStatus
, Error
> {
1028 let (config
, _digest
) = config
::drive
::config()?
;
1030 let _lock_guard
= lock_tape_device(&config
, &drive
)?
;
1032 let drive_config
: LinuxTapeDrive
= config
.lookup("linux", &drive
)?
;
1034 // Note: use open_linux_tape_device, because this also works if no medium loaded
1035 let file
= open_linux_tape_device(&drive_config
.path
)?
;
1037 let mut handle
= LinuxTapeHandle
::new(file
);
1039 handle
.get_drive_and_media_status()
1046 schema
: DRIVE_NAME_SCHEMA
,
1049 description
: "Force overriding existing index.",
1054 description
: "Verbose mode - log all found chunks.",
1061 schema
: UPID_SCHEMA
,
1064 /// Scan media and record content
1065 pub fn catalog_media(
1067 force
: Option
<bool
>,
1068 verbose
: Option
<bool
>,
1069 rpcenv
: &mut dyn RpcEnvironment
,
1070 ) -> Result
<Value
, Error
> {
1072 let verbose
= verbose
.unwrap_or(false);
1073 let force
= force
.unwrap_or(false);
1075 let (config
, _digest
) = config
::drive
::config()?
;
1077 // early check/lock before starting worker
1078 let lock_guard
= lock_tape_device(&config
, &drive
)?
;
1080 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
1082 let to_stdout
= rpcenv
.env_type() == RpcEnvironmentType
::CLI
;
1084 let upid_str
= WorkerTask
::new_thread(
1086 Some(drive
.clone()),
1090 let _lock_guard
= lock_guard
; // keep lock guard
1092 let mut drive
= open_drive(&config
, &drive
)?
;
1096 let media_id
= match drive
.read_label()?
{
1097 (Some(media_id
), key_config
) => {
1099 "found media label: {}",
1100 serde_json
::to_string_pretty(&serde_json
::to_value(&media_id
)?
)?
1102 if key_config
.is_some() {
1104 "encryption key config: {}",
1105 serde_json
::to_string_pretty(&serde_json
::to_value(&key_config
)?
)?
1110 (None
, _
) => bail
!("media is empty (no media label found)"),
1113 let status_path
= Path
::new(TAPE_STATUS_DIR
);
1115 let mut inventory
= Inventory
::load(status_path
)?
;
1116 inventory
.store(media_id
.clone(), false)?
;
1118 let pool
= match media_id
.media_set_label
{
1120 worker
.log("media is empty");
1121 MediaCatalog
::destroy(status_path
, &media_id
.label
.uuid
)?
;
1125 if set
.uuid
.as_ref() == [0u8;16] { // media is empty
1126 worker
.log("media is empty");
1127 MediaCatalog
::destroy(status_path
, &media_id
.label
.uuid
)?
;
1130 let encrypt_fingerprint
= set
.encryption_key_fingerprint
.clone()
1131 .map(|fp
| (fp
, set
.uuid
.clone()));
1133 drive
.set_encryption(encrypt_fingerprint
)?
;
1139 let _lock
= MediaPool
::lock(status_path
, &pool
)?
;
1141 if MediaCatalog
::exists(status_path
, &media_id
.label
.uuid
) && !force
{
1142 bail
!("media catalog exists (please use --force to overwrite)");
1145 restore_media(&worker
, &mut drive
, &media_id
, None
, verbose
)?
;
1159 schema
: CHANGER_NAME_SCHEMA
,
1165 description
: "The list of configured drives with model information.",
1168 type: DriveListEntry
,
1174 changer
: Option
<String
>,
1176 ) -> Result
<Vec
<DriveListEntry
>, Error
> {
1178 let (config
, _
) = config
::drive
::config()?
;
1180 let linux_drives
= linux_tape_device_list();
1182 let drive_list
: Vec
<LinuxTapeDrive
> = config
.convert_to_typed_array("linux")?
;
1184 let mut list
= Vec
::new();
1186 for drive
in drive_list
{
1187 if changer
.is_some() && drive
.changer
!= changer
{
1191 let info
= lookup_device_identification(&linux_drives
, &drive
.path
);
1192 let entry
= DriveListEntry { config: drive, info }
;
1200 pub const SUBDIRS
: SubdirMap
= &sorted
!([
1202 "barcode-label-media",
1204 .post(&API_METHOD_BARCODE_LABEL_MEDIA
)
1209 .post(&API_METHOD_CATALOG_MEDIA
)
1214 .put(&API_METHOD_CLEAN_DRIVE
)
1219 .post(&API_METHOD_EJECT_MEDIA
)
1224 .post(&API_METHOD_ERASE_MEDIA
)
1229 .put(&API_METHOD_EXPORT_MEDIA
)
1234 .get(&API_METHOD_INVENTORY
)
1235 .put(&API_METHOD_UPDATE_INVENTORY
)
1240 .post(&API_METHOD_LABEL_MEDIA
)
1245 .put(&API_METHOD_LOAD_MEDIA
)
1250 .put(&API_METHOD_LOAD_SLOT
)
1255 .get(&API_METHOD_CARTRIDGE_MEMORY
)
1258 "volume-statistics",
1260 .get(&API_METHOD_VOLUME_STATISTICS
)
1265 .get(&API_METHOD_READ_LABEL
)
1270 .post(&API_METHOD_REWIND
)
1275 .get(&API_METHOD_STATUS
)
1280 .put(&API_METHOD_UNLOAD
)
1284 const ITEM_ROUTER
: Router
= Router
::new()
1285 .get(&list_subdirs_api_method
!(SUBDIRS
))
1288 pub const ROUTER
: Router
= Router
::new()
1289 .get(&API_METHOD_LIST_DRIVES
)
1290 .match_all("drive", &ITEM_ROUTER
);