tape: merge MediaStateDatabase into Inventory
This commit is contained in:
parent
54f4ecd46a
commit
cfae8f0656
@ -34,7 +34,6 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
TAPE_STATUS_DIR,
|
TAPE_STATUS_DIR,
|
||||||
Inventory,
|
Inventory,
|
||||||
MediaStateDatabase,
|
|
||||||
PoolWriter,
|
PoolWriter,
|
||||||
MediaPool,
|
MediaPool,
|
||||||
SnapshotReader,
|
SnapshotReader,
|
||||||
@ -154,12 +153,10 @@ fn update_media_online_status(drive: &str) -> Result<bool, Error> {
|
|||||||
|
|
||||||
let status_path = Path::new(TAPE_STATUS_DIR);
|
let status_path = Path::new(TAPE_STATUS_DIR);
|
||||||
let mut inventory = Inventory::load(status_path)?;
|
let mut inventory = Inventory::load(status_path)?;
|
||||||
let mut state_db = MediaStateDatabase::load(status_path)?;
|
|
||||||
|
|
||||||
update_changer_online_status(
|
update_changer_online_status(
|
||||||
&config,
|
&config,
|
||||||
&mut inventory,
|
&mut inventory,
|
||||||
&mut state_db,
|
|
||||||
&changer_name,
|
&changer_name,
|
||||||
&changer_id_list,
|
&changer_id_list,
|
||||||
)?;
|
)?;
|
||||||
|
@ -20,7 +20,6 @@ use crate::{
|
|||||||
ElementStatus,
|
ElementStatus,
|
||||||
OnlineStatusMap,
|
OnlineStatusMap,
|
||||||
Inventory,
|
Inventory,
|
||||||
MediaStateDatabase,
|
|
||||||
linux_tape_changer_list,
|
linux_tape_changer_list,
|
||||||
mtx_status,
|
mtx_status,
|
||||||
mtx_status_to_online_set,
|
mtx_status_to_online_set,
|
||||||
@ -57,14 +56,13 @@ pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
|
|||||||
}).await??;
|
}).await??;
|
||||||
|
|
||||||
let state_path = Path::new(TAPE_STATUS_DIR);
|
let state_path = Path::new(TAPE_STATUS_DIR);
|
||||||
let inventory = Inventory::load(state_path)?;
|
let mut inventory = Inventory::load(state_path)?;
|
||||||
|
|
||||||
let mut map = OnlineStatusMap::new(&config)?;
|
let mut map = OnlineStatusMap::new(&config)?;
|
||||||
let online_set = mtx_status_to_online_set(&status, &inventory);
|
let online_set = mtx_status_to_online_set(&status, &inventory);
|
||||||
map.update_online_status(&name, online_set)?;
|
map.update_online_status(&name, online_set)?;
|
||||||
|
|
||||||
let mut state_db = MediaStateDatabase::load(state_path)?;
|
inventory.update_online_status(&map)?;
|
||||||
state_db.update_online_status(&map)?;
|
|
||||||
|
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ use crate::{
|
|||||||
MediaChange,
|
MediaChange,
|
||||||
MediaPool,
|
MediaPool,
|
||||||
Inventory,
|
Inventory,
|
||||||
MediaStateDatabase,
|
|
||||||
MediaCatalog,
|
MediaCatalog,
|
||||||
MediaId,
|
MediaId,
|
||||||
mtx_load,
|
mtx_load,
|
||||||
@ -421,7 +420,7 @@ fn write_media_label(
|
|||||||
MediaCatalog::overwrite(status_path, &media_id, false)?;
|
MediaCatalog::overwrite(status_path, &media_id, false)?;
|
||||||
|
|
||||||
let mut inventory = Inventory::load(status_path)?;
|
let mut inventory = Inventory::load(status_path)?;
|
||||||
inventory.store(media_id.clone())?;
|
inventory.store(media_id.clone(), false)?;
|
||||||
|
|
||||||
drive.rewind()?;
|
drive.rewind()?;
|
||||||
|
|
||||||
@ -542,12 +541,10 @@ pub async fn inventory(
|
|||||||
let state_path = Path::new(TAPE_STATUS_DIR);
|
let state_path = Path::new(TAPE_STATUS_DIR);
|
||||||
|
|
||||||
let mut inventory = Inventory::load(state_path)?;
|
let mut inventory = Inventory::load(state_path)?;
|
||||||
let mut state_db = MediaStateDatabase::load(state_path)?;
|
|
||||||
|
|
||||||
update_changer_online_status(
|
update_changer_online_status(
|
||||||
&config,
|
&config,
|
||||||
&mut inventory,
|
&mut inventory,
|
||||||
&mut state_db,
|
|
||||||
&changer_name,
|
&changer_name,
|
||||||
&changer_id_list,
|
&changer_id_list,
|
||||||
)?;
|
)?;
|
||||||
@ -630,9 +627,8 @@ pub fn update_inventory(
|
|||||||
let state_path = Path::new(TAPE_STATUS_DIR);
|
let state_path = Path::new(TAPE_STATUS_DIR);
|
||||||
|
|
||||||
let mut inventory = Inventory::load(state_path)?;
|
let mut inventory = Inventory::load(state_path)?;
|
||||||
let mut state_db = MediaStateDatabase::load(state_path)?;
|
|
||||||
|
|
||||||
update_changer_online_status(&config, &mut inventory, &mut state_db, &changer_name, &changer_id_list)?;
|
update_changer_online_status(&config, &mut inventory, &changer_name, &changer_id_list)?;
|
||||||
|
|
||||||
for changer_id in changer_id_list.iter() {
|
for changer_id in changer_id_list.iter() {
|
||||||
if changer_id.starts_with("CLN") {
|
if changer_id.starts_with("CLN") {
|
||||||
@ -668,7 +664,7 @@ pub fn update_inventory(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
worker.log(format!("inventorize media '{}' with uuid '{}'", changer_id, media_id.label.uuid));
|
worker.log(format!("inventorize media '{}' with uuid '{}'", changer_id, media_id.label.uuid));
|
||||||
inventory.store(media_id)?;
|
inventory.store(media_id, false)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -743,9 +739,8 @@ fn barcode_label_media_worker(
|
|||||||
let state_path = Path::new(TAPE_STATUS_DIR);
|
let state_path = Path::new(TAPE_STATUS_DIR);
|
||||||
|
|
||||||
let mut inventory = Inventory::load(state_path)?;
|
let mut inventory = Inventory::load(state_path)?;
|
||||||
let mut state_db = MediaStateDatabase::load(state_path)?;
|
|
||||||
|
|
||||||
update_changer_online_status(&config, &mut inventory, &mut state_db, &changer_name, &changer_id_list)?;
|
update_changer_online_status(&config, &mut inventory, &changer_name, &changer_id_list)?;
|
||||||
|
|
||||||
if changer_id_list.is_empty() {
|
if changer_id_list.is_empty() {
|
||||||
bail!("changer device does not list any media labels");
|
bail!("changer device does not list any media labels");
|
||||||
@ -921,7 +916,7 @@ pub fn catalog_media(
|
|||||||
let status_path = Path::new(TAPE_STATUS_DIR);
|
let status_path = Path::new(TAPE_STATUS_DIR);
|
||||||
|
|
||||||
let mut inventory = Inventory::load(status_path)?;
|
let mut inventory = Inventory::load(status_path)?;
|
||||||
inventory.store(media_id.clone())?;
|
inventory.store(media_id.clone(), false)?;
|
||||||
|
|
||||||
let pool = match media_id.media_set_label {
|
let pool = match media_id.media_set_label {
|
||||||
None => {
|
None => {
|
||||||
|
@ -28,7 +28,6 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
TAPE_STATUS_DIR,
|
TAPE_STATUS_DIR,
|
||||||
Inventory,
|
Inventory,
|
||||||
MediaStateDatabase,
|
|
||||||
MediaPool,
|
MediaPool,
|
||||||
MediaCatalog,
|
MediaCatalog,
|
||||||
update_online_status,
|
update_online_status,
|
||||||
@ -118,11 +117,10 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
|
|||||||
if pool.is_none() {
|
if pool.is_none() {
|
||||||
|
|
||||||
let inventory = Inventory::load(status_path)?;
|
let inventory = Inventory::load(status_path)?;
|
||||||
let state_db = MediaStateDatabase::load(status_path)?;
|
|
||||||
|
|
||||||
for media_id in inventory.list_unassigned_media() {
|
for media_id in inventory.list_unassigned_media() {
|
||||||
|
|
||||||
let (mut status, location) = state_db.status_and_location(&media_id.label.uuid);
|
let (mut status, location) = inventory.status_and_location(&media_id.label.uuid);
|
||||||
|
|
||||||
if status == MediaStatus::Unknown {
|
if status == MediaStatus::Unknown {
|
||||||
status = MediaStatus::Writable;
|
status = MediaStatus::Writable;
|
||||||
@ -184,9 +182,6 @@ pub fn destroy_media(changer_id: String, force: Option<bool>,) -> Result<(), Err
|
|||||||
|
|
||||||
inventory.remove_media(&uuid)?;
|
inventory.remove_media(&uuid)?;
|
||||||
|
|
||||||
let mut state_db = MediaStateDatabase::load(status_path)?;
|
|
||||||
state_db.remove_media(&uuid)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,12 @@ use crate::{
|
|||||||
api2::types::{
|
api2::types::{
|
||||||
MediaSetPolicy,
|
MediaSetPolicy,
|
||||||
RetentionPolicy,
|
RetentionPolicy,
|
||||||
|
MediaStatus,
|
||||||
|
MediaLocation,
|
||||||
},
|
},
|
||||||
tape::{
|
tape::{
|
||||||
TAPE_STATUS_DIR,
|
TAPE_STATUS_DIR,
|
||||||
|
OnlineStatusMap,
|
||||||
file_formats::{
|
file_formats::{
|
||||||
MediaLabel,
|
MediaLabel,
|
||||||
MediaSetLabel,
|
MediaSetLabel,
|
||||||
@ -120,9 +123,18 @@ impl MediaSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize,Deserialize)]
|
||||||
|
struct MediaStateEntry {
|
||||||
|
id: MediaId,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
location: Option<MediaLocation>,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
status: Option<MediaStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Media Inventory
|
/// Media Inventory
|
||||||
pub struct Inventory {
|
pub struct Inventory {
|
||||||
map: BTreeMap<Uuid, MediaId>,
|
map: BTreeMap<Uuid, MediaStateEntry>,
|
||||||
|
|
||||||
inventory_path: PathBuf,
|
inventory_path: PathBuf,
|
||||||
lockfile_path: PathBuf,
|
lockfile_path: PathBuf,
|
||||||
@ -171,8 +183,8 @@ impl Inventory {
|
|||||||
|
|
||||||
let mut set_start_times = HashMap::new();
|
let mut set_start_times = HashMap::new();
|
||||||
|
|
||||||
for media in self.map.values() {
|
for entry in self.map.values() {
|
||||||
let set = match &media.media_set_label {
|
let set = match &entry.id.media_set_label {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(set) => set,
|
Some(set) => set,
|
||||||
};
|
};
|
||||||
@ -189,21 +201,21 @@ impl Inventory {
|
|||||||
open_file_locked(&self.lockfile_path, std::time::Duration::new(10, 0), true)
|
open_file_locked(&self.lockfile_path, std::time::Duration::new(10, 0), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_media_db(path: &Path) -> Result<BTreeMap<Uuid, MediaId>, Error> {
|
fn load_media_db(path: &Path) -> Result<BTreeMap<Uuid, MediaStateEntry>, Error> {
|
||||||
|
|
||||||
let data = file_get_json(path, Some(json!([])))?;
|
let data = file_get_json(path, Some(json!([])))?;
|
||||||
let media_list: Vec<MediaId> = serde_json::from_value(data)?;
|
let media_list: Vec<MediaStateEntry> = serde_json::from_value(data)?;
|
||||||
|
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
for item in media_list.into_iter() {
|
for entry in media_list.into_iter() {
|
||||||
map.insert(item.label.uuid.clone(), item);
|
map.insert(entry.id.label.uuid.clone(), entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(map)
|
Ok(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_file(&self) -> Result<(), Error> {
|
fn replace_file(&self) -> Result<(), Error> {
|
||||||
let list: Vec<&MediaId> = self.map.values().collect();
|
let list: Vec<&MediaStateEntry> = self.map.values().collect();
|
||||||
let raw = serde_json::to_string_pretty(&serde_json::to_value(list)?)?;
|
let raw = serde_json::to_string_pretty(&serde_json::to_value(list)?)?;
|
||||||
|
|
||||||
let backup_user = crate::backup::backup_user()?;
|
let backup_user = crate::backup::backup_user()?;
|
||||||
@ -219,22 +231,40 @@ impl Inventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stores a single MediaID persistently
|
/// Stores a single MediaID persistently
|
||||||
pub fn store(&mut self, mut media_id: MediaId) -> Result<(), Error> {
|
pub fn store(
|
||||||
|
&mut self,
|
||||||
|
mut media_id: MediaId,
|
||||||
|
clear_media_status: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let _lock = self.lock()?;
|
let _lock = self.lock()?;
|
||||||
self.map = Self::load_media_db(&self.inventory_path)?;
|
self.map = Self::load_media_db(&self.inventory_path)?;
|
||||||
|
|
||||||
|
let uuid = media_id.label.uuid.clone();
|
||||||
|
|
||||||
|
if let Some(previous) = self.map.remove(&media_id.label.uuid) {
|
||||||
// do not overwrite unsaved pool assignments
|
// do not overwrite unsaved pool assignments
|
||||||
if media_id.media_set_label.is_none() {
|
if media_id.media_set_label.is_none() {
|
||||||
if let Some(previous) = self.map.get(&media_id.label.uuid) {
|
if let Some(ref set) = previous.id.media_set_label {
|
||||||
if let Some(ref set) = previous.media_set_label {
|
|
||||||
if set.uuid.as_ref() == [0u8;16] {
|
if set.uuid.as_ref() == [0u8;16] {
|
||||||
media_id.media_set_label = Some(set.clone());
|
media_id.media_set_label = Some(set.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let entry = MediaStateEntry {
|
||||||
|
id: media_id,
|
||||||
|
location: previous.location,
|
||||||
|
status: if clear_media_status {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
previous.status
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.map.insert(uuid, entry);
|
||||||
|
} else {
|
||||||
|
let entry = MediaStateEntry { id: media_id, location: None, status: None };
|
||||||
|
self.map.insert(uuid, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.map.insert(media_id.label.uuid.clone(), media_id);
|
|
||||||
self.update_helpers();
|
self.update_helpers();
|
||||||
self.replace_file()?;
|
self.replace_file()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -252,14 +282,14 @@ impl Inventory {
|
|||||||
|
|
||||||
/// Lookup media
|
/// Lookup media
|
||||||
pub fn lookup_media(&self, uuid: &Uuid) -> Option<&MediaId> {
|
pub fn lookup_media(&self, uuid: &Uuid) -> Option<&MediaId> {
|
||||||
self.map.get(uuid)
|
self.map.get(uuid).map(|entry| &entry.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// find media by changer_id
|
/// find media by changer_id
|
||||||
pub fn find_media_by_changer_id(&self, changer_id: &str) -> Option<&MediaId> {
|
pub fn find_media_by_changer_id(&self, changer_id: &str) -> Option<&MediaId> {
|
||||||
for (_uuid, media_id) in &self.map {
|
for (_uuid, entry) in &self.map {
|
||||||
if media_id.label.changer_id == changer_id {
|
if entry.id.label.changer_id == changer_id {
|
||||||
return Some(media_id);
|
return Some(&entry.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -271,8 +301,8 @@ impl Inventory {
|
|||||||
pub fn lookup_media_pool(&self, uuid: &Uuid) -> Option<(&str, bool)> {
|
pub fn lookup_media_pool(&self, uuid: &Uuid) -> Option<(&str, bool)> {
|
||||||
match self.map.get(uuid) {
|
match self.map.get(uuid) {
|
||||||
None => None,
|
None => None,
|
||||||
Some(media_id) => {
|
Some(entry) => {
|
||||||
match media_id.media_set_label {
|
match entry.id.media_set_label {
|
||||||
None => None, // not assigned to any pool
|
None => None, // not assigned to any pool
|
||||||
Some(ref set) => {
|
Some(ref set) => {
|
||||||
let is_empty = set.uuid.as_ref() == [0u8;16];
|
let is_empty = set.uuid.as_ref() == [0u8;16];
|
||||||
@ -287,8 +317,8 @@ impl Inventory {
|
|||||||
pub fn list_pool_media(&self, pool: &str) -> Vec<MediaId> {
|
pub fn list_pool_media(&self, pool: &str) -> Vec<MediaId> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
|
||||||
for (_uuid, media_id) in &self.map {
|
for (_uuid, entry) in &self.map {
|
||||||
match media_id.media_set_label {
|
match entry.id.media_set_label {
|
||||||
None => continue, // not assigned to any pool
|
None => continue, // not assigned to any pool
|
||||||
Some(ref set) => {
|
Some(ref set) => {
|
||||||
if set.pool != pool {
|
if set.pool != pool {
|
||||||
@ -297,15 +327,14 @@ impl Inventory {
|
|||||||
|
|
||||||
if set.uuid.as_ref() == [0u8;16] { // should we do this??
|
if set.uuid.as_ref() == [0u8;16] { // should we do this??
|
||||||
list.push(MediaId {
|
list.push(MediaId {
|
||||||
label: media_id.label.clone(),
|
label: entry.id.label.clone(),
|
||||||
media_set_label: None,
|
media_set_label: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
list.push(media_id.clone());
|
list.push(entry.id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list
|
list
|
||||||
@ -315,12 +344,12 @@ impl Inventory {
|
|||||||
pub fn list_used_media(&self) -> Vec<MediaId> {
|
pub fn list_used_media(&self) -> Vec<MediaId> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
|
||||||
for (_uuid, media_id) in &self.map {
|
for (_uuid, entry) in &self.map {
|
||||||
match media_id.media_set_label {
|
match entry.id.media_set_label {
|
||||||
None => continue, // not assigned to any pool
|
None => continue, // not assigned to any pool
|
||||||
Some(ref set) => {
|
Some(ref set) => {
|
||||||
if set.uuid.as_ref() != [0u8;16] {
|
if set.uuid.as_ref() != [0u8;16] {
|
||||||
list.push(media_id.clone());
|
list.push(entry.id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,9 +362,9 @@ impl Inventory {
|
|||||||
pub fn list_unassigned_media(&self) -> Vec<MediaId> {
|
pub fn list_unassigned_media(&self) -> Vec<MediaId> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
|
||||||
for (_uuid, media_id) in &self.map {
|
for (_uuid, entry) in &self.map {
|
||||||
if media_id.media_set_label.is_none() {
|
if entry.id.media_set_label.is_none() {
|
||||||
list.push(media_id.clone());
|
list.push(entry.id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,14 +380,14 @@ impl Inventory {
|
|||||||
|
|
||||||
let mut last_pool = None;
|
let mut last_pool = None;
|
||||||
|
|
||||||
for media in self.map.values() {
|
for entry in self.map.values() {
|
||||||
match media.media_set_label {
|
match entry.id.media_set_label {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(MediaSetLabel { ref uuid, .. }) => {
|
Some(MediaSetLabel { ref uuid, .. }) => {
|
||||||
if uuid != media_set_uuid {
|
if uuid != media_set_uuid {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some((pool, _)) = self.lookup_media_pool(&media.label.uuid) {
|
if let Some((pool, _)) = self.lookup_media_pool(&entry.id.label.uuid) {
|
||||||
if let Some(last_pool) = last_pool {
|
if let Some(last_pool) = last_pool {
|
||||||
if last_pool != pool {
|
if last_pool != pool {
|
||||||
bail!("detected media set with inconsistent pool assignment - internal error");
|
bail!("detected media set with inconsistent pool assignment - internal error");
|
||||||
@ -382,14 +411,14 @@ impl Inventory {
|
|||||||
|
|
||||||
let mut set = MediaSet::with_data(media_set_uuid.clone(), Vec::new());
|
let mut set = MediaSet::with_data(media_set_uuid.clone(), Vec::new());
|
||||||
|
|
||||||
for media in self.map.values() {
|
for entry in self.map.values() {
|
||||||
match media.media_set_label {
|
match entry.id.media_set_label {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(MediaSetLabel { seq_nr, ref uuid, .. }) => {
|
Some(MediaSetLabel { seq_nr, ref uuid, .. }) => {
|
||||||
if uuid != media_set_uuid {
|
if uuid != media_set_uuid {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
set.insert_media(media.label.uuid.clone(), seq_nr)?;
|
set.insert_media(entry.id.label.uuid.clone(), seq_nr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,8 +431,8 @@ impl Inventory {
|
|||||||
|
|
||||||
let mut set_map: HashMap<Uuid, MediaSet> = HashMap::new();
|
let mut set_map: HashMap<Uuid, MediaSet> = HashMap::new();
|
||||||
|
|
||||||
for media in self.map.values() {
|
for entry in self.map.values() {
|
||||||
match media.media_set_label {
|
match entry.id.media_set_label {
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(MediaSetLabel { seq_nr, ref uuid, .. }) => {
|
Some(MediaSetLabel { seq_nr, ref uuid, .. }) => {
|
||||||
|
|
||||||
@ -411,7 +440,7 @@ impl Inventory {
|
|||||||
MediaSet::with_data(uuid.clone(), Vec::new())
|
MediaSet::with_data(uuid.clone(), Vec::new())
|
||||||
});
|
});
|
||||||
|
|
||||||
set.insert_media(media.label.uuid.clone(), seq_nr)?;
|
set.insert_media(entry.id.label.uuid.clone(), seq_nr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,7 +454,7 @@ impl Inventory {
|
|||||||
let mut last_set: Option<(Uuid, i64)> = None;
|
let mut last_set: Option<(Uuid, i64)> = None;
|
||||||
|
|
||||||
let set_list = self.map.values()
|
let set_list = self.map.values()
|
||||||
.filter_map(|media| media.media_set_label.as_ref())
|
.filter_map(|entry| entry.id.media_set_label.as_ref())
|
||||||
.filter(|set| &set.pool == &pool && set.uuid.as_ref() != [0u8;16]);
|
.filter(|set| &set.pool == &pool && set.uuid.as_ref() != [0u8;16]);
|
||||||
|
|
||||||
for set in set_list {
|
for set in set_list {
|
||||||
@ -448,7 +477,7 @@ impl Inventory {
|
|||||||
|
|
||||||
// consistency check - must be the only set with that ctime
|
// consistency check - must be the only set with that ctime
|
||||||
let set_list = self.map.values()
|
let set_list = self.map.values()
|
||||||
.filter_map(|media| media.media_set_label.as_ref())
|
.filter_map(|entry| entry.id.media_set_label.as_ref())
|
||||||
.filter(|set| &set.pool == &pool && set.uuid.as_ref() != [0u8;16]);
|
.filter(|set| &set.pool == &pool && set.uuid.as_ref() != [0u8;16]);
|
||||||
|
|
||||||
for set in set_list {
|
for set in set_list {
|
||||||
@ -466,7 +495,7 @@ impl Inventory {
|
|||||||
fn media_set_next_start_time(&self, media_set_uuid: &Uuid) -> Option<i64> {
|
fn media_set_next_start_time(&self, media_set_uuid: &Uuid) -> Option<i64> {
|
||||||
|
|
||||||
let (pool, ctime) = match self.map.values()
|
let (pool, ctime) = match self.map.values()
|
||||||
.filter_map(|media| media.media_set_label.as_ref())
|
.filter_map(|entry| entry.id.media_set_label.as_ref())
|
||||||
.find_map(|set| {
|
.find_map(|set| {
|
||||||
if &set.uuid == media_set_uuid {
|
if &set.uuid == media_set_uuid {
|
||||||
Some((set.pool.clone(), set.ctime))
|
Some((set.pool.clone(), set.ctime))
|
||||||
@ -479,7 +508,7 @@ impl Inventory {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let set_list = self.map.values()
|
let set_list = self.map.values()
|
||||||
.filter_map(|media| media.media_set_label.as_ref())
|
.filter_map(|entry| entry.id.media_set_label.as_ref())
|
||||||
.filter(|set| (&set.uuid != media_set_uuid) && (&set.pool == &pool));
|
.filter(|set| (&set.uuid != media_set_uuid) && (&set.pool == &pool));
|
||||||
|
|
||||||
let mut next_ctime = None;
|
let mut next_ctime = None;
|
||||||
@ -586,7 +615,7 @@ impl Inventory {
|
|||||||
};
|
};
|
||||||
let uuid = label.uuid.clone();
|
let uuid = label.uuid.clone();
|
||||||
|
|
||||||
self.store(MediaId { label, media_set_label: None }).unwrap();
|
self.store(MediaId { label, media_set_label: None }, false).unwrap();
|
||||||
|
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
@ -610,7 +639,7 @@ impl Inventory {
|
|||||||
|
|
||||||
let set = MediaSetLabel::with_data(pool, [0u8; 16].into(), 0, ctime);
|
let set = MediaSetLabel::with_data(pool, [0u8; 16].into(), 0, ctime);
|
||||||
|
|
||||||
self.store(MediaId { label, media_set_label: Some(set) }).unwrap();
|
self.store(MediaId { label, media_set_label: Some(set) }, false).unwrap();
|
||||||
|
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
@ -629,12 +658,122 @@ impl Inventory {
|
|||||||
};
|
};
|
||||||
let uuid = label.uuid.clone();
|
let uuid = label.uuid.clone();
|
||||||
|
|
||||||
self.store(MediaId { label, media_set_label: Some(set) }).unwrap();
|
self.store(MediaId { label, media_set_label: Some(set) }, false).unwrap();
|
||||||
|
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status/location handling
|
||||||
|
impl Inventory {
|
||||||
|
|
||||||
|
/// Returns status and location with reasonable defaults.
|
||||||
|
///
|
||||||
|
/// Default status is 'MediaStatus::Unknown'.
|
||||||
|
/// Default location is 'MediaLocation::Offline'.
|
||||||
|
pub fn status_and_location(&self, uuid: &Uuid) -> (MediaStatus, MediaLocation) {
|
||||||
|
|
||||||
|
match self.map.get(uuid) {
|
||||||
|
None => {
|
||||||
|
// no info stored - assume media is writable/offline
|
||||||
|
(MediaStatus::Unknown, MediaLocation::Offline)
|
||||||
|
}
|
||||||
|
Some(entry) => {
|
||||||
|
let location = entry.location.clone().unwrap_or(MediaLocation::Offline);
|
||||||
|
let status = entry.status.unwrap_or(MediaStatus::Unknown);
|
||||||
|
(status, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock database, reload database, set status, store database
|
||||||
|
fn set_media_status(&mut self, uuid: &Uuid, status: Option<MediaStatus>) -> Result<(), Error> {
|
||||||
|
let _lock = self.lock()?;
|
||||||
|
self.map = Self::load_media_db(&self.inventory_path)?;
|
||||||
|
if let Some(entry) = self.map.get_mut(uuid) {
|
||||||
|
entry.status = status;
|
||||||
|
self.update_helpers();
|
||||||
|
self.replace_file()?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!("no such media '{}'", uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock database, reload database, set status to Full, store database
|
||||||
|
pub fn set_media_status_full(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
||||||
|
self.set_media_status(uuid, Some(MediaStatus::Full))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock database, reload database, set status to Damaged, store database
|
||||||
|
pub fn set_media_status_damaged(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
||||||
|
self.set_media_status(uuid, Some(MediaStatus::Damaged))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock database, reload database, set status to None, store database
|
||||||
|
pub fn clear_media_status(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
||||||
|
self.set_media_status(uuid, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock database, reload database, set location, store database
|
||||||
|
fn set_media_location(&mut self, uuid: &Uuid, location: Option<MediaLocation>) -> Result<(), Error> {
|
||||||
|
let _lock = self.lock()?;
|
||||||
|
self.map = Self::load_media_db(&self.inventory_path)?;
|
||||||
|
if let Some(entry) = self.map.get_mut(uuid) {
|
||||||
|
entry.location = location;
|
||||||
|
self.update_helpers();
|
||||||
|
self.replace_file()?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!("no such media '{}'", uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock database, reload database, set location to vault, store database
|
||||||
|
pub fn set_media_location_vault(&mut self, uuid: &Uuid, vault: &str) -> Result<(), Error> {
|
||||||
|
self.set_media_location(uuid, Some(MediaLocation::Vault(vault.to_string())))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock database, reload database, set location to offline, store database
|
||||||
|
pub fn set_media_location_offline(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
||||||
|
self.set_media_location(uuid, Some(MediaLocation::Offline))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update online status
|
||||||
|
pub fn update_online_status(&mut self, online_map: &OnlineStatusMap) -> Result<(), Error> {
|
||||||
|
let _lock = self.lock()?;
|
||||||
|
self.map = Self::load_media_db(&self.inventory_path)?;
|
||||||
|
|
||||||
|
for (uuid, entry) in self.map.iter_mut() {
|
||||||
|
if let Some(changer_name) = online_map.lookup_changer(uuid) {
|
||||||
|
entry.location = Some(MediaLocation::Online(changer_name.to_string()));
|
||||||
|
} else {
|
||||||
|
if let Some(MediaLocation::Online(ref changer_name)) = entry.location {
|
||||||
|
match online_map.online_map(changer_name) {
|
||||||
|
None => {
|
||||||
|
// no such changer device
|
||||||
|
entry.location = Some(MediaLocation::Offline);
|
||||||
|
}
|
||||||
|
Some(None) => {
|
||||||
|
// got no info - do nothing
|
||||||
|
}
|
||||||
|
Some(Some(_)) => {
|
||||||
|
// media changer changed
|
||||||
|
entry.location = Some(MediaLocation::Offline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_helpers();
|
||||||
|
self.replace_file()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// shell completion helper
|
// shell completion helper
|
||||||
|
|
||||||
/// List of known media uuids
|
/// List of known media uuids
|
||||||
@ -663,7 +802,7 @@ pub fn complete_media_set_uuid(
|
|||||||
};
|
};
|
||||||
|
|
||||||
inventory.map.values()
|
inventory.map.values()
|
||||||
.filter_map(|media| media.media_set_label.as_ref())
|
.filter_map(|entry| entry.id.media_set_label.as_ref())
|
||||||
.map(|set| set.uuid.to_string()).collect()
|
.map(|set| set.uuid.to_string()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,5 +817,5 @@ pub fn complete_media_changer_id(
|
|||||||
Err(_) => return Vec::new(),
|
Err(_) => return Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
inventory.map.values().map(|media| media.label.changer_id.clone()).collect()
|
inventory.map.values().map(|entry| entry.id.label.changer_id.clone()).collect()
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ use crate::{
|
|||||||
MediaId,
|
MediaId,
|
||||||
MediaSet,
|
MediaSet,
|
||||||
Inventory,
|
Inventory,
|
||||||
MediaStateDatabase,
|
|
||||||
file_formats::{
|
file_formats::{
|
||||||
MediaLabel,
|
MediaLabel,
|
||||||
MediaSetLabel,
|
MediaSetLabel,
|
||||||
@ -47,7 +46,6 @@ pub struct MediaPool {
|
|||||||
use_offline_media: bool,
|
use_offline_media: bool,
|
||||||
|
|
||||||
inventory: Inventory,
|
inventory: Inventory,
|
||||||
state_db: MediaStateDatabase,
|
|
||||||
|
|
||||||
current_media_set: MediaSet,
|
current_media_set: MediaSet,
|
||||||
}
|
}
|
||||||
@ -70,15 +68,12 @@ impl MediaPool {
|
|||||||
None => MediaSet::new(),
|
None => MediaSet::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_db = MediaStateDatabase::load(state_path)?;
|
|
||||||
|
|
||||||
Ok(MediaPool {
|
Ok(MediaPool {
|
||||||
name: String::from(name),
|
name: String::from(name),
|
||||||
media_set_policy,
|
media_set_policy,
|
||||||
retention,
|
retention,
|
||||||
use_offline_media,
|
use_offline_media,
|
||||||
inventory,
|
inventory,
|
||||||
state_db,
|
|
||||||
current_media_set,
|
current_media_set,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -104,7 +99,7 @@ impl MediaPool {
|
|||||||
|
|
||||||
fn compute_media_state(&self, media_id: &MediaId) -> (MediaStatus, MediaLocation) {
|
fn compute_media_state(&self, media_id: &MediaId) -> (MediaStatus, MediaLocation) {
|
||||||
|
|
||||||
let (status, location) = self.state_db.status_and_location(&media_id.label.uuid);
|
let (status, location) = self.inventory.status_and_location(&media_id.label.uuid);
|
||||||
|
|
||||||
match status {
|
match status {
|
||||||
MediaStatus::Full | MediaStatus::Damaged | MediaStatus::Retired => {
|
MediaStatus::Full | MediaStatus::Damaged | MediaStatus::Retired => {
|
||||||
@ -181,7 +176,7 @@ impl MediaPool {
|
|||||||
pub fn set_media_status_full(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
pub fn set_media_status_full(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
||||||
let media = self.lookup_media(uuid)?; // check if media belongs to this pool
|
let media = self.lookup_media(uuid)?; // check if media belongs to this pool
|
||||||
if media.status() != &MediaStatus::Full {
|
if media.status() != &MediaStatus::Full {
|
||||||
self.state_db.set_media_status_full(uuid)?;
|
self.inventory.set_media_status_full(uuid)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -304,7 +299,7 @@ impl MediaPool {
|
|||||||
|
|
||||||
media.set_media_set_label(set);
|
media.set_media_set_label(set);
|
||||||
|
|
||||||
self.inventory.store(media.id().clone())?; // store persistently
|
self.inventory.store(media.id().clone(), true)?; // store persistently
|
||||||
|
|
||||||
self.current_media_set.add_media(media.uuid().clone());
|
self.current_media_set.add_media(media.uuid().clone());
|
||||||
|
|
||||||
@ -347,8 +342,8 @@ impl MediaPool {
|
|||||||
|
|
||||||
media.set_media_set_label(set);
|
media.set_media_set_label(set);
|
||||||
|
|
||||||
self.inventory.store(media.id().clone())?; // store persistently
|
let clear_media_status = true; // remove Full status
|
||||||
self.state_db.clear_media_status(media.uuid())?; // remove Full status
|
self.inventory.store(media.id().clone(), clear_media_status)?; // store persistently
|
||||||
|
|
||||||
self.current_media_set.add_media(media.uuid().clone());
|
self.current_media_set.add_media(media.uuid().clone());
|
||||||
|
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use anyhow::Error;
|
|
||||||
use ::serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use proxmox::tools::{
|
|
||||||
Uuid,
|
|
||||||
fs::{
|
|
||||||
open_file_locked,
|
|
||||||
replace_file,
|
|
||||||
file_get_json,
|
|
||||||
CreateOptions,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
tape::{
|
|
||||||
OnlineStatusMap,
|
|
||||||
},
|
|
||||||
api2::types::{
|
|
||||||
MediaStatus,
|
|
||||||
MediaLocation,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Serialize,Deserialize)]
|
|
||||||
struct MediaStateEntry {
|
|
||||||
u: Uuid,
|
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
|
||||||
l: Option<MediaLocation>,
|
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
|
||||||
s: Option<MediaStatus>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediaStateEntry {
|
|
||||||
fn new(uuid: Uuid) -> Self {
|
|
||||||
MediaStateEntry { u: uuid, l: None, s: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores MediaLocation and MediaState persistently
|
|
||||||
pub struct MediaStateDatabase {
|
|
||||||
|
|
||||||
map: BTreeMap<Uuid, MediaStateEntry>,
|
|
||||||
|
|
||||||
database_path: PathBuf,
|
|
||||||
lockfile_path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MediaStateDatabase {
|
|
||||||
|
|
||||||
pub const MEDIA_STATUS_DATABASE_FILENAME: &'static str = "media-status-db.json";
|
|
||||||
pub const MEDIA_STATUS_DATABASE_LOCKFILE: &'static str = ".media-status-db.lck";
|
|
||||||
|
|
||||||
|
|
||||||
/// Lock the database
|
|
||||||
pub fn lock(&self) -> Result<std::fs::File, Error> {
|
|
||||||
open_file_locked(&self.lockfile_path, std::time::Duration::new(10, 0), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns status and location with reasonable defaults.
|
|
||||||
///
|
|
||||||
/// Default status is 'MediaStatus::Unknown'.
|
|
||||||
/// Default location is 'MediaLocation::Offline'.
|
|
||||||
pub fn status_and_location(&self, uuid: &Uuid) -> (MediaStatus, MediaLocation) {
|
|
||||||
|
|
||||||
match self.map.get(uuid) {
|
|
||||||
None => {
|
|
||||||
// no info stored - assume media is writable/offline
|
|
||||||
(MediaStatus::Unknown, MediaLocation::Offline)
|
|
||||||
}
|
|
||||||
Some(entry) => {
|
|
||||||
let location = entry.l.clone().unwrap_or(MediaLocation::Offline);
|
|
||||||
let status = entry.s.unwrap_or(MediaStatus::Unknown);
|
|
||||||
(status, location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_media_db(path: &Path) -> Result<BTreeMap<Uuid, MediaStateEntry>, Error> {
|
|
||||||
|
|
||||||
let data = file_get_json(path, Some(json!([])))?;
|
|
||||||
let list: Vec<MediaStateEntry> = serde_json::from_value(data)?;
|
|
||||||
|
|
||||||
let mut map = BTreeMap::new();
|
|
||||||
for entry in list.into_iter() {
|
|
||||||
map.insert(entry.u.clone(), entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the database into memory
|
|
||||||
pub fn load(base_path: &Path) -> Result<MediaStateDatabase, Error> {
|
|
||||||
|
|
||||||
let mut database_path = base_path.to_owned();
|
|
||||||
database_path.push(Self::MEDIA_STATUS_DATABASE_FILENAME);
|
|
||||||
|
|
||||||
let mut lockfile_path = base_path.to_owned();
|
|
||||||
lockfile_path.push(Self::MEDIA_STATUS_DATABASE_LOCKFILE);
|
|
||||||
|
|
||||||
Ok(MediaStateDatabase {
|
|
||||||
map: Self::load_media_db(&database_path)?,
|
|
||||||
database_path,
|
|
||||||
lockfile_path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock database, reload database, set status to Full, store database
|
|
||||||
pub fn set_media_status_full(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
let entry = self.map.entry(uuid.clone()).or_insert(MediaStateEntry::new(uuid.clone()));
|
|
||||||
entry.s = Some(MediaStatus::Full);
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update online status
|
|
||||||
pub fn update_online_status(&mut self, online_map: &OnlineStatusMap) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
|
|
||||||
for (_uuid, entry) in self.map.iter_mut() {
|
|
||||||
if let Some(changer_name) = online_map.lookup_changer(&entry.u) {
|
|
||||||
entry.l = Some(MediaLocation::Online(changer_name.to_string()));
|
|
||||||
} else {
|
|
||||||
if let Some(MediaLocation::Online(ref changer_name)) = entry.l {
|
|
||||||
match online_map.online_map(changer_name) {
|
|
||||||
None => {
|
|
||||||
// no such changer device
|
|
||||||
entry.l = Some(MediaLocation::Offline);
|
|
||||||
}
|
|
||||||
Some(None) => {
|
|
||||||
// got no info - do nothing
|
|
||||||
}
|
|
||||||
Some(Some(_)) => {
|
|
||||||
// media changer changed
|
|
||||||
entry.l = Some(MediaLocation::Offline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uuid, changer_name) in online_map.changer_map() {
|
|
||||||
if self.map.contains_key(uuid) { continue; }
|
|
||||||
let mut entry = MediaStateEntry::new(uuid.clone());
|
|
||||||
entry.l = Some(MediaLocation::Online(changer_name.to_string()));
|
|
||||||
self.map.insert(uuid.clone(), entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock database, reload database, set status to Damaged, store database
|
|
||||||
pub fn set_media_status_damaged(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
let entry = self.map.entry(uuid.clone()).or_insert(MediaStateEntry::new(uuid.clone()));
|
|
||||||
entry.s = Some(MediaStatus::Damaged);
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock database, reload database, set status to None, store database
|
|
||||||
pub fn clear_media_status(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
let entry = self.map.entry(uuid.clone()).or_insert(MediaStateEntry::new(uuid.clone()));
|
|
||||||
entry.s = None ;
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock database, reload database, set location to vault, store database
|
|
||||||
pub fn set_media_location_vault(&mut self, uuid: &Uuid, vault: &str) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
let entry = self.map.entry(uuid.clone()).or_insert(MediaStateEntry::new(uuid.clone()));
|
|
||||||
entry.l = Some(MediaLocation::Vault(vault.to_string()));
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock database, reload database, set location to offline, store database
|
|
||||||
pub fn set_media_location_offline(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
let entry = self.map.entry(uuid.clone()).or_insert(MediaStateEntry::new(uuid.clone()));
|
|
||||||
entry.l = Some(MediaLocation::Offline);
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lock database, reload database, remove media, store database
|
|
||||||
pub fn remove_media(&mut self, uuid: &Uuid) -> Result<(), Error> {
|
|
||||||
let _lock = self.lock()?;
|
|
||||||
self.map = Self::load_media_db(&self.database_path)?;
|
|
||||||
self.map.remove(uuid);
|
|
||||||
self.store()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store(&self) -> Result<(), Error> {
|
|
||||||
|
|
||||||
let mut list = Vec::new();
|
|
||||||
for entry in self.map.values() {
|
|
||||||
list.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
let raw = serde_json::to_string_pretty(&serde_json::to_value(list)?)?;
|
|
||||||
|
|
||||||
let backup_user = crate::backup::backup_user()?;
|
|
||||||
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
|
|
||||||
let options = CreateOptions::new()
|
|
||||||
.perm(mode)
|
|
||||||
.owner(backup_user.uid)
|
|
||||||
.group(backup_user.gid);
|
|
||||||
|
|
||||||
replace_file(&self.database_path, raw.as_bytes(), options)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,9 +29,6 @@ pub use changer::*;
|
|||||||
mod drive;
|
mod drive;
|
||||||
pub use drive::*;
|
pub use drive::*;
|
||||||
|
|
||||||
mod media_state_database;
|
|
||||||
pub use media_state_database::*;
|
|
||||||
|
|
||||||
mod online_status_map;
|
mod online_status_map;
|
||||||
pub use online_status_map::*;
|
pub use online_status_map::*;
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ use crate::{
|
|||||||
tape::{
|
tape::{
|
||||||
MediaChange,
|
MediaChange,
|
||||||
Inventory,
|
Inventory,
|
||||||
MediaStateDatabase,
|
|
||||||
mtx_status,
|
mtx_status,
|
||||||
mtx_status_to_online_set,
|
mtx_status_to_online_set,
|
||||||
},
|
},
|
||||||
@ -97,7 +96,7 @@ pub fn update_online_status(state_path: &Path) -> Result<OnlineStatusMap, Error>
|
|||||||
|
|
||||||
let (config, _digest) = crate::config::drive::config()?;
|
let (config, _digest) = crate::config::drive::config()?;
|
||||||
|
|
||||||
let inventory = Inventory::load(state_path)?;
|
let mut inventory = Inventory::load(state_path)?;
|
||||||
|
|
||||||
let changers: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
|
let changers: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
|
||||||
|
|
||||||
@ -135,8 +134,7 @@ pub fn update_online_status(state_path: &Path) -> Result<OnlineStatusMap, Error>
|
|||||||
map.update_online_status(&vtape.name, online_set)?;
|
map.update_online_status(&vtape.name, online_set)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state_db = MediaStateDatabase::load(state_path)?;
|
inventory.update_online_status(&map)?;
|
||||||
state_db.update_online_status(&map)?;
|
|
||||||
|
|
||||||
Ok(map)
|
Ok(map)
|
||||||
}
|
}
|
||||||
@ -145,7 +143,6 @@ pub fn update_online_status(state_path: &Path) -> Result<OnlineStatusMap, Error>
|
|||||||
pub fn update_changer_online_status(
|
pub fn update_changer_online_status(
|
||||||
drive_config: &SectionConfigData,
|
drive_config: &SectionConfigData,
|
||||||
inventory: &mut Inventory,
|
inventory: &mut Inventory,
|
||||||
state_db: &mut MediaStateDatabase,
|
|
||||||
changer_name: &str,
|
changer_name: &str,
|
||||||
changer_id_list: &Vec<String>,
|
changer_id_list: &Vec<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@ -158,7 +155,7 @@ pub fn update_changer_online_status(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
online_map.update_online_status(&changer_name, online_set)?;
|
online_map.update_online_status(&changer_name, online_set)?;
|
||||||
state_db.update_online_status(&online_map)?;
|
inventory.update_online_status(&online_map)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user