[pbs-devel] [PATCH proxmox-backup] tape: save 'bytes used' in tape inventory
Dominik Csapak
d.csapak at proxmox.com
Mon May 13 12:46:09 CEST 2024
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 at proxmox.com>
---
pbs-api-types/src/tape/media.rs | 3 +++
src/api2/tape/media.rs | 3 +++
src/tape/drive/lto/mod.rs | 4 ++++
src/tape/drive/mod.rs | 3 +++
src/tape/drive/virtual_tape.rs | 4 ++++
src/tape/inventory.rs | 30 ++++++++++++++++++++++++++++++
src/tape/media_pool.rs | 32 +++++++++++++++++++++++++++++---
src/tape/pool_writer/mod.rs | 16 +++++++++++++++-
www/tape/TapeInventory.js | 7 +++++++
9 files changed, 98 insertions(+), 4 deletions(-)
diff --git a/pbs-api-types/src/tape/media.rs b/pbs-api-types/src/tape/media.rs
index 6792cd3c9..6227f4634 100644
--- a/pbs-api-types/src/tape/media.rs
+++ b/pbs-api-types/src/tape/media.rs
@@ -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(
diff --git a/src/api2/tape/media.rs b/src/api2/tape/media.rs
index 07bed86a2..159906ba7 100644
--- a/src/api2/tape/media.rs
+++ b/src/api2/tape/media.rs
@@ -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),
});
}
diff --git a/src/tape/drive/lto/mod.rs b/src/tape/drive/lto/mod.rs
index 7de07cf95..f3143c907 100644
--- a/src/tape/drive/lto/mod.rs
+++ b/src/tape/drive/lto/mod.rs
@@ -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> {
diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index 39602461f..b21a62d20 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -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`].
diff --git a/src/tape/drive/virtual_tape.rs b/src/tape/drive/virtual_tape.rs
index b13c58c4e..c183e2681 100644
--- a/src/tape/drive/virtual_tape.rs
+++ b/src/tape/drive/virtual_tape.rs
@@ -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 {
diff --git a/src/tape/inventory.rs b/src/tape/inventory.rs
index 7514d76c0..5e4318e21 100644
--- a/src/tape/inventory.rs
+++ b/src/tape/inventory.rs
@@ -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()?;
diff --git a/src/tape/media_pool.rs b/src/tape/media_pool.rs
index 8f2b0adda..1e8c739e7 100644
--- a/src/tape/media_pool.rs
+++ b/src/tape/media_pool.rs
@@ -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
+ }
}
diff --git a/src/tape/pool_writer/mod.rs b/src/tape/pool_writer/mod.rs
index 68e28714a..1df297b68 100644
--- a/src/tape/pool_writer/mod.rs
+++ b/src/tape/pool_writer/mod.rs
@@ -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()?;
}
diff --git a/www/tape/TapeInventory.js b/www/tape/TapeInventory.js
index 47d19acc0..305134e3e 100644
--- a/www/tape/TapeInventory.js
+++ b/www/tape/TapeInventory.js
@@ -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,
+ },
],
});
--
2.39.2
More information about the pbs-devel
mailing list