use std::collections::{HashMap, BTreeMap};
use std::path::{Path, PathBuf};
+use std::os::unix::io::AsRawFd;
use anyhow::{bail, Error};
use serde::{Serialize, Deserialize};
fs::{
open_file_locked,
replace_file,
+ fchown,
file_get_json,
CreateOptions,
},
},
tape::{
TAPE_STATUS_DIR,
- OnlineStatusMap,
MediaSet,
file_formats::{
MediaLabel,
MediaSetLabel,
},
+ changer::OnlineStatusMap,
},
};
/// 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)
+ let file = open_file_locked(&self.lockfile_path, std::time::Duration::new(10, 0), true)?;
+ if cfg!(test) {
+ // We cannot use chown inside test environment (no permissions)
+ return Ok(file);
+ }
+
+ let backup_user = crate::backup::backup_user()?;
+ fchown(file.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
+
+ Ok(file)
}
fn load_media_db(path: &Path) -> Result<BTreeMap<Uuid, MediaStateEntry>, Error> {
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()?;
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
- let options = CreateOptions::new()
- .perm(mode)
- .owner(backup_user.uid)
- .group(backup_user.gid);
+
+ let options = if cfg!(test) {
+ // We cannot use chown inside test environment (no permissions)
+ CreateOptions::new().perm(mode)
+ } else {
+ let backup_user = crate::backup::backup_user()?;
+ CreateOptions::new()
+ .perm(mode)
+ .owner(backup_user.uid)
+ .group(backup_user.gid)
+ };
replace_file(&self.inventory_path, raw.as_bytes(), options)?;
self.map.get(uuid).map(|entry| &entry.id)
}
+ /// List all media Uuids
+ pub fn media_list(&self) -> Vec<&Uuid> {
+ self.map.keys().collect()
+ }
+
/// find media by label_text
pub fn find_media_by_label_text(&self, label_text: &str) -> Option<&MediaId> {
- for (_uuid, entry) in &self.map {
+ self.map.values().find_map(|entry| {
if entry.id.label.label_text == label_text {
- return Some(&entry.id);
+ Some(&entry.id)
+ } else {
+ None
}
- }
- None
+ })
}
/// Lookup media pool
pub fn list_pool_media(&self, pool: &str) -> Vec<MediaId> {
let mut list = Vec::new();
- for (_uuid, entry) in &self.map {
+ for entry in self.map.values() {
match entry.id.media_set_label {
None => continue, // not assigned to any pool
Some(ref set) => {
continue; // belong to another pool
}
- if set.uuid.as_ref() == [0u8;16] { // should we do this??
+ if set.uuid.as_ref() == [0u8;16] {
list.push(MediaId {
label: entry.id.label.clone(),
media_set_label: None,
pub fn list_used_media(&self) -> Vec<MediaId> {
let mut list = Vec::new();
- for (_uuid, entry) in &self.map {
+ for entry in self.map.values() {
match entry.id.media_set_label {
None => continue, // not assigned to any pool
Some(ref set) => {
/// List media not assigned to any pool
pub fn list_unassigned_media(&self) -> Vec<MediaId> {
- let mut list = Vec::new();
-
- for (_uuid, entry) in &self.map {
+ self.map.values().filter_map(|entry|
if entry.id.media_set_label.is_none() {
- list.push(entry.id.clone());
+ Some(entry.id.clone())
+ } else {
+ None
}
- }
-
- list
+ ).collect()
}
pub fn media_set_start_time(&self, media_set_uuid: &Uuid) -> Option<i64> {
- self.media_set_start_times.get(media_set_uuid).map(|t| *t)
+ self.media_set_start_times.get(media_set_uuid).copied()
}
/// Lookup media set pool
match last_pool {
Some(pool) => Ok(pool.to_string()),
- None => bail!("media set {} is incomplete - unable to lookup pool"),
+ None => bail!("media set {} is incomplete - unable to lookup pool", media_set_uuid),
}
}
let set_list = self.map.values()
.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 {
match last_set {
// consistency check - must be the only set with that ctime
let set_list = self.map.values()
.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 {
if set.uuid != uuid && set.ctime >= ctime { // should not happen
let set_list = self.map.values()
.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;
Some(time) => time,
};
- let max_use_time = match media_set_policy {
- MediaSetPolicy::ContinueCurrent => {
- match self.media_set_next_start_time(&set.uuid) {
- Some(next_start_time) => next_start_time,
- None => return i64::MAX,
- }
+ let max_use_time = match self.media_set_next_start_time(&set.uuid) {
+ Some(next_start_time) => {
+ match media_set_policy {
+ MediaSetPolicy::AlwaysCreate => set_start_time,
+ _ => next_start_time,
+ }
}
- MediaSetPolicy::AlwaysCreate => {
- set_start_time + 1
- }
- MediaSetPolicy::CreateAt(ref event) => {
- match compute_next_event(event, set_start_time, false) {
- Ok(Some(next)) => next,
- Ok(None) | Err(_) => return i64::MAX,
+ None => {
+ match media_set_policy {
+ MediaSetPolicy::ContinueCurrent => {
+ return i64::MAX;
+ }
+ MediaSetPolicy::AlwaysCreate => {
+ set_start_time
+ }
+ MediaSetPolicy::CreateAt(ref event) => {
+ match compute_next_event(event, set_start_time, false) {
+ Ok(Some(next)) => next,
+ Ok(None) | Err(_) => return i64::MAX,
+ }
+ }
}
}
};
) -> Result<String, Error> {
if let Some(ctime) = self.media_set_start_time(media_set_uuid) {
- let mut template = template.unwrap_or(String::from("%c"));
+ let mut template = template.unwrap_or_else(|| String::from("%c"));
template = template.replace("%id%", &media_set_uuid.to_string());
proxmox::tools::time::strftime_local(&template, ctime)
} else {
// Helpers to simplify testing
- /// Genreate and insert a new free tape (test helper)
+ /// Generate and insert a new free tape (test helper)
pub fn generate_free_tape(&mut self, label_text: &str, ctime: i64) -> Uuid {
let label = MediaLabel {
uuid
}
- /// Genreate and insert a new tape assigned to a specific pool
+ /// Generate and insert a new tape assigned to a specific pool
/// (test helper)
pub fn generate_assigned_tape(
&mut self,
uuid
}
- /// Genreate and insert a used tape (test helper)
+ /// Generate and insert a used tape (test helper)
pub fn generate_used_tape(
&mut self,
label_text: &str,
self.set_media_status(uuid, Some(MediaStatus::Damaged))
}
+ /// Lock database, reload database, set status to Retired, store database
+ pub fn set_media_status_retired(&mut self, uuid: &Uuid) -> Result<(), Error> {
+ self.set_media_status(uuid, Some(MediaStatus::Retired))
+ }
+
/// 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)
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);
- }
+ } 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);
}
}
}