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:
parent
aea66a8128
commit
4b21a00744
@ -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(
|
||||
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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`].
|
||||
|
@ -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 {
|
||||
|
@ -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()?;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()?;
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user