move drive config to pbs_config workspace

Also moved the tape type definitions to pbs_api_types.
This commit is contained in:
Dietmar Maurer
2021-09-03 09:10:18 +02:00
parent ccb3b45e18
commit 1ce8e905ea
36 changed files with 323 additions and 357 deletions

View File

@ -10,25 +10,25 @@ use proxmox::api::{
schema::parse_property_string,
};
use pbs_api_types::{
Authid,
PROXMOX_CONFIG_DIGEST_SCHEMA,
CHANGER_NAME_SCHEMA,
SCSI_CHANGER_PATH_SCHEMA,
SLOT_ARRAY_SCHEMA,
EXPORT_SLOT_LIST_SCHEMA,
ScsiTapeChanger,
LtoTapeDrive,
};
use crate::{
config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_TAPE_AUDIT,
PRIV_TAPE_MODIFY,
},
},
api2::types::{
Authid,
PROXMOX_CONFIG_DIGEST_SCHEMA,
CHANGER_NAME_SCHEMA,
SCSI_CHANGER_PATH_SCHEMA,
SLOT_ARRAY_SCHEMA,
EXPORT_SLOT_LIST_SCHEMA,
ScsiTapeChanger,
LtoTapeDrive,
},
tape::{
linux_tape_changer_list,
check_drive_path,
@ -62,9 +62,9 @@ pub fn create_changer(
export_slots: Option<String>,
) -> Result<(), Error> {
let _lock = config::drive::lock()?;
let _lock = pbs_config::drive::lock()?;
let (mut config, _digest) = config::drive::config()?;
let (mut config, _digest) = pbs_config::drive::config()?;
let linux_changers = linux_tape_changer_list();
@ -90,7 +90,7 @@ pub fn create_changer(
config.set_data(&name, "changer", &item)?;
config::drive::save_config(&config)?;
pbs_config::drive::save_config(&config)?;
Ok(())
}
@ -117,7 +117,7 @@ pub fn get_config(
mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<ScsiTapeChanger, Error> {
let (config, digest) = config::drive::config()?;
let (config, digest) = pbs_config::drive::config()?;
let data: ScsiTapeChanger = config.lookup("changer", &name)?;
@ -150,7 +150,7 @@ pub fn list_changers(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let (config, digest) = config::drive::config()?;
let (config, digest) = pbs_config::drive::config()?;
let list: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
@ -219,9 +219,9 @@ pub fn update_changer(
_param: Value,
) -> Result<(), Error> {
let _lock = config::drive::lock()?;
let _lock = pbs_config::drive::lock()?;
let (mut config, expected_digest) = config::drive::config()?;
let (mut config, expected_digest) = pbs_config::drive::config()?;
if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?;
@ -268,7 +268,7 @@ pub fn update_changer(
config.set_data(&name, "changer", &data)?;
config::drive::save_config(&config)?;
pbs_config::drive::save_config(&config)?;
Ok(())
}
@ -289,9 +289,9 @@ pub fn update_changer(
/// Delete a tape changer configuration
pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
let _lock = config::drive::lock()?;
let _lock = pbs_config::drive::lock()?;
let (mut config, _digest) = config::drive::config()?;
let (mut config, _digest) = pbs_config::drive::config()?;
match config.sections.get(&name) {
Some((section_type, _)) => {
@ -312,7 +312,7 @@ pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
}
}
config::drive::save_config(&config)?;
pbs_config::drive::save_config(&config)?;
Ok(())
}

View File

@ -4,23 +4,23 @@ use serde_json::Value;
use proxmox::api::{api, Router, RpcEnvironment, Permission};
use pbs_api_types::{
Authid,
PROXMOX_CONFIG_DIGEST_SCHEMA,
DRIVE_NAME_SCHEMA,
LtoTapeDrive,
LtoTapeDriveUpdater,
ScsiTapeChanger,
};
use crate::{
config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_TAPE_AUDIT,
PRIV_TAPE_MODIFY,
},
},
api2::types::{
Authid,
PROXMOX_CONFIG_DIGEST_SCHEMA,
DRIVE_NAME_SCHEMA,
LtoTapeDrive,
LtoTapeDriveUpdater,
ScsiTapeChanger,
},
tape::{
lto_tape_device_list,
check_drive_path,
@ -44,9 +44,9 @@ use crate::{
/// Create a new drive
pub fn create_drive(config: LtoTapeDrive) -> Result<(), Error> {
let _lock = config::drive::lock()?;
let _lock = pbs_config::drive::lock()?;
let (mut section_config, _digest) = config::drive::config()?;
let (mut section_config, _digest) = pbs_config::drive::config()?;
let lto_drives = lto_tape_device_list();
@ -65,7 +65,7 @@ pub fn create_drive(config: LtoTapeDrive) -> Result<(), Error> {
section_config.set_data(&config.name, "lto", &config)?;
config::drive::save_config(&section_config)?;
pbs_config::drive::save_config(&section_config)?;
Ok(())
}
@ -92,7 +92,7 @@ pub fn get_config(
mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<LtoTapeDrive, Error> {
let (config, digest) = config::drive::config()?;
let (config, digest) = pbs_config::drive::config()?;
let data: LtoTapeDrive = config.lookup("lto", &name)?;
@ -125,7 +125,7 @@ pub fn list_drives(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let (config, digest) = config::drive::config()?;
let (config, digest) = pbs_config::drive::config()?;
let drive_list: Vec<LtoTapeDrive> = config.convert_to_typed_array("lto")?;
@ -192,9 +192,9 @@ pub fn update_drive(
_param: Value,
) -> Result<(), Error> {
let _lock = config::drive::lock()?;
let _lock = pbs_config::drive::lock()?;
let (mut config, expected_digest) = config::drive::config()?;
let (mut config, expected_digest) = pbs_config::drive::config()?;
if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?;
@ -239,7 +239,7 @@ pub fn update_drive(
config.set_data(&name, "lto", &data)?;
config::drive::save_config(&config)?;
pbs_config::drive::save_config(&config)?;
Ok(())
}
@ -260,9 +260,9 @@ pub fn update_drive(
/// Delete a drive configuration
pub fn delete_drive(name: String, _param: Value) -> Result<(), Error> {
let _lock = config::drive::lock()?;
let _lock = pbs_config::drive::lock()?;
let (mut config, _digest) = config::drive::config()?;
let (mut config, _digest) = pbs_config::drive::config()?;
match config.sections.get(&name) {
Some((section_type, _)) => {
@ -274,7 +274,7 @@ pub fn delete_drive(name: String, _param: Value) -> Result<(), Error> {
None => bail!("Delete drive '{}' failed - no such drive", name),
}
config::drive::save_config(&config)?;
pbs_config::drive::save_config(&config)?;
Ok(())
}

View File

@ -123,7 +123,7 @@ pub fn list_tape_backup_jobs(
let (job_config, digest) = config::tape_job::config()?;
let (pool_config, _pool_digest) = config::media_pool::config()?;
let (drive_config, _digest) = config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
let job_list_iter = job_config
.convert_to_typed_array("backup")?
@ -194,7 +194,7 @@ pub fn do_tape_backup_job(
let (config, _digest) = config::media_pool::config()?;
let pool_config: MediaPoolConfig = config.lookup("pool", &setup.pool)?;
let (drive_config, _digest) = config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
// for scheduled jobs we acquire the lock later in the worker
let drive_lock = if schedule.is_some() {
@ -373,7 +373,7 @@ pub fn backup(
let (config, _digest) = config::media_pool::config()?;
let pool_config: MediaPoolConfig = config.lookup("pool", &setup.pool)?;
let (drive_config, _digest) = config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
// early check/lock before starting worker
let drive_lock = lock_tape_device(&drive_config, &setup.drive)?;
@ -578,7 +578,7 @@ fn backup_worker(
// Try to update the the media online status
fn update_media_online_status(drive: &str) -> Result<Option<String>, Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
if let Ok(Some((mut changer, changer_name))) = media_changer(&config, drive) {

View File

@ -9,7 +9,6 @@ use proxmox::list_subdirs_api_method;
use crate::{
config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_TAPE_AUDIT,
@ -71,7 +70,7 @@ pub async fn get_status(
cache: bool,
) -> Result<Vec<MtxStatusEntry>, Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
@ -171,7 +170,7 @@ pub async fn transfer(
to: u64,
) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let mut changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
@ -205,7 +204,7 @@ pub fn list_changers(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let linux_changers = linux_tape_changer_list();

View File

@ -75,6 +75,7 @@ use crate::{
TapeDriver,
LtoTapeHandle,
open_lto_tape_device,
open_lto_tape_drive,
media_changer,
required_media_changer,
open_drive,
@ -101,7 +102,7 @@ where
+ FnOnce(Arc<WorkerTask>, SectionConfigData) -> Result<(), Error>,
{
// early check/lock before starting worker
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let lock_guard = lock_tape_device(&config, &drive)?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
@ -125,7 +126,7 @@ where
R: Send + 'static,
{
// early check/lock before starting worker
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let lock_guard = lock_tape_device(&config, &drive)?;
tokio::task::spawn_blocking(move || {
let _lock_guard = lock_guard;
@ -1140,7 +1141,7 @@ pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error>
"reading cartridge memory".to_string(),
move |config| {
let drive_config: LtoTapeDrive = config.lookup("lto", &drive)?;
let mut handle = drive_config.open()?;
let mut handle = open_lto_tape_drive(&drive_config)?;
handle.cartridge_memory()
}
@ -1170,7 +1171,7 @@ pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Er
"reading volume statistics".to_string(),
move |config| {
let drive_config: LtoTapeDrive = config.lookup("lto", &drive)?;
let mut handle = drive_config.open()?;
let mut handle = open_lto_tape_drive(&drive_config)?;
handle.volume_statistics()
}
@ -1376,7 +1377,7 @@ pub fn list_drives(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let (config, _) = config::drive::config()?;
let (config, _) = pbs_config::drive::config()?;
let lto_drives = lto_tape_device_list();

View File

@ -2,7 +2,6 @@ use std::path::Path;
use std::collections::HashSet;
use anyhow::{bail, format_err, Error};
use serde::{Serialize, Deserialize};
use proxmox::{
api::{api, Router, SubdirMap, RpcEnvironment, Permission},
@ -11,6 +10,11 @@ use proxmox::{
};
use pbs_datastore::backup_info::BackupDir;
use pbs_api_types::{
MEDIA_POOL_NAME_SCHEMA, MEDIA_LABEL_SCHEMA, MEDIA_UUID_SCHEMA, CHANGER_NAME_SCHEMA,
VAULT_NAME_SCHEMA, Authid, MediaPoolConfig, MediaListEntry, MediaSetListEntry,
MediaStatus, MediaContentEntry, MediaContentListFilter,
};
use crate::{
config::{
@ -20,23 +24,7 @@ use crate::{
PRIV_TAPE_AUDIT,
},
},
api2::types::{
Authid,
BACKUP_ID_SCHEMA,
BACKUP_TYPE_SCHEMA,
MEDIA_POOL_NAME_SCHEMA,
MEDIA_LABEL_SCHEMA,
MEDIA_UUID_SCHEMA,
MEDIA_SET_UUID_SCHEMA,
CHANGER_NAME_SCHEMA,
MediaPoolConfig,
MediaListEntry,
MediaSetListEntry,
MediaStatus,
MediaContentEntry,
VAULT_NAME_SCHEMA,
},
tape::{
tape::{
TAPE_STATUS_DIR,
Inventory,
MediaPool,
@ -393,46 +381,6 @@ pub fn destroy_media(label_text: String, force: Option<bool>,) -> Result<(), Err
Ok(())
}
#[api(
properties: {
pool: {
schema: MEDIA_POOL_NAME_SCHEMA,
optional: true,
},
"label-text": {
schema: MEDIA_LABEL_SCHEMA,
optional: true,
},
"media": {
schema: MEDIA_UUID_SCHEMA,
optional: true,
},
"media-set": {
schema: MEDIA_SET_UUID_SCHEMA,
optional: true,
},
"backup-type": {
schema: BACKUP_TYPE_SCHEMA,
optional: true,
},
"backup-id": {
schema: BACKUP_ID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Content list filter parameters
pub struct MediaContentListFilter {
pub pool: Option<String>,
pub label_text: Option<String>,
pub media: Option<Uuid>,
pub media_set: Option<Uuid>,
pub backup_type: Option<String>,
pub backup_id: Option<String>,
}
#[api(
input: {
properties: {

View File

@ -49,7 +49,6 @@ use crate::{
TAPE_RESTORE_SNAPSHOT_SCHEMA,
},
config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_DATASTORE_BACKUP,
@ -271,7 +270,7 @@ pub fn restore(
bail!("no permissions on /tape/pool/{}", pool);
}
let (drive_config, _digest) = config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
// early check/lock before starting worker
let drive_lock = lock_tape_device(&drive_config, &drive)?;

View File

@ -8,9 +8,6 @@ use proxmox::const_regex;
use crate::config::acl::Role;
mod tape;
pub use tape::*;
mod acme;
pub use acme::*;
@ -43,7 +40,6 @@ const_regex!{
pub DATASTORE_MAP_REGEX = concat!(r"(:?", PROXMOX_SAFE_ID_REGEX_STR!(), r"=)?", PROXMOX_SAFE_ID_REGEX_STR!());
pub TAPE_RESTORE_SNAPSHOT_REGEX = concat!(r"^", PROXMOX_SAFE_ID_REGEX_STR!(), r":", SNAPSHOT_PATH_REGEX_STR!(), r"$");
}
pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
@ -70,9 +66,6 @@ pub const BLOCKDEVICE_NAME_FORMAT: ApiStringFormat =
pub const DATASTORE_MAP_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&DATASTORE_MAP_REGEX);
pub const TAPE_RESTORE_SNAPSHOT_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX);
pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
.format(&PASSWORD_FORMAT)
.min_length(1)
@ -85,12 +78,6 @@ pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
.max_length(64)
.schema();
pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA: Schema = StringSchema::new(
"Tape encryption key fingerprint (sha256)."
)
.format(&FINGERPRINT_SHA256_FORMAT)
.schema();
pub const CHUNK_DIGEST_SCHEMA: Schema = StringSchema::new("Chunk digest (SHA256).")
.format(&CHUNK_DIGEST_FORMAT)
.schema();
@ -238,22 +225,6 @@ pub const DATASTORE_MAP_LIST_SCHEMA: Schema = StringSchema::new(
.format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA))
.schema();
pub const TAPE_RESTORE_SNAPSHOT_SCHEMA: Schema = StringSchema::new(
"A snapshot in the format: 'store:type/id/time")
.format(&TAPE_RESTORE_SNAPSHOT_FORMAT)
.type_text("store:type/id/time")
.schema();
pub const MEDIA_SET_UUID_SCHEMA: Schema =
StringSchema::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).")
.format(&UUID_FORMAT)
.schema();
pub const MEDIA_UUID_SCHEMA: Schema =
StringSchema::new("Media Uuid.")
.format(&UUID_FORMAT)
.schema();
pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run sync job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))

View File

@ -1,135 +0,0 @@
//! Types for tape changer API
use serde::{Deserialize, Serialize};
use proxmox::api::{
api,
schema::{
Schema,
ApiStringFormat,
ArraySchema,
IntegerSchema,
StringSchema,
},
};
use crate::api2::types::{
PROXMOX_SAFE_ID_FORMAT,
OptionalDeviceIdentification,
};
pub const CHANGER_NAME_SCHEMA: Schema = StringSchema::new("Tape Changer Identifier.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const SCSI_CHANGER_PATH_SCHEMA: Schema = StringSchema::new(
"Path to Linux generic SCSI device (e.g. '/dev/sg4')")
.schema();
pub const MEDIA_LABEL_SCHEMA: Schema = StringSchema::new("Media Label/Barcode.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(2)
.max_length(32)
.schema();
pub const SLOT_ARRAY_SCHEMA: Schema = ArraySchema::new(
"Slot list.", &IntegerSchema::new("Slot number")
.minimum(1)
.schema())
.schema();
pub const EXPORT_SLOT_LIST_SCHEMA: Schema = StringSchema::new("\
A list of slot numbers, comma separated. Those slots are reserved for
Import/Export, i.e. any media in those slots are considered to be
'offline'.
")
.format(&ApiStringFormat::PropertyString(&SLOT_ARRAY_SCHEMA))
.schema();
#[api(
properties: {
name: {
schema: CHANGER_NAME_SCHEMA,
},
path: {
schema: SCSI_CHANGER_PATH_SCHEMA,
},
"export-slots": {
schema: EXPORT_SLOT_LIST_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// SCSI tape changer
pub struct ScsiTapeChanger {
pub name: String,
pub path: String,
#[serde(skip_serializing_if="Option::is_none")]
pub export_slots: Option<String>,
}
#[api(
properties: {
config: {
type: ScsiTapeChanger,
},
info: {
type: OptionalDeviceIdentification,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Changer config with optional device identification attributes
pub struct ChangerListEntry {
#[serde(flatten)]
pub config: ScsiTapeChanger,
#[serde(flatten)]
pub info: OptionalDeviceIdentification,
}
#[api()]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Mtx Entry Kind
pub enum MtxEntryKind {
/// Drive
Drive,
/// Slot
Slot,
/// Import/Export Slot
ImportExport,
}
#[api(
properties: {
"entry-kind": {
type: MtxEntryKind,
},
"label-text": {
schema: MEDIA_LABEL_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Mtx Status Entry
pub struct MtxStatusEntry {
pub entry_kind: MtxEntryKind,
/// The ID of the slot or drive
pub entry_id: u64,
/// The media label (volume tag) if the slot/drive is full
#[serde(skip_serializing_if="Option::is_none")]
pub label_text: Option<String>,
/// The slot the drive was loaded from
#[serde(skip_serializing_if="Option::is_none")]
pub loaded_slot: Option<u64>,
/// The current state of the drive
#[serde(skip_serializing_if="Option::is_none")]
pub state: Option<String>,
}

View File

@ -1,55 +0,0 @@
use ::serde::{Deserialize, Serialize};
use proxmox::api::api;
#[api()]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Optional Device Identification Attributes
pub struct OptionalDeviceIdentification {
/// Vendor (autodetected)
#[serde(skip_serializing_if="Option::is_none")]
pub vendor: Option<String>,
/// Model (autodetected)
#[serde(skip_serializing_if="Option::is_none")]
pub model: Option<String>,
/// Serial number (autodetected)
#[serde(skip_serializing_if="Option::is_none")]
pub serial: Option<String>,
}
#[api()]
#[derive(Debug,Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Kind of device
pub enum DeviceKind {
/// Tape changer (Autoloader, Robot)
Changer,
/// Normal SCSI tape device
Tape,
}
#[api(
properties: {
kind: {
type: DeviceKind,
},
},
)]
#[derive(Debug,Serialize,Deserialize)]
/// Tape device information
pub struct TapeDeviceInfo {
pub kind: DeviceKind,
/// Path to the linux device node
pub path: String,
/// Serial number (autodetected)
pub serial: String,
/// Vendor (autodetected)
pub vendor: String,
/// Model (autodetected)
pub model: String,
/// Device major number
pub major: u32,
/// Device minor number
pub minor: u32,
}

View File

@ -1,285 +0,0 @@
//! Types for tape drive API
use std::convert::TryFrom;
use anyhow::{bail, Error};
use serde::{Deserialize, Serialize};
use proxmox::api::{
api,
schema::{Schema, IntegerSchema, StringSchema, Updater},
};
use crate::api2::types::{
PROXMOX_SAFE_ID_FORMAT,
CHANGER_NAME_SCHEMA,
OptionalDeviceIdentification,
};
pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const LTO_DRIVE_PATH_SCHEMA: Schema = StringSchema::new(
"The path to a LTO SCSI-generic tape device (i.e. '/dev/sg0')")
.schema();
pub const CHANGER_DRIVENUM_SCHEMA: Schema = IntegerSchema::new(
"Associated changer drive number (requires option changer)")
.minimum(0)
.maximum(255)
.default(0)
.schema();
#[api(
properties: {
name: {
schema: DRIVE_NAME_SCHEMA,
}
}
)]
#[derive(Serialize,Deserialize)]
/// Simulate tape drives (only for test and debug)
#[serde(rename_all = "kebab-case")]
pub struct VirtualTapeDrive {
pub name: String,
/// Path to directory
pub path: String,
/// Virtual tape size
#[serde(skip_serializing_if="Option::is_none")]
pub max_size: Option<usize>,
}
#[api(
properties: {
name: {
schema: DRIVE_NAME_SCHEMA,
},
path: {
schema: LTO_DRIVE_PATH_SCHEMA,
},
changer: {
schema: CHANGER_NAME_SCHEMA,
optional: true,
},
"changer-drivenum": {
schema: CHANGER_DRIVENUM_SCHEMA,
optional: true,
},
}
)]
#[derive(Serialize,Deserialize,Updater)]
#[serde(rename_all = "kebab-case")]
/// Lto SCSI tape driver
pub struct LtoTapeDrive {
#[updater(skip)]
pub name: String,
pub path: String,
#[serde(skip_serializing_if="Option::is_none")]
pub changer: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub changer_drivenum: Option<u64>,
}
#[api(
properties: {
config: {
type: LtoTapeDrive,
},
info: {
type: OptionalDeviceIdentification,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Drive list entry
pub struct DriveListEntry {
#[serde(flatten)]
pub config: LtoTapeDrive,
#[serde(flatten)]
pub info: OptionalDeviceIdentification,
/// the state of the drive if locked
#[serde(skip_serializing_if="Option::is_none")]
pub state: Option<String>,
}
#[api()]
#[derive(Serialize,Deserialize)]
/// Medium auxiliary memory attributes (MAM)
pub struct MamAttribute {
/// Attribute id
pub id: u16,
/// Attribute name
pub name: String,
/// Attribute value
pub value: String,
}
#[api()]
#[derive(Serialize,Deserialize,Copy,Clone,Debug)]
pub enum TapeDensity {
/// Unknown (no media loaded)
Unknown,
/// LTO1
LTO1,
/// LTO2
LTO2,
/// LTO3
LTO3,
/// LTO4
LTO4,
/// LTO5
LTO5,
/// LTO6
LTO6,
/// LTO7
LTO7,
/// LTO7M8
LTO7M8,
/// LTO8
LTO8,
}
impl TryFrom<u8> for TapeDensity {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let density = match value {
0x00 => TapeDensity::Unknown,
0x40 => TapeDensity::LTO1,
0x42 => TapeDensity::LTO2,
0x44 => TapeDensity::LTO3,
0x46 => TapeDensity::LTO4,
0x58 => TapeDensity::LTO5,
0x5a => TapeDensity::LTO6,
0x5c => TapeDensity::LTO7,
0x5d => TapeDensity::LTO7M8,
0x5e => TapeDensity::LTO8,
_ => bail!("unknown tape density code 0x{:02x}", value),
};
Ok(density)
}
}
#[api(
properties: {
density: {
type: TapeDensity,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Drive/Media status for Lto SCSI drives.
///
/// Media related data is optional - only set if there is a medium
/// loaded.
pub struct LtoDriveAndMediaStatus {
/// Vendor
pub vendor: String,
/// Product
pub product: String,
/// Revision
pub revision: String,
/// Block size (0 is variable size)
pub blocksize: u32,
/// Compression enabled
pub compression: bool,
/// Drive buffer mode
pub buffer_mode: u8,
/// Tape density
pub density: TapeDensity,
/// Media is write protected
#[serde(skip_serializing_if="Option::is_none")]
pub write_protect: Option<bool>,
/// Tape Alert Flags
#[serde(skip_serializing_if="Option::is_none")]
pub alert_flags: Option<String>,
/// Current file number
#[serde(skip_serializing_if="Option::is_none")]
pub file_number: Option<u64>,
/// Current block number
#[serde(skip_serializing_if="Option::is_none")]
pub block_number: Option<u64>,
/// Medium Manufacture Date (epoch)
#[serde(skip_serializing_if="Option::is_none")]
pub manufactured: Option<i64>,
/// Total Bytes Read in Medium Life
#[serde(skip_serializing_if="Option::is_none")]
pub bytes_read: Option<u64>,
/// Total Bytes Written in Medium Life
#[serde(skip_serializing_if="Option::is_none")]
pub bytes_written: Option<u64>,
/// Number of mounts for the current volume (i.e., Thread Count)
#[serde(skip_serializing_if="Option::is_none")]
pub volume_mounts: Option<u64>,
/// Count of the total number of times the medium has passed over
/// the head.
#[serde(skip_serializing_if="Option::is_none")]
pub medium_passes: Option<u64>,
/// Estimated tape wearout factor (assuming max. 16000 end-to-end passes)
#[serde(skip_serializing_if="Option::is_none")]
pub medium_wearout: Option<f64>,
}
#[api()]
/// Volume statistics from SCSI log page 17h
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Lp17VolumeStatistics {
/// Volume mounts (thread count)
pub volume_mounts: u64,
/// Total data sets written
pub volume_datasets_written: u64,
/// Write retries
pub volume_recovered_write_data_errors: u64,
/// Total unrecovered write errors
pub volume_unrecovered_write_data_errors: u64,
/// Total suspended writes
pub volume_write_servo_errors: u64,
/// Total fatal suspended writes
pub volume_unrecovered_write_servo_errors: u64,
/// Total datasets read
pub volume_datasets_read: u64,
/// Total read retries
pub volume_recovered_read_errors: u64,
/// Total unrecovered read errors
pub volume_unrecovered_read_errors: u64,
/// Last mount unrecovered write errors
pub last_mount_unrecovered_write_errors: u64,
/// Last mount unrecovered read errors
pub last_mount_unrecovered_read_errors: u64,
/// Last mount bytes written
pub last_mount_bytes_written: u64,
/// Last mount bytes read
pub last_mount_bytes_read: u64,
/// Lifetime bytes written
pub lifetime_bytes_written: u64,
/// Lifetime bytes read
pub lifetime_bytes_read: u64,
/// Last load write compression ratio
pub last_load_write_compression_ratio: u64,
/// Last load read compression ratio
pub last_load_read_compression_ratio: u64,
/// Medium mount time
pub medium_mount_time: u64,
/// Medium ready time
pub medium_ready_time: u64,
/// Total native capacity
pub total_native_capacity: u64,
/// Total used native capacity
pub total_used_native_capacity: u64,
/// Write protect
pub write_protect: bool,
/// Volume is WORM
pub worm: bool,
/// Beginning of medium passes
pub beginning_of_medium_passes: u64,
/// Middle of medium passes
pub middle_of_tape_passes: u64,
/// Volume serial number
pub serial: String,
}

View File

@ -1,173 +0,0 @@
use ::serde::{Deserialize, Serialize};
use proxmox::{
api::api,
tools::Uuid,
};
use crate::api2::types::{
MEDIA_UUID_SCHEMA,
MEDIA_SET_UUID_SCHEMA,
MediaStatus,
MediaLocation,
};
#[api(
properties: {
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media Set list entry
pub struct MediaSetListEntry {
/// Media set name
pub media_set_name: String,
pub media_set_uuid: Uuid,
/// MediaSet creation time stamp
pub media_set_ctime: i64,
/// Media Pool
pub pool: String,
}
#[api(
properties: {
location: {
type: MediaLocation,
},
status: {
type: MediaStatus,
},
uuid: {
schema: MEDIA_UUID_SCHEMA,
},
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media list entry
pub struct MediaListEntry {
/// Media label text (or Barcode)
pub label_text: String,
pub uuid: Uuid,
/// Creation time stamp
pub ctime: i64,
pub location: MediaLocation,
pub status: MediaStatus,
/// Expired flag
pub expired: bool,
/// Catalog status OK
pub catalog: bool,
/// Media set name
#[serde(skip_serializing_if="Option::is_none")]
pub media_set_name: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub media_set_uuid: Option<Uuid>,
/// Media set seq_nr
#[serde(skip_serializing_if="Option::is_none")]
pub seq_nr: Option<u64>,
/// MediaSet creation time stamp
#[serde(skip_serializing_if="Option::is_none")]
pub media_set_ctime: Option<i64>,
/// Media Pool
#[serde(skip_serializing_if="Option::is_none")]
pub pool: Option<String>,
}
#[api(
properties: {
uuid: {
schema: MEDIA_UUID_SCHEMA,
},
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media label info
pub struct MediaIdFlat {
/// Unique ID
pub uuid: Uuid,
/// Media label text (or Barcode)
pub label_text: String,
/// Creation time stamp
pub ctime: i64,
// All MediaSet properties are optional here
/// MediaSet Pool
#[serde(skip_serializing_if="Option::is_none")]
pub pool: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub media_set_uuid: Option<Uuid>,
/// MediaSet media sequence number
#[serde(skip_serializing_if="Option::is_none")]
pub seq_nr: Option<u64>,
/// MediaSet Creation time stamp
#[serde(skip_serializing_if="Option::is_none")]
pub media_set_ctime: Option<i64>,
/// Encryption key fingerprint
#[serde(skip_serializing_if="Option::is_none")]
pub encryption_key_fingerprint: Option<String>,
}
#[api(
properties: {
uuid: {
schema: MEDIA_UUID_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Label with optional Uuid
pub struct LabelUuidMap {
/// Changer label text (or Barcode)
pub label_text: String,
/// Associated Uuid (if any)
pub uuid: Option<Uuid>,
}
#[api(
properties: {
uuid: {
schema: MEDIA_UUID_SCHEMA,
},
"media-set-uuid": {
schema: MEDIA_SET_UUID_SCHEMA,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all = "kebab-case")]
/// Media content list entry
pub struct MediaContentEntry {
/// Media label text (or Barcode)
pub label_text: String,
/// Media Uuid
pub uuid: Uuid,
/// Media set name
pub media_set_name: String,
/// Media set uuid
pub media_set_uuid: Uuid,
/// MediaSet Creation time stamp
pub media_set_ctime: i64,
/// Media set seq_nr
pub seq_nr: u64,
/// Media Pool
pub pool: String,
/// Datastore Name
pub store: String,
/// Backup snapshot
pub snapshot: String,
/// Snapshot creation time (epoch)
pub backup_time: i64,
}

View File

@ -1,91 +0,0 @@
use anyhow::{bail, Error};
use proxmox::api::{
schema::{
Schema,
StringSchema,
ApiStringFormat,
parse_simple_value,
},
};
use crate::api2::types::{
PROXMOX_SAFE_ID_FORMAT,
CHANGER_NAME_SCHEMA,
};
pub const VAULT_NAME_SCHEMA: Schema = StringSchema::new("Vault name.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
#[derive(Debug, PartialEq, Clone)]
/// Media location
pub enum MediaLocation {
/// Ready for use (inside tape library)
Online(String),
/// Local available, but need to be mounted (insert into tape
/// drive)
Offline,
/// Media is inside a Vault
Vault(String),
}
proxmox::forward_deserialize_to_from_str!(MediaLocation);
proxmox::forward_serialize_to_display!(MediaLocation);
impl proxmox::api::schema::ApiType for MediaLocation {
const API_SCHEMA: Schema = StringSchema::new(
"Media location (e.g. 'offline', 'online-<changer_name>', 'vault-<vault_name>')")
.format(&ApiStringFormat::VerifyFn(|text| {
let location: MediaLocation = text.parse()?;
match location {
MediaLocation::Online(ref changer) => {
parse_simple_value(changer, &CHANGER_NAME_SCHEMA)?;
}
MediaLocation::Vault(ref vault) => {
parse_simple_value(vault, &VAULT_NAME_SCHEMA)?;
}
MediaLocation::Offline => { /* OK */}
}
Ok(())
}))
.schema();
}
impl std::fmt::Display for MediaLocation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MediaLocation::Offline => {
write!(f, "offline")
}
MediaLocation::Online(changer) => {
write!(f, "online-{}", changer)
}
MediaLocation::Vault(vault) => {
write!(f, "vault-{}", vault)
}
}
}
}
impl std::str::FromStr for MediaLocation {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "offline" {
return Ok(MediaLocation::Offline);
}
if let Some(changer) = s.strip_prefix("online-") {
return Ok(MediaLocation::Online(changer.to_string()));
}
if let Some(vault) = s.strip_prefix("vault-") {
return Ok(MediaLocation::Vault(vault.to_string()));
}
bail!("MediaLocation parse error");
}
}

View File

@ -1,161 +0,0 @@
//! Types for tape media pool API
//!
//! Note: Both MediaSetPolicy and RetentionPolicy are complex enums,
//! so we cannot use them directly for the API. Instead, we represent
//! them as String.
use std::str::FromStr;
use anyhow::Error;
use serde::{Deserialize, Serialize};
use proxmox::api::{
api,
schema::{Schema, StringSchema, ApiStringFormat, Updater},
};
use pbs_systemd::time::{parse_calendar_event, parse_time_span, CalendarEvent, TimeSpan};
use crate::api2::types::{
PROXMOX_SAFE_ID_FORMAT,
SINGLE_LINE_COMMENT_FORMAT,
SINGLE_LINE_COMMENT_SCHEMA,
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
};
pub const MEDIA_POOL_NAME_SCHEMA: Schema = StringSchema::new("Media pool name.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(2)
.max_length(32)
.schema();
pub const MEDIA_SET_NAMING_TEMPLATE_SCHEMA: Schema = StringSchema::new(
"Media set naming template (may contain strftime() time format specifications).")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.min_length(2)
.max_length(64)
.schema();
pub const MEDIA_SET_ALLOCATION_POLICY_FORMAT: ApiStringFormat =
ApiStringFormat::VerifyFn(|s| { MediaSetPolicy::from_str(s)?; Ok(()) });
pub const MEDIA_SET_ALLOCATION_POLICY_SCHEMA: Schema = StringSchema::new(
"Media set allocation policy ('continue', 'always', or a calendar event).")
.format(&MEDIA_SET_ALLOCATION_POLICY_FORMAT)
.schema();
/// Media set allocation policy
pub enum MediaSetPolicy {
/// Try to use the current media set
ContinueCurrent,
/// Each backup job creates a new media set
AlwaysCreate,
/// Create a new set when the specified CalendarEvent triggers
CreateAt(CalendarEvent),
}
impl std::str::FromStr for MediaSetPolicy {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "continue" {
return Ok(MediaSetPolicy::ContinueCurrent);
}
if s == "always" {
return Ok(MediaSetPolicy::AlwaysCreate);
}
let event = parse_calendar_event(s)?;
Ok(MediaSetPolicy::CreateAt(event))
}
}
pub const MEDIA_RETENTION_POLICY_FORMAT: ApiStringFormat =
ApiStringFormat::VerifyFn(|s| { RetentionPolicy::from_str(s)?; Ok(()) });
pub const MEDIA_RETENTION_POLICY_SCHEMA: Schema = StringSchema::new(
"Media retention policy ('overwrite', 'keep', or time span).")
.format(&MEDIA_RETENTION_POLICY_FORMAT)
.schema();
/// Media retention Policy
pub enum RetentionPolicy {
/// Always overwrite media
OverwriteAlways,
/// Protect data for the timespan specified
ProtectFor(TimeSpan),
/// Never overwrite data
KeepForever,
}
impl std::str::FromStr for RetentionPolicy {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "overwrite" {
return Ok(RetentionPolicy::OverwriteAlways);
}
if s == "keep" {
return Ok(RetentionPolicy::KeepForever);
}
let time_span = parse_time_span(s)?;
Ok(RetentionPolicy::ProtectFor(time_span))
}
}
#[api(
properties: {
name: {
schema: MEDIA_POOL_NAME_SCHEMA,
},
allocation: {
schema: MEDIA_SET_ALLOCATION_POLICY_SCHEMA,
optional: true,
},
retention: {
schema: MEDIA_RETENTION_POLICY_SCHEMA,
optional: true,
},
template: {
schema: MEDIA_SET_NAMING_TEMPLATE_SCHEMA,
optional: true,
},
encrypt: {
schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
optional: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
},
)]
#[derive(Serialize,Deserialize,Updater)]
/// Media pool configuration
pub struct MediaPoolConfig {
/// The pool name
#[updater(skip)]
pub name: String,
/// Media Set allocation policy
#[serde(skip_serializing_if="Option::is_none")]
pub allocation: Option<String>,
/// Media retention policy
#[serde(skip_serializing_if="Option::is_none")]
pub retention: Option<String>,
/// Media set naming template (default "%c")
///
/// The template is UTF8 text, and can include strftime time
/// format specifications.
#[serde(skip_serializing_if="Option::is_none")]
pub template: Option<String>,
/// Encryption key fingerprint
///
/// If set, encrypt all data using the specified key.
#[serde(skip_serializing_if="Option::is_none")]
pub encrypt: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
}

View File

@ -1,21 +0,0 @@
use ::serde::{Deserialize, Serialize};
use proxmox::api::api;
#[api()]
/// Media status
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Media Status
pub enum MediaStatus {
/// Media is ready to be written
Writable,
/// Media is full (contains data)
Full,
/// Media is marked as unknown, needs rescan
Unknown,
/// Media is marked as damaged
Damaged,
/// Media is marked as retired
Retired,
}

View File

@ -1,22 +0,0 @@
//! Types for tape backup API
mod device;
pub use device::*;
mod changer;
pub use changer::*;
mod drive;
pub use drive::*;
mod media_pool;
pub use media_pool::*;
mod media_status;
pub use media_status::*;
mod media_location;
pub use media_location::*;
mod media;
pub use media::*;

View File

@ -55,7 +55,7 @@ fn main() -> Result<(), Error> {
let text = match arg.as_ref() {
"apidata.js" => generate_api_tree(),
"datastore.cfg" => dump_section_config(&config::datastore::CONFIG),
"tape.cfg" => dump_section_config(&config::drive::CONFIG),
"tape.cfg" => dump_section_config(&pbs_config::drive::CONFIG),
"tape-job.cfg" => dump_section_config(&config::tape_job::CONFIG),
"user.cfg" => dump_section_config(&config::user::CONFIG),
"remote.cfg" => dump_section_config(&pbs_config::remote::CONFIG),

View File

@ -30,6 +30,23 @@ use proxmox::{
},
};
use pbs_api_types::{
LTO_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, LtoTapeDrive,
};
use pbs_config::drive::complete_drive_name;
use proxmox_backup::{
tape::{
complete_drive_path,
lto_tape_device_list,
drive::{
TapeDriver,
LtoTapeHandle,
open_lto_tape_device,
},
},
};
pub const FILE_MARK_COUNT_SCHEMA: Schema =
IntegerSchema::new("File mark count.")
.minimum(1)
@ -57,31 +74,10 @@ pub const DRIVE_OPTION_LIST_SCHEMA: Schema =
.min_length(1)
.schema();
use proxmox_backup::{
config::{
self,
drive::complete_drive_name,
},
api2::types::{
LTO_DRIVE_PATH_SCHEMA,
DRIVE_NAME_SCHEMA,
LtoTapeDrive,
},
tape::{
complete_drive_path,
lto_tape_device_list,
drive::{
TapeDriver,
LtoTapeHandle,
open_lto_tape_device,
},
},
};
fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
if let Some(name) = param["drive"].as_str() {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
return LtoTapeHandle::new(open_lto_tape_device(&drive.path)?);
@ -93,7 +89,7 @@ fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
}
if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
return LtoTapeHandle::new(open_lto_tape_device(&drive.path)?);
@ -104,7 +100,7 @@ fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
return LtoTapeHandle::new(open_lto_tape_device(&device)?);
}
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let mut drive_names = Vec::new();
for (name, (section_type, _)) in config.sections.iter() {

View File

@ -25,6 +25,8 @@ use proxmox::{
},
};
use pbs_config::drive::complete_changer_name;
use proxmox_backup::{
tools::sgutils2::{
scsi_inquiry,
@ -43,18 +45,12 @@ use proxmox_backup::{
sg_pt_changer,
},
},
config::{
self,
drive::{
complete_changer_name,
}
},
};
fn get_changer_handle(param: &Value) -> Result<File, Error> {
if let Some(name) = param["changer"].as_str() {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let changer_config: ScsiTapeChanger = config.lookup("changer", &name)?;
eprintln!("using device {}", changer_config.path);
return sg_pt_changer::open(&changer_config.path);
@ -66,7 +62,7 @@ fn get_changer_handle(param: &Value) -> Result<File, Error> {
}
if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
if let Some(changer) = drive.changer {
let changer_config: ScsiTapeChanger = config.lookup("changer", &changer)?;

View File

@ -21,6 +21,8 @@ use pbs_tools::format::{
render_bytes_human_readable,
};
use pbs_config::drive::complete_drive_name;
use proxmox_backup::{
api2::{
self,
@ -36,9 +38,7 @@ use proxmox_backup::{
},
},
config::{
self,
datastore::complete_datastore_name,
drive::complete_drive_name,
media_pool::complete_pool_name,
},
tape::{
@ -121,7 +121,7 @@ async fn format_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -154,7 +154,7 @@ async fn rewind(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -187,7 +187,7 @@ async fn eject_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -223,7 +223,7 @@ async fn load_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -253,7 +253,7 @@ async fn load_media(mut param: Value) -> Result<(), Error> {
/// Export media with specified label
async fn export_media(mut param: Value) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -283,7 +283,7 @@ async fn export_media(mut param: Value) -> Result<(), Error> {
/// Load media from the specified slot
async fn load_media_from_slot(mut param: Value) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -320,7 +320,7 @@ async fn unload_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -360,7 +360,7 @@ async fn label_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -398,7 +398,7 @@ async fn read_label(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -458,7 +458,7 @@ async fn inventory(
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
let do_read = read_labels.unwrap_or(false) || read_all_labels.unwrap_or(false);
@ -516,7 +516,7 @@ async fn barcode_label_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -543,7 +543,7 @@ async fn barcode_label_media(mut param: Value) -> Result<(), Error> {
/// Move to end of media (MTEOM, used to debug)
fn move_to_eom(mut param: Value) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -573,7 +573,7 @@ fn move_to_eom(mut param: Value) -> Result<(), Error> {
/// method is expected to fails when we reach EOT.
fn debug_scan(mut param: Value) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -655,7 +655,7 @@ async fn cartridge_memory(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -696,7 +696,7 @@ async fn volume_statistics(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -734,7 +734,7 @@ async fn status(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -794,7 +794,7 @@ async fn clean_drive(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;
@ -855,7 +855,7 @@ async fn backup(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
param["drive"] = extract_drive_name(&mut param, &config)?.into();
@ -910,7 +910,7 @@ async fn restore(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
param["drive"] = extract_drive_name(&mut param, &config)?.into();
@ -957,7 +957,7 @@ async fn catalog_media(mut param: Value) -> Result<(), Error> {
let output_format = extract_output_format(&mut param);
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive = extract_drive_name(&mut param, &config)?;

View File

@ -11,6 +11,11 @@ use proxmox::{
},
};
use pbs_config::drive::{
complete_drive_name,
complete_changer_name,
};
use proxmox_backup::{
api2::{
self,
@ -20,16 +25,7 @@ use proxmox_backup::{
},
tape::{
complete_changer_path,
drive::{
media_changer,
},
},
config::{
self,
drive::{
complete_drive_name,
complete_changer_name,
}
drive::media_changer,
},
};
@ -232,7 +228,7 @@ async fn get_status(
rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
param["name"] = lookup_changer_name(&param, &config)?.into();
@ -295,7 +291,7 @@ pub async fn transfer(
rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
param["name"] = lookup_changer_name(&param, &config)?.into();

View File

@ -10,21 +10,16 @@ use proxmox::{
},
};
use proxmox_backup::{
api2::{
self,
types::{
DRIVE_NAME_SCHEMA,
},
},
tape::complete_drive_path,
config::drive::{
complete_drive_name,
complete_changer_name,
complete_lto_drive_name,
},
use pbs_api_types::DRIVE_NAME_SCHEMA;
use pbs_config::drive::{
complete_drive_name,
complete_changer_name,
complete_lto_drive_name,
};
use proxmox_backup::{api2, tape::complete_drive_path};
pub fn drive_commands() -> CommandLineInterface {
let cmd_def = CliCommandMap::new()

View File

@ -16,7 +16,6 @@ use pbs_datastore::Kdf;
use pbs_datastore::paperkey::{PaperkeyFormat, generate_paper_key};
use proxmox_backup::{
config,
api2::{
self,
types::{
@ -208,7 +207,7 @@ async fn restore_key(
rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
param["drive"] = crate::extract_drive_name(&mut param, &config)?.into();
if !tty::stdin_isatty() {

View File

@ -10,18 +10,14 @@ use proxmox::{
},
};
use pbs_api_types::{
MEDIA_POOL_NAME_SCHEMA, CHANGER_NAME_SCHEMA, MediaStatus, MediaListEntry,
MediaContentListFilter,
};
use pbs_config::drive::complete_changer_name;
use proxmox_backup::{
api2::{
self,
types::{
MEDIA_POOL_NAME_SCHEMA,
CHANGER_NAME_SCHEMA,
MediaStatus,
MediaListEntry,
},
tape::media::MediaContentListFilter,
},
config::drive::complete_changer_name,
api2,
tape::{
complete_media_label_text,
complete_media_uuid,

View File

@ -18,22 +18,18 @@ use proxmox::{
tools::Uuid,
};
use pbs_api_types::Fingerprint;
use pbs_api_types::{
Fingerprint, LTO_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
MEDIA_SET_UUID_SCHEMA, LtoTapeDrive,
};
use proxmox_backup::{
config,
api2::types::{
LTO_DRIVE_PATH_SCHEMA,
DRIVE_NAME_SCHEMA,
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
MEDIA_SET_UUID_SCHEMA,
LtoTapeDrive,
},
tape::{
drive::{
TapeDriver,
LtoTapeHandle,
open_lto_tape_device,
open_lto_tape_drive,
check_tape_is_lto_tape_device,
},
},
@ -42,10 +38,10 @@ use proxmox_backup::{
fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
let handle = if let Some(name) = param["drive"].as_str() {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
drive.open()?
open_lto_tape_drive(&drive)?
} else if let Some(device) = param["device"].as_str() {
eprintln!("using device {}", device);
LtoTapeHandle::new(open_lto_tape_device(&device)?)?
@ -56,12 +52,12 @@ fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
check_tape_is_lto_tape_device(&file)?;
LtoTapeHandle::new(file)?
} else if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
drive.open()?
open_lto_tape_drive(&drive)?
} else {
let (config, _digest) = config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let mut drive_names = Vec::new();
for (name, (section_type, _)) in config.sections.iter() {
@ -73,7 +69,7 @@ fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
let name = drive_names[0];
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
drive.open()?
open_lto_tape_drive(&drive)?
} else {
bail!("no drive/device specified");
}

View File

@ -1,150 +0,0 @@
//! Tape drive/changer configuration
//!
//! This configuration module is based on [`SectionConfig`], and
//! provides a type safe interface to store [`LtoTapeDrive`],
//! [`VirtualTapeDrive`] and [`ScsiTapeChanger`] configurations.
//!
//! Drive type [`VirtualTapeDrive`] is only useful for debugging.
//!
//! [LtoTapeDrive]: crate::api2::types::LtoTapeDrive
//! [VirtualTapeDrive]: crate::api2::types::VirtualTapeDrive
//! [ScsiTapeChanger]: crate::api2::types::ScsiTapeChanger
//! [SectionConfig]: proxmox::api::section_config::SectionConfig
use std::collections::HashMap;
use anyhow::{bail, Error};
use lazy_static::lazy_static;
use proxmox::{
api::{
schema::*,
section_config::{
SectionConfig,
SectionConfigData,
SectionConfigPlugin,
},
},
};
use pbs_config::{open_backup_lockfile, BackupLockGuard};
use crate::{
api2::types::{
DRIVE_NAME_SCHEMA,
VirtualTapeDrive,
LtoTapeDrive,
ScsiTapeChanger,
},
};
lazy_static! {
/// Static [`SectionConfig`] to access parser/writer functions.
pub static ref CONFIG: SectionConfig = init();
}
fn init() -> SectionConfig {
let mut config = SectionConfig::new(&DRIVE_NAME_SCHEMA);
let obj_schema = match VirtualTapeDrive::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("virtual".to_string(), Some("name".to_string()), obj_schema);
config.register_plugin(plugin);
let obj_schema = match LtoTapeDrive::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("lto".to_string(), Some("name".to_string()), obj_schema);
config.register_plugin(plugin);
let obj_schema = match ScsiTapeChanger::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("changer".to_string(), Some("name".to_string()), obj_schema);
config.register_plugin(plugin);
config
}
/// Configuration file name
pub const DRIVE_CFG_FILENAME: &str = "/etc/proxmox-backup/tape.cfg";
/// Lock file name (used to prevent concurrent access)
pub const DRIVE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.tape.lck";
/// Get exclusive lock
pub fn lock() -> Result<BackupLockGuard, Error> {
open_backup_lockfile(DRIVE_CFG_LOCKFILE, None, true)
}
/// Read and parse the configuration file
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
let content = proxmox::tools::fs::file_read_optional_string(DRIVE_CFG_FILENAME)?
.unwrap_or_else(|| "".to_string());
let digest = openssl::sha::sha256(content.as_bytes());
let data = CONFIG.parse(DRIVE_CFG_FILENAME, &content)?;
Ok((data, digest))
}
/// Save the configuration file
pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
let raw = CONFIG.write(DRIVE_CFG_FILENAME, &config)?;
pbs_config::replace_backup_config(DRIVE_CFG_FILENAME, raw.as_bytes())
}
/// Check if the specified drive name exists in the config.
pub fn check_drive_exists(config: &SectionConfigData, drive: &str) -> Result<(), Error> {
match config.sections.get(drive) {
Some((section_type, _)) => {
if !(section_type == "lto" || section_type == "virtual") {
bail!("Entry '{}' exists, but is not a tape drive", drive);
}
}
None => bail!("Drive '{}' does not exist", drive),
}
Ok(())
}
// shell completion helper
/// List all drive names
pub fn complete_drive_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.sections.iter()
.map(|(id, _)| id.to_string())
.collect(),
Err(_) => return vec![],
}
}
/// List Lto tape drives
pub fn complete_lto_drive_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.sections.iter()
.filter(|(_id, (section_type, _))| {
section_type == "lto"
})
.map(|(id, _)| id.to_string())
.collect(),
Err(_) => return vec![],
}
}
/// List Scsi tape changer names
pub fn complete_changer_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.sections.iter()
.filter(|(_id, (section_type, _))| {
section_type == "changer"
})
.map(|(id, _)| id.to_string())
.collect(),
Err(_) => return vec![],
}
}

View File

@ -25,7 +25,6 @@ pub mod tfa;
pub mod token_shadow;
pub mod user;
pub mod verify;
pub mod drive;
pub mod media_pool;
pub mod tape_encryption_keys;
pub mod tape_job;

View File

@ -524,7 +524,7 @@ pub struct MtxMediaChanger {
impl MtxMediaChanger {
pub fn with_drive_config(drive_config: &LtoTapeDrive) -> Result<Self, Error> {
let (config, _digest) = crate::config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let changer_config: ScsiTapeChanger = match drive_config.changer {
Some(ref changer) => config.lookup("changer", changer)?,
None => bail!("drive '{}' has no associated changer", drive_config.name),

View File

@ -125,7 +125,7 @@ pub fn mtx_status_to_online_set(status: &MtxStatus, inventory: &Inventory) -> Ha
/// For a single 'changer', or else simply ask all changer devices.
pub fn update_online_status(state_path: &Path, changer: Option<&str>) -> Result<OnlineStatusMap, Error> {
let (config, _digest) = crate::config::drive::config()?;
let (config, _digest) = pbs_config::drive::config()?;
let mut inventory = Inventory::load(state_path)?;

View File

@ -54,37 +54,34 @@ use crate::{
},
};
impl LtoTapeDrive {
/// Open a tape device
///
/// This does additional checks:
///
/// - check if it is a non-rewinding tape device
/// - check if drive is ready (tape loaded)
/// - check block size
/// - for autoloader only, try to reload ejected tapes
pub fn open_lto_tape_drive(config: &LtoTapeDrive) -> Result<LtoTapeHandle, Error> {
/// Open a tape device
///
/// This does additional checks:
///
/// - check if it is a non-rewinding tape device
/// - check if drive is ready (tape loaded)
/// - check block size
/// - for autoloader only, try to reload ejected tapes
pub fn open(&self) -> Result<LtoTapeHandle, Error> {
proxmox::try_block!({
let file = open_lto_tape_device(&config.path)?;
proxmox::try_block!({
let file = open_lto_tape_device(&self.path)?;
let mut handle = LtoTapeHandle::new(file)?;
let mut handle = LtoTapeHandle::new(file)?;
if !handle.sg_tape.test_unit_ready().is_ok() {
// for autoloader only, try to reload ejected tapes
if self.changer.is_some() {
let _ = handle.sg_tape.load(); // just try, ignore error
}
if !handle.sg_tape.test_unit_ready().is_ok() {
// for autoloader only, try to reload ejected tapes
if config.changer.is_some() {
let _ = handle.sg_tape.load(); // just try, ignore error
}
}
handle.sg_tape.wait_until_ready()?;
handle.sg_tape.wait_until_ready()?;
handle.set_default_options()?;
handle.set_default_options()?;
Ok(handle)
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", self.name, self.path, err))
}
Ok(handle)
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", config.name, config.path, err))
}
/// Lto Tape device handle

View File

@ -32,12 +32,9 @@ use pbs_api_types::Fingerprint;
use pbs_datastore::key_derivation::KeyConfig;
use pbs_datastore::task::TaskState;
use pbs_datastore::task_log;
use pbs_api_types::{VirtualTapeDrive, LtoTapeDrive};
use crate::{
api2::types::{
VirtualTapeDrive,
LtoTapeDrive,
},
server::{
send_load_media_email,
WorkerTask,
@ -47,7 +44,10 @@ use crate::{
TapeRead,
BlockReadError,
MediaId,
drive::lto::TapeAlertFlags,
drive::{
virtual_tape::open_virtual_tape_drive,
lto::TapeAlertFlags,
},
file_formats::{
PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0,
PROXMOX_BACKUP_MEDIA_SET_LABEL_MAGIC_1_0,
@ -305,12 +305,12 @@ pub fn open_drive(
match section_type_name.as_ref() {
"virtual" => {
let tape = VirtualTapeDrive::deserialize(config)?;
let handle = tape.open()?;
let handle = open_virtual_tape_drive(&tape)?;
Ok(Box::new(handle))
}
"lto" => {
let tape = LtoTapeDrive::deserialize(config)?;
let handle = tape.open()?;
let handle = open_lto_tape_drive(&tape)?;
Ok(Box::new(handle))
}
_ => bail!("unknown drive type '{}' - internal error"),
@ -395,7 +395,7 @@ pub fn request_and_load_media(
tape.load_media(&label_text)?;
let mut handle: Box<dyn TapeDriver> = Box::new(tape.open()?);
let mut handle: Box<dyn TapeDriver> = Box::new(open_virtual_tape_drive(&tape)?);
let media_id = check_label(handle.as_mut(), &label.uuid)?;
@ -413,7 +413,7 @@ pub fn request_and_load_media(
let mut changer = MtxMediaChanger::with_drive_config(&drive_config)?;
changer.load_media(&label_text)?;
let mut handle: Box<dyn TapeDriver> = Box::new(drive_config.open()?);
let mut handle: Box<dyn TapeDriver> = Box::new(open_lto_tape_drive(&drive_config)?);
let media_id = check_label(handle.as_mut(), &label.uuid)?;
@ -463,7 +463,7 @@ pub fn request_and_load_media(
);
}
let mut handle = match drive_config.open() {
let mut handle = match open_lto_tape_drive(&drive_config) {
Ok(handle) => handle,
Err(err) => {
update_and_log_request_error(

View File

@ -42,26 +42,23 @@ use crate::{
},
};
impl VirtualTapeDrive {
/// This needs to lock the drive
pub fn open_virtual_tape_drive(config: &VirtualTapeDrive) -> Result<VirtualTapeHandle, Error> {
proxmox::try_block!({
let mut lock_path = std::path::PathBuf::from(&config.path);
lock_path.push(".drive.lck");
/// This needs to lock the drive
pub fn open(&self) -> Result<VirtualTapeHandle, Error> {
proxmox::try_block!({
let mut lock_path = std::path::PathBuf::from(&self.path);
lock_path.push(".drive.lck");
let options = CreateOptions::new();
let timeout = std::time::Duration::new(10, 0);
let lock = proxmox::tools::fs::open_file_locked(&lock_path, timeout, true, options)?;
let options = CreateOptions::new();
let timeout = std::time::Duration::new(10, 0);
let lock = proxmox::tools::fs::open_file_locked(&lock_path, timeout, true, options)?;
Ok(VirtualTapeHandle {
_lock: lock,
drive_name: self.name.clone(),
max_size: self.max_size.unwrap_or(64*1024*1024),
path: std::path::PathBuf::from(&self.path),
})
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", self.name, self.path, err))
}
Ok(VirtualTapeHandle {
_lock: lock,
drive_name: config.name.clone(),
max_size: config.max_size.unwrap_or(64*1024*1024),
path: std::path::PathBuf::from(&config.path),
})
}).map_err(|err: Error| format_err!("open drive '{}' ({}) failed - {}", config.name, config.path, err))
}
#[derive(Serialize,Deserialize)]
@ -583,42 +580,42 @@ impl MediaChange for VirtualTapeDrive {
}
fn status(&mut self) -> Result<MtxStatus, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.status()
}
fn transfer_media(&mut self, from: u64, to: u64) -> Result<MtxStatus, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.transfer_media(from, to)
}
fn export_media(&mut self, label_text: &str) -> Result<Option<u64>, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.export_media(label_text)
}
fn load_media_from_slot(&mut self, slot: u64) -> Result<MtxStatus, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.load_media_from_slot(slot)
}
fn load_media(&mut self, label_text: &str) -> Result<MtxStatus, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.load_media(label_text)
}
fn unload_media(&mut self, target_slot: Option<u64>) -> Result<MtxStatus, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.unload_media(target_slot)
}
fn online_media_label_texts(&mut self) -> Result<Vec<String>, Error> {
let handle = self.open()?;
let handle = open_virtual_tape_drive(self)?;
handle.online_media_label_texts()
}
fn clean_drive(&mut self) -> Result<MtxStatus, Error> {
let mut handle = self.open()?;
let mut handle = open_virtual_tape_drive(self)?;
handle.clean_drive()
}
}

View File

@ -133,7 +133,7 @@ impl PoolWriter {
None => return Ok(()), // no media loaded
};
let (drive_config, _digest) = crate::config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
if let Some((mut changer, _)) = media_changer(&drive_config, &self.drive_name)? {
worker.log("eject media");
@ -153,7 +153,7 @@ impl PoolWriter {
pub fn export_media_set(&mut self, worker: &WorkerTask) -> Result<(), Error> {
let mut status = self.status.take();
let (drive_config, _digest) = crate::config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
if let Some((mut changer, _)) = media_changer(&drive_config, &self.drive_name)? {
@ -226,7 +226,7 @@ impl PoolWriter {
}
}
let (drive_config, _digest) = crate::config::drive::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
let (mut drive, old_media_id) =
request_and_load_media(worker, &drive_config, &self.drive_name, media.label(), &self.notify_email)?;