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
|
/// Media Pool
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub pool: Option<String>,
|
pub pool: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Bytes currently used
|
||||||
|
pub bytes_used: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
|
@ -204,6 +204,7 @@ pub async fn list_media(
|
|||||||
media_set_uuid,
|
media_set_uuid,
|
||||||
media_set_name,
|
media_set_name,
|
||||||
seq_nr,
|
seq_nr,
|
||||||
|
bytes_used: media.bytes_used(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,6 +233,7 @@ pub async fn list_media(
|
|||||||
media_set_ctime: None,
|
media_set_ctime: None,
|
||||||
seq_nr: None,
|
seq_nr: None,
|
||||||
pool: 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_uuid,
|
||||||
media_set_name,
|
media_set_name,
|
||||||
seq_nr,
|
seq_nr,
|
||||||
|
bytes_used: inventory.get_media_bytes_used(&media_id.label.uuid),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,6 +268,10 @@ impl TapeDriver for LtoTapeHandle {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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> {
|
fn run_sg_tape_cmd(subcmd: &str, args: &[&str], fd: RawFd) -> Result<String, Error> {
|
||||||
|
@ -242,6 +242,9 @@ pub trait TapeDriver {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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`].
|
/// A boxed implementor of [`MediaChange`].
|
||||||
|
@ -461,6 +461,10 @@ impl TapeDriver for VirtualTapeHandle {
|
|||||||
let status = VirtualDriveStatus { current_tape: None };
|
let status = VirtualDriveStatus { current_tape: None };
|
||||||
self.store_status(&status)
|
self.store_status(&status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_volume_statistics(&mut self) -> Result<pbs_api_types::Lp17VolumeStatistics, Error> {
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaChange for VirtualTapeHandle {
|
impl MediaChange for VirtualTapeHandle {
|
||||||
|
@ -84,6 +84,8 @@ struct MediaStateEntry {
|
|||||||
location: Option<MediaLocation>,
|
location: Option<MediaLocation>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
status: Option<MediaStatus>,
|
status: Option<MediaStatus>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
bytes_used: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Media Inventory
|
/// Media Inventory
|
||||||
@ -211,6 +213,7 @@ impl Inventory {
|
|||||||
} else {
|
} else {
|
||||||
previous.status
|
previous.status
|
||||||
},
|
},
|
||||||
|
bytes_used: previous.bytes_used,
|
||||||
};
|
};
|
||||||
self.map.insert(uuid, entry);
|
self.map.insert(uuid, entry);
|
||||||
} else {
|
} else {
|
||||||
@ -218,6 +221,7 @@ impl Inventory {
|
|||||||
id: media_id,
|
id: media_id,
|
||||||
location: None,
|
location: None,
|
||||||
status: None,
|
status: None,
|
||||||
|
bytes_used: None,
|
||||||
};
|
};
|
||||||
self.map.insert(uuid, entry);
|
self.map.insert(uuid, entry);
|
||||||
}
|
}
|
||||||
@ -720,6 +724,32 @@ impl Inventory {
|
|||||||
self.set_media_location(uuid, Some(MediaLocation::Offline))
|
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
|
/// Update online status
|
||||||
pub fn update_online_status(&mut self, online_map: &OnlineStatusMap) -> Result<(), Error> {
|
pub fn update_online_status(&mut self, online_map: &OnlineStatusMap) -> Result<(), Error> {
|
||||||
let _lock = self.lock()?;
|
let _lock = self.lock()?;
|
||||||
|
@ -212,8 +212,11 @@ impl MediaPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (status, location) = self.compute_media_state(&media_id);
|
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
|
/// List all media associated with this pool
|
||||||
@ -224,7 +227,8 @@ impl MediaPool {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|media_id| {
|
.map(|media_id| {
|
||||||
let (status, location) = self.compute_media_state(&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()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -238,6 +242,15 @@ impl MediaPool {
|
|||||||
Ok(())
|
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
|
/// Make sure the current media set is usable for writing
|
||||||
///
|
///
|
||||||
/// If not, starts a new media set. Also creates a new
|
/// If not, starts a new media set. Also creates a new
|
||||||
@ -715,15 +728,23 @@ pub struct BackupMedia {
|
|||||||
location: MediaLocation,
|
location: MediaLocation,
|
||||||
/// Media status
|
/// Media status
|
||||||
status: MediaStatus,
|
status: MediaStatus,
|
||||||
|
/// Bytes used
|
||||||
|
bytes_used: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackupMedia {
|
impl BackupMedia {
|
||||||
/// Creates a new instance
|
/// 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 {
|
Self {
|
||||||
id,
|
id,
|
||||||
location,
|
location,
|
||||||
status,
|
status,
|
||||||
|
bytes_used,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,4 +797,9 @@ impl BackupMedia {
|
|||||||
pub fn label_text(&self) -> &str {
|
pub fn label_text(&self) -> &str {
|
||||||
&self.id.label.label_text
|
&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 {
|
if let Some(ref mut status) = self.status {
|
||||||
status.drive.sync()?; // sync all data to the tape
|
status.drive.sync()?; // sync all data to the tape
|
||||||
status.bytes_written_after_sync = 0; // reset bytes written
|
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
|
self.catalog_set.lock().unwrap().commit()?; // then commit the catalog
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -237,7 +245,13 @@ impl PoolWriter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(PoolWriterState { mut drive, .. }) = self.status.take() {
|
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");
|
task_log!(worker, "eject current media");
|
||||||
drive.eject_media()?;
|
drive.eject_media()?;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ Ext.define('pbs-model-tapes', {
|
|||||||
'seq-nr',
|
'seq-nr',
|
||||||
'status',
|
'status',
|
||||||
'uuid',
|
'uuid',
|
||||||
|
'bytes-used',
|
||||||
],
|
],
|
||||||
idProperty: 'uuid',
|
idProperty: 'uuid',
|
||||||
proxy: {
|
proxy: {
|
||||||
@ -326,5 +327,11 @@ Ext.define('PBS.TapeManagement.TapeInventory', {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: gettext("Bytes Used"),
|
||||||
|
dataIndex: 'bytes-used',
|
||||||
|
flex: 1,
|
||||||
|
renderer: Proxmox.Utils.render_size,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user