tape: save 'bytes used' in tape inventory

and show them on the ui. This can help uses with seeing how much a tape
is used.

The value is updated on 'commit' and when the tape is changed during a
backup.

For drives not supporting the volume statistics, this is simply skipped.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Dominik Csapak 2024-05-13 12:46:09 +02:00 committed by Dietmar Maurer
parent aea66a8128
commit 4b21a00744
9 changed files with 98 additions and 4 deletions

View File

@ -81,6 +81,9 @@ pub struct MediaListEntry {
/// Media Pool
#[serde(skip_serializing_if = "Option::is_none")]
pub pool: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Bytes currently used
pub bytes_used: Option<u64>,
}
#[api(

View File

@ -204,6 +204,7 @@ pub async fn list_media(
media_set_uuid,
media_set_name,
seq_nr,
bytes_used: media.bytes_used(),
});
}
}
@ -232,6 +233,7 @@ pub async fn list_media(
media_set_ctime: None,
seq_nr: None,
pool: None,
bytes_used: inventory.get_media_bytes_used(&media_id.label.uuid),
});
}
}
@ -279,6 +281,7 @@ pub async fn list_media(
media_set_uuid,
media_set_name,
seq_nr,
bytes_used: inventory.get_media_bytes_used(&media_id.label.uuid),
});
}

View File

@ -268,6 +268,10 @@ impl TapeDriver for LtoTapeHandle {
}
Ok(())
}
fn get_volume_statistics(&mut self) -> Result<pbs_api_types::Lp17VolumeStatistics, Error> {
self.volume_statistics()
}
}
fn run_sg_tape_cmd(subcmd: &str, args: &[&str], fd: RawFd) -> Result<String, Error> {

View File

@ -242,6 +242,9 @@ pub trait TapeDriver {
}
Ok(())
}
/// Returns volume statistics from a loaded tape
fn get_volume_statistics(&mut self) -> Result<pbs_api_types::Lp17VolumeStatistics, Error>;
}
/// A boxed implementor of [`MediaChange`].

View File

@ -461,6 +461,10 @@ impl TapeDriver for VirtualTapeHandle {
let status = VirtualDriveStatus { current_tape: None };
self.store_status(&status)
}
fn get_volume_statistics(&mut self) -> Result<pbs_api_types::Lp17VolumeStatistics, Error> {
Ok(Default::default())
}
}
impl MediaChange for VirtualTapeHandle {

View File

@ -84,6 +84,8 @@ struct MediaStateEntry {
location: Option<MediaLocation>,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<MediaStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
bytes_used: Option<u64>,
}
/// Media Inventory
@ -211,6 +213,7 @@ impl Inventory {
} else {
previous.status
},
bytes_used: previous.bytes_used,
};
self.map.insert(uuid, entry);
} else {
@ -218,6 +221,7 @@ impl Inventory {
id: media_id,
location: None,
status: None,
bytes_used: None,
};
self.map.insert(uuid, entry);
}
@ -720,6 +724,32 @@ impl Inventory {
self.set_media_location(uuid, Some(MediaLocation::Offline))
}
/// Lock database, reload database, set bytes used for media, store database
pub fn set_media_bytes_used(
&mut self,
uuid: &Uuid,
bytes_used: Option<u64>,
) -> Result<(), Error> {
let _lock = self.lock()?;
self.map = self.load_media_db()?;
if let Some(entry) = self.map.get_mut(uuid) {
entry.bytes_used = bytes_used;
self.update_helpers();
self.replace_file()?;
Ok(())
} else {
bail!("no such media '{}'", uuid);
}
}
/// Returns bytes used of the given media, if set
pub fn get_media_bytes_used(&self, uuid: &Uuid) -> Option<u64> {
match self.map.get(uuid) {
Some(entry) => entry.bytes_used,
None => None,
}
}
/// Update online status
pub fn update_online_status(&mut self, online_map: &OnlineStatusMap) -> Result<(), Error> {
let _lock = self.lock()?;

View File

@ -212,8 +212,11 @@ impl MediaPool {
}
let (status, location) = self.compute_media_state(&media_id);
let bytes_used = self.inventory.get_media_bytes_used(uuid);
Ok(BackupMedia::with_media_id(media_id, location, status))
Ok(BackupMedia::with_media_id(
media_id, location, status, bytes_used,
))
}
/// List all media associated with this pool
@ -224,7 +227,8 @@ impl MediaPool {
.into_iter()
.map(|media_id| {
let (status, location) = self.compute_media_state(&media_id);
BackupMedia::with_media_id(media_id, location, status)
let bytes_used = self.inventory.get_media_bytes_used(&media_id.label.uuid);
BackupMedia::with_media_id(media_id, location, status, bytes_used)
})
.collect()
}
@ -238,6 +242,15 @@ impl MediaPool {
Ok(())
}
/// Update bytes used for media in inventory
pub fn set_media_bytes_used(
&mut self,
uuid: &Uuid,
bytes_used: Option<u64>,
) -> Result<(), Error> {
self.inventory.set_media_bytes_used(uuid, bytes_used)
}
/// Make sure the current media set is usable for writing
///
/// If not, starts a new media set. Also creates a new
@ -715,15 +728,23 @@ pub struct BackupMedia {
location: MediaLocation,
/// Media status
status: MediaStatus,
/// Bytes used
bytes_used: Option<u64>,
}
impl BackupMedia {
/// Creates a new instance
pub fn with_media_id(id: MediaId, location: MediaLocation, status: MediaStatus) -> Self {
pub fn with_media_id(
id: MediaId,
location: MediaLocation,
status: MediaStatus,
bytes_used: Option<u64>,
) -> Self {
Self {
id,
location,
status,
bytes_used,
}
}
@ -776,4 +797,9 @@ impl BackupMedia {
pub fn label_text(&self) -> &str {
&self.id.label.label_text
}
/// Returns the bytes used, if set
pub fn bytes_used(&self) -> Option<u64> {
self.bytes_used
}
}

View File

@ -203,6 +203,14 @@ impl PoolWriter {
if let Some(ref mut status) = self.status {
status.drive.sync()?; // sync all data to the tape
status.bytes_written_after_sync = 0; // reset bytes written
// not all drives support that
if let Ok(stats) = status.drive.get_volume_statistics() {
self.pool.set_media_bytes_used(
&status.media_uuid,
Some(stats.total_used_native_capacity),
)?;
}
}
self.catalog_set.lock().unwrap().commit()?; // then commit the catalog
Ok(())
@ -237,7 +245,13 @@ impl PoolWriter {
);
if let Some(PoolWriterState { mut drive, .. }) = self.status.take() {
if last_media_uuid.is_some() {
if let Some(uuid) = &last_media_uuid {
// not all drives support that
if let Ok(stats) = drive.get_volume_statistics() {
self.pool
.set_media_bytes_used(uuid, Some(stats.total_used_native_capacity))?;
}
task_log!(worker, "eject current media");
drive.eject_media()?;
}

View File

@ -16,6 +16,7 @@ Ext.define('pbs-model-tapes', {
'seq-nr',
'status',
'uuid',
'bytes-used',
],
idProperty: 'uuid',
proxy: {
@ -326,5 +327,11 @@ Ext.define('PBS.TapeManagement.TapeInventory', {
flex: 1,
hidden: true,
},
{
text: gettext("Bytes Used"),
dataIndex: 'bytes-used',
flex: 1,
renderer: Proxmox.Utils.render_size,
},
],
});