tape: cache changer state

This commit is contained in:
Dietmar Maurer 2021-02-19 16:48:19 +01:00
parent 5b9f575648
commit 4188fd59a0
4 changed files with 88 additions and 8 deletions

View File

@ -39,6 +39,11 @@ use crate::{
name: {
schema: CHANGER_NAME_SCHEMA,
},
cache: {
description: "Use cached value.",
optional: true,
default: true,
},
},
},
returns: {
@ -50,14 +55,17 @@ use crate::{
},
)]
/// Get tape changer status
pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
pub async fn get_status(
name: String,
cache: bool,
) -> Result<Vec<MtxStatusEntry>, Error> {
let (config, _digest) = config::drive::config()?;
let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
let status = tokio::task::spawn_blocking(move || {
changer_config.status()
changer_config.status(cache)
}).await??;
let state_path = Path::new(TAPE_STATUS_DIR);

View File

@ -217,6 +217,12 @@ fn get_config(
schema: CHANGER_NAME_SCHEMA,
optional: true,
},
cache: {
description: "Use cached value.",
type: bool,
optional: true,
default: true,
},
},
},
)]

View File

@ -11,12 +11,20 @@ mod online_status_map;
pub use online_status_map::*;
use std::collections::HashSet;
use std::path::PathBuf;
use anyhow::{bail, Error};
use serde::{Serialize, Deserialize};
use serde_json::Value;
use proxmox::api::schema::parse_property_string;
use proxmox::{
api::schema::parse_property_string,
tools::fs::{
CreateOptions,
replace_file,
file_read_optional_string,
},
};
use crate::api2::types::{
SLOT_ARRAY_SCHEMA,
@ -158,7 +166,7 @@ impl MtxStatus {
/// Interface to SCSI changer devices
pub trait ScsiMediaChange {
fn status(&mut self) -> Result<MtxStatus, Error>;
fn status(&mut self, use_cache: bool) -> Result<MtxStatus, Error>;
fn load_slot(&mut self, from_slot: u64, drivenum: u64) -> Result<(), Error>;
@ -398,12 +406,29 @@ const USE_MTX: bool = false;
impl ScsiMediaChange for ScsiTapeChanger {
fn status(&mut self) -> Result<MtxStatus, Error> {
if USE_MTX {
fn status(&mut self, use_cache: bool) -> Result<MtxStatus, Error> {
if use_cache {
if let Some(state) = load_changer_state_cache(&self.name)? {
return Ok(state);
}
}
let status = if USE_MTX {
mtx::mtx_status(&self)
} else {
sg_pt_changer::status(&self)
};
match &status {
Ok(status) => {
save_changer_state_cache(&self.name, status)?;
}
Err(_) => {
delete_changer_state_cache(&self.name);
}
}
status
}
fn load_slot(&mut self, from_slot: u64, drivenum: u64) -> Result<(), Error> {
@ -434,6 +459,47 @@ impl ScsiMediaChange for ScsiTapeChanger {
}
}
fn save_changer_state_cache(
changer: &str,
state: &MtxStatus,
) -> Result<(), Error> {
let mut path = PathBuf::from("/run/proxmox-backup/changer-state");
std::fs::create_dir_all(&path)?;
path.push(changer);
let backup_user = crate::backup::backup_user()?;
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
let options = CreateOptions::new()
.perm(mode)
.owner(backup_user.uid)
.group(backup_user.gid);
let state = serde_json::to_string_pretty(state)?;
replace_file(path, state.as_bytes(), options)
}
fn delete_changer_state_cache(changer: &str) {
let mut path = PathBuf::from("/run/proxmox-backup/changer-state");
path.push(changer);
let _ = std::fs::remove_file(&path); // ignore errors
}
fn load_changer_state_cache(changer: &str) -> Result<Option<MtxStatus>, Error> {
let mut path = PathBuf::from("/run/proxmox-backup/changer-state");
path.push(changer);
let data = match file_read_optional_string(&path)? {
None => return Ok(None),
Some(data) => data,
};
let state = serde_json::from_str(&data)?;
Ok(Some(state))
}
/// Implements MediaChange using 'mtx' linux cli tool
pub struct MtxMediaChanger {
drive_name: String, // used for error messages
@ -469,7 +535,7 @@ impl MediaChange for MtxMediaChanger {
}
fn status(&mut self) -> Result<MtxStatus, Error> {
self.config.status()
self.config.status(false)
}
fn transfer_media(&mut self, from: u64, to: u64) -> Result<(), Error> {

View File

@ -142,7 +142,7 @@ pub fn update_online_status(state_path: &Path, changer: Option<&str>) -> Result<
}
found_changer = true;
}
let status = match changer_config.status() {
let status = match changer_config.status(false) {
Ok(status) => status,
Err(err) => {
eprintln!("unable to get changer '{}' status - {}", changer_config.name, err);