diff --git a/src/api2/tape/media.rs b/src/api2/tape/media.rs index a87cad0f..683ff1b9 100644 --- a/src/api2/tape/media.rs +++ b/src/api2/tape/media.rs @@ -58,12 +58,15 @@ pub async fn list_media(pool: Option) -> Result, Err let status_path = Path::new(TAPE_STATUS_DIR); - tokio::task::spawn_blocking(move || { + let catalogs = tokio::task::spawn_blocking(move || { + // update online media status if let Err(err) = update_online_status(status_path) { eprintln!("{}", err); eprintln!("update online media status failed - using old state"); } - }).await?; + // test what catalog files we have + MediaCatalog::media_with_catalogs(status_path) + }).await??; let mut list = Vec::new(); @@ -100,12 +103,20 @@ pub async fn list_media(pool: Option) -> Result, Err .unwrap_or_else(|_| set.uuid.to_string()) }); + let catalog_ok = if media.media_set_label().is_none() { + // Media is empty, we need no catalog + true + } else { + catalogs.contains(media.uuid()) + }; + list.push(MediaListEntry { uuid: media.uuid().to_string(), changer_id: media.changer_id().to_string(), pool: Some(pool_name.to_string()), location: media.location().clone(), status: *media.status(), + catalog: catalog_ok, expired, media_set_uuid, media_set_name, @@ -131,6 +142,7 @@ pub async fn list_media(pool: Option) -> Result, Err changer_id: media_id.label.changer_id.to_string(), location, status, + catalog: true, // empty, so we do not need a catalog expired: false, media_set_uuid: None, media_set_name: None, diff --git a/src/api2/types/tape/media.rs b/src/api2/types/tape/media.rs index 6f0fa15c..4df5a671 100644 --- a/src/api2/types/tape/media.rs +++ b/src/api2/types/tape/media.rs @@ -29,6 +29,8 @@ pub struct MediaListEntry { pub status: MediaStatus, /// Expired flag pub expired: bool, + /// Catalog status OK + pub catalog: bool, /// Media set name #[serde(skip_serializing_if="Option::is_none")] pub media_set_name: Option, diff --git a/src/bin/proxmox_tape/media.rs b/src/bin/proxmox_tape/media.rs index 6f694b8c..dd1118c6 100644 --- a/src/bin/proxmox_tape/media.rs +++ b/src/bin/proxmox_tape/media.rs @@ -104,6 +104,14 @@ async fn list_media( }) } + fn catalog_status(value: &Value, _record: &Value) -> Result { + let catalog_ok = value.as_bool().unwrap(); + if catalog_ok { + Ok(String::from("ok")) + } else { + Ok(String::from("missing")) + } + } let options = default_table_format_options() .sortby("pool", false) .sortby("media-set-uuid", false) @@ -115,6 +123,7 @@ async fn list_media( .column(ColumnConfig::new("seq-nr")) .column(ColumnConfig::new("status").renderer(render_status)) .column(ColumnConfig::new("location")) + .column(ColumnConfig::new("catalog").renderer(catalog_status)) .column(ColumnConfig::new("uuid")) .column(ColumnConfig::new("media-set-uuid")) ; diff --git a/src/tape/media_catalog.rs b/src/tape/media_catalog.rs index b5306ff0..a3dd04db 100644 --- a/src/tape/media_catalog.rs +++ b/src/tape/media_catalog.rs @@ -3,7 +3,7 @@ use std::fs::File; use std::io::{Write, Read, BufReader, Seek, SeekFrom}; use std::os::unix::io::AsRawFd; use std::path::Path; -use std::collections::HashMap; +use std::collections::{HashSet, HashMap}; use anyhow::{bail, format_err, Error}; use endian_trait::Endian; @@ -22,6 +22,7 @@ use proxmox::tools::{ }; use crate::{ + tools::fs::read_subdir, backup::BackupDir, tape::{ MediaId, @@ -58,6 +59,22 @@ pub struct MediaCatalog { impl MediaCatalog { + /// List media with catalogs + pub fn media_with_catalogs(base_path: &Path) -> Result, Error> { + let mut catalogs = HashSet::new(); + + for entry in read_subdir(libc::AT_FDCWD, base_path)? { + let entry = entry?; + let name = unsafe { entry.file_name_utf8_unchecked() }; + if !name.ends_with(".log") { continue; } + if let Ok(uuid) = Uuid::parse_str(&name[..(name.len()-4)]) { + catalogs.insert(uuid); + } + } + + Ok(catalogs) + } + /// Test if a catalog exists pub fn exists(base_path: &Path, uuid: &Uuid) -> bool { let mut path = base_path.to_owned();