From cfae8f0656edb38799c62560878c473a91473ac5 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 1 Jan 2021 16:15:13 +0100 Subject: [PATCH] tape: merge MediaStateDatabase into Inventory --- src/api2/tape/backup.rs | 3 - src/api2/tape/changer.rs | 6 +- src/api2/tape/drive.rs | 15 +- src/api2/tape/media.rs | 7 +- src/tape/inventory.rs | 237 ++++++++++++++++++++++++------- src/tape/media_pool.rs | 17 +-- src/tape/media_state_database.rs | 221 ---------------------------- src/tape/mod.rs | 3 - src/tape/online_status_map.rs | 9 +- 9 files changed, 205 insertions(+), 313 deletions(-) delete mode 100644 src/tape/media_state_database.rs diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs index afb3f392..ffccd5f0 100644 --- a/src/api2/tape/backup.rs +++ b/src/api2/tape/backup.rs @@ -34,7 +34,6 @@ use crate::{ tape::{ TAPE_STATUS_DIR, Inventory, - MediaStateDatabase, PoolWriter, MediaPool, SnapshotReader, @@ -154,12 +153,10 @@ fn update_media_online_status(drive: &str) -> Result { let status_path = Path::new(TAPE_STATUS_DIR); let mut inventory = Inventory::load(status_path)?; - let mut state_db = MediaStateDatabase::load(status_path)?; update_changer_online_status( &config, &mut inventory, - &mut state_db, &changer_name, &changer_id_list, )?; diff --git a/src/api2/tape/changer.rs b/src/api2/tape/changer.rs index a8092788..cc155842 100644 --- a/src/api2/tape/changer.rs +++ b/src/api2/tape/changer.rs @@ -20,7 +20,6 @@ use crate::{ ElementStatus, OnlineStatusMap, Inventory, - MediaStateDatabase, linux_tape_changer_list, mtx_status, mtx_status_to_online_set, @@ -57,14 +56,13 @@ pub async fn get_status(name: String) -> Result, Error> { }).await??; 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 online_set = mtx_status_to_online_set(&status, &inventory); map.update_online_status(&name, online_set)?; - let mut state_db = MediaStateDatabase::load(state_path)?; - state_db.update_online_status(&map)?; + inventory.update_online_status(&map)?; let mut list = Vec::new(); diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs index 198acab6..da2b701a 100644 --- a/src/api2/tape/drive.rs +++ b/src/api2/tape/drive.rs @@ -48,7 +48,6 @@ use crate::{ MediaChange, MediaPool, Inventory, - MediaStateDatabase, MediaCatalog, MediaId, mtx_load, @@ -421,7 +420,7 @@ fn write_media_label( MediaCatalog::overwrite(status_path, &media_id, false)?; let mut inventory = Inventory::load(status_path)?; - inventory.store(media_id.clone())?; + inventory.store(media_id.clone(), false)?; drive.rewind()?; @@ -542,12 +541,10 @@ pub async fn inventory( let state_path = Path::new(TAPE_STATUS_DIR); 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, )?; @@ -630,9 +627,8 @@ pub fn update_inventory( let state_path = Path::new(TAPE_STATUS_DIR); 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() { if changer_id.starts_with("CLN") { @@ -668,7 +664,7 @@ pub fn update_inventory( continue; } 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 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() { 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 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 { None => { diff --git a/src/api2/tape/media.rs b/src/api2/tape/media.rs index 805b82ac..a87cad0f 100644 --- a/src/api2/tape/media.rs +++ b/src/api2/tape/media.rs @@ -28,7 +28,6 @@ use crate::{ tape::{ TAPE_STATUS_DIR, Inventory, - MediaStateDatabase, MediaPool, MediaCatalog, update_online_status, @@ -118,11 +117,10 @@ pub async fn list_media(pool: Option) -> Result, Err if pool.is_none() { let inventory = Inventory::load(status_path)?; - let state_db = MediaStateDatabase::load(status_path)?; 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 { status = MediaStatus::Writable; @@ -184,9 +182,6 @@ pub fn destroy_media(changer_id: String, force: Option,) -> Result<(), Err inventory.remove_media(&uuid)?; - let mut state_db = MediaStateDatabase::load(status_path)?; - state_db.remove_media(&uuid)?; - Ok(()) } diff --git a/src/tape/inventory.rs b/src/tape/inventory.rs index 3f97f777..b8ab149f 100644 --- a/src/tape/inventory.rs +++ b/src/tape/inventory.rs @@ -26,9 +26,12 @@ use crate::{ api2::types::{ MediaSetPolicy, RetentionPolicy, + MediaStatus, + MediaLocation, }, tape::{ TAPE_STATUS_DIR, + OnlineStatusMap, file_formats::{ MediaLabel, MediaSetLabel, @@ -120,9 +123,18 @@ impl MediaSet { } } +#[derive(Serialize,Deserialize)] +struct MediaStateEntry { + id: MediaId, + #[serde(skip_serializing_if="Option::is_none")] + location: Option, + #[serde(skip_serializing_if="Option::is_none")] + status: Option, +} + /// Media Inventory pub struct Inventory { - map: BTreeMap, + map: BTreeMap, inventory_path: PathBuf, lockfile_path: PathBuf, @@ -171,8 +183,8 @@ impl Inventory { let mut set_start_times = HashMap::new(); - for media in self.map.values() { - let set = match &media.media_set_label { + for entry in self.map.values() { + let set = match &entry.id.media_set_label { None => continue, Some(set) => set, }; @@ -189,21 +201,21 @@ impl Inventory { open_file_locked(&self.lockfile_path, std::time::Duration::new(10, 0), true) } - fn load_media_db(path: &Path) -> Result, Error> { + fn load_media_db(path: &Path) -> Result, Error> { let data = file_get_json(path, Some(json!([])))?; - let media_list: Vec = serde_json::from_value(data)?; + let media_list: Vec = serde_json::from_value(data)?; let mut map = BTreeMap::new(); - for item in media_list.into_iter() { - map.insert(item.label.uuid.clone(), item); + for entry in media_list.into_iter() { + map.insert(entry.id.label.uuid.clone(), entry); } Ok(map) } 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 backup_user = crate::backup::backup_user()?; @@ -219,22 +231,40 @@ impl Inventory { } /// 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()?; self.map = Self::load_media_db(&self.inventory_path)?; - // do not overwrite unsaved pool assignments - 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.media_set_label { + let uuid = media_id.label.uuid.clone(); + + if let Some(previous) = self.map.remove(&media_id.label.uuid) { + // do not overwrite unsaved pool assignments + if media_id.media_set_label.is_none() { + if let Some(ref set) = previous.id.media_set_label { if set.uuid.as_ref() == [0u8;16] { 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.replace_file()?; Ok(()) @@ -252,14 +282,14 @@ impl Inventory { /// Lookup media 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 pub fn find_media_by_changer_id(&self, changer_id: &str) -> Option<&MediaId> { - for (_uuid, media_id) in &self.map { - if media_id.label.changer_id == changer_id { - return Some(media_id); + for (_uuid, entry) in &self.map { + if entry.id.label.changer_id == changer_id { + return Some(&entry.id); } } None @@ -271,8 +301,8 @@ impl Inventory { pub fn lookup_media_pool(&self, uuid: &Uuid) -> Option<(&str, bool)> { match self.map.get(uuid) { None => None, - Some(media_id) => { - match media_id.media_set_label { + Some(entry) => { + match entry.id.media_set_label { None => None, // not assigned to any pool Some(ref set) => { let is_empty = set.uuid.as_ref() == [0u8;16]; @@ -287,8 +317,8 @@ impl Inventory { pub fn list_pool_media(&self, pool: &str) -> Vec { let mut list = Vec::new(); - for (_uuid, media_id) in &self.map { - match media_id.media_set_label { + for (_uuid, entry) in &self.map { + match entry.id.media_set_label { None => continue, // not assigned to any pool Some(ref set) => { if set.pool != pool { @@ -297,15 +327,14 @@ impl Inventory { if set.uuid.as_ref() == [0u8;16] { // should we do this?? list.push(MediaId { - label: media_id.label.clone(), + label: entry.id.label.clone(), media_set_label: None, }) } else { - list.push(media_id.clone()); + list.push(entry.id.clone()); } } } - } list @@ -315,12 +344,12 @@ impl Inventory { pub fn list_used_media(&self) -> Vec { let mut list = Vec::new(); - for (_uuid, media_id) in &self.map { - match media_id.media_set_label { + for (_uuid, entry) in &self.map { + match entry.id.media_set_label { None => continue, // not assigned to any pool Some(ref set) => { 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 { let mut list = Vec::new(); - for (_uuid, media_id) in &self.map { - if media_id.media_set_label.is_none() { - list.push(media_id.clone()); + for (_uuid, entry) in &self.map { + if entry.id.media_set_label.is_none() { + list.push(entry.id.clone()); } } @@ -351,14 +380,14 @@ impl Inventory { let mut last_pool = None; - for media in self.map.values() { - match media.media_set_label { + for entry in self.map.values() { + match entry.id.media_set_label { None => continue, Some(MediaSetLabel { ref uuid, .. }) => { if uuid != media_set_uuid { 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 last_pool != pool { 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()); - for media in self.map.values() { - match media.media_set_label { + for entry in self.map.values() { + match entry.id.media_set_label { None => continue, Some(MediaSetLabel { seq_nr, ref uuid, .. }) => { if uuid != media_set_uuid { 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 = HashMap::new(); - for media in self.map.values() { - match media.media_set_label { + for entry in self.map.values() { + match entry.id.media_set_label { None => continue, Some(MediaSetLabel { seq_nr, ref uuid, .. }) => { @@ -411,7 +440,7 @@ impl Inventory { 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 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]); for set in set_list { @@ -448,7 +477,7 @@ impl Inventory { // consistency check - must be the only set with that ctime 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]); for set in set_list { @@ -466,7 +495,7 @@ impl Inventory { fn media_set_next_start_time(&self, media_set_uuid: &Uuid) -> Option { 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| { if &set.uuid == media_set_uuid { Some((set.pool.clone(), set.ctime)) @@ -479,7 +508,7 @@ impl Inventory { }; 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)); let mut next_ctime = None; @@ -586,7 +615,7 @@ impl Inventory { }; 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 } @@ -610,7 +639,7 @@ impl Inventory { 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 } @@ -629,12 +658,122 @@ impl Inventory { }; 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 } } +// 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) -> 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) -> 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 /// List of known media uuids @@ -663,7 +802,7 @@ pub fn complete_media_set_uuid( }; 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() } @@ -678,5 +817,5 @@ pub fn complete_media_changer_id( 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() } diff --git a/src/tape/media_pool.rs b/src/tape/media_pool.rs index 8c7f2cc3..d26200d0 100644 --- a/src/tape/media_pool.rs +++ b/src/tape/media_pool.rs @@ -26,7 +26,6 @@ use crate::{ MediaId, MediaSet, Inventory, - MediaStateDatabase, file_formats::{ MediaLabel, MediaSetLabel, @@ -47,7 +46,6 @@ pub struct MediaPool { use_offline_media: bool, inventory: Inventory, - state_db: MediaStateDatabase, current_media_set: MediaSet, } @@ -70,15 +68,12 @@ impl MediaPool { None => MediaSet::new(), }; - let state_db = MediaStateDatabase::load(state_path)?; - Ok(MediaPool { name: String::from(name), media_set_policy, retention, use_offline_media, inventory, - state_db, current_media_set, }) } @@ -104,7 +99,7 @@ impl MediaPool { 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 { MediaStatus::Full | MediaStatus::Damaged | MediaStatus::Retired => { @@ -181,7 +176,7 @@ impl MediaPool { 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 if media.status() != &MediaStatus::Full { - self.state_db.set_media_status_full(uuid)?; + self.inventory.set_media_status_full(uuid)?; } Ok(()) } @@ -304,7 +299,7 @@ impl MediaPool { 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()); @@ -347,9 +342,9 @@ impl MediaPool { media.set_media_set_label(set); - self.inventory.store(media.id().clone())?; // store persistently - self.state_db.clear_media_status(media.uuid())?; // remove Full status - + let clear_media_status = true; // remove Full status + self.inventory.store(media.id().clone(), clear_media_status)?; // store persistently + self.current_media_set.add_media(media.uuid().clone()); return Ok(media.uuid().clone()); diff --git a/src/tape/media_state_database.rs b/src/tape/media_state_database.rs deleted file mode 100644 index 2804e284..00000000 --- a/src/tape/media_state_database.rs +++ /dev/null @@ -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, - #[serde(skip_serializing_if="Option::is_none")] - s: Option, -} - -impl MediaStateEntry { - fn new(uuid: Uuid) -> Self { - MediaStateEntry { u: uuid, l: None, s: None } - } -} - -/// Stores MediaLocation and MediaState persistently -pub struct MediaStateDatabase { - - map: BTreeMap, - - 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 { - 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, Error> { - - let data = file_get_json(path, Some(json!([])))?; - let list: Vec = 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 { - - 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(()) - } -} diff --git a/src/tape/mod.rs b/src/tape/mod.rs index 296513af..485ffc66 100644 --- a/src/tape/mod.rs +++ b/src/tape/mod.rs @@ -29,9 +29,6 @@ pub use changer::*; mod drive; pub use drive::*; -mod media_state_database; -pub use media_state_database::*; - mod online_status_map; pub use online_status_map::*; diff --git a/src/tape/online_status_map.rs b/src/tape/online_status_map.rs index f809c823..64915e03 100644 --- a/src/tape/online_status_map.rs +++ b/src/tape/online_status_map.rs @@ -14,7 +14,6 @@ use crate::{ tape::{ MediaChange, Inventory, - MediaStateDatabase, mtx_status, mtx_status_to_online_set, }, @@ -97,7 +96,7 @@ pub fn update_online_status(state_path: &Path) -> Result let (config, _digest) = crate::config::drive::config()?; - let inventory = Inventory::load(state_path)?; + let mut inventory = Inventory::load(state_path)?; let changers: Vec = config.convert_to_typed_array("changer")?; @@ -135,8 +134,7 @@ pub fn update_online_status(state_path: &Path) -> Result map.update_online_status(&vtape.name, online_set)?; } - let mut state_db = MediaStateDatabase::load(state_path)?; - state_db.update_online_status(&map)?; + inventory.update_online_status(&map)?; Ok(map) } @@ -145,7 +143,6 @@ pub fn update_online_status(state_path: &Path) -> Result pub fn update_changer_online_status( drive_config: &SectionConfigData, inventory: &mut Inventory, - state_db: &mut MediaStateDatabase, changer_name: &str, changer_id_list: &Vec, ) -> Result<(), Error> { @@ -158,7 +155,7 @@ pub fn update_changer_online_status( } } online_map.update_online_status(&changer_name, online_set)?; - state_db.update_online_status(&online_map)?; + inventory.update_online_status(&online_map)?; Ok(()) }