tape: implement LTO userspace driver

This commit is contained in:
Dietmar Maurer
2021-03-30 17:07:59 +02:00
parent 1336ae8249
commit a79082a0dd
17 changed files with 1078 additions and 531 deletions

View File

@ -1,18 +1,18 @@
/// Control magnetic tape drive operation
///
/// This is a Rust implementation, meant to replace the 'mt' command
/// line tool.
/// This is a Rust implementation, using the Proxmox userspace tape
/// driver. This is meant as replacement fot the 'mt' command line
/// tool.
///
/// Features:
///
/// - written in Rust
/// - use Proxmox userspace driver (using SG_IO)
/// - optional json output format
/// - support tape alert flags
/// - support volume statistics
/// - read cartridge memory
use std::collections::HashMap;
use anyhow::{bail, Error};
use serde_json::Value;
@ -43,7 +43,7 @@ pub const RECORD_COUNT_SCHEMA: Schema =
.schema();
pub const DRIVE_OPTION_SCHEMA: Schema = StringSchema::new(
"Linux Tape Driver Option, either numeric value or option name.")
"Lto Tape Driver Option, either numeric value or option name.")
.schema();
pub const DRIVE_OPTION_LIST_SCHEMA: Schema =
@ -57,103 +57,60 @@ use proxmox_backup::{
drive::complete_drive_name,
},
api2::types::{
LINUX_DRIVE_PATH_SCHEMA,
LTO_DRIVE_PATH_SCHEMA,
DRIVE_NAME_SCHEMA,
LinuxTapeDrive,
LtoTapeDrive,
},
tape::{
complete_drive_path,
linux_tape_device_list,
lto_tape_device_list,
drive::{
linux_mtio::{MTCmd, SetDrvBufferOptions},
TapeDriver,
LinuxTapeHandle,
open_linux_tape_device,
LtoTapeHandle,
open_lto_tape_device,
},
},
};
lazy_static::lazy_static!{
static ref DRIVE_OPTIONS: HashMap<String, SetDrvBufferOptions> = {
let mut map = HashMap::new();
for i in 0..31 {
let bit: i32 = 1 << i;
let flag = SetDrvBufferOptions::from_bits_truncate(bit);
if flag.bits() == 0 { continue; }
let name = format!("{:?}", flag)
.to_lowercase()
.replace("_", "-");
map.insert(name, flag);
}
map
};
}
fn parse_drive_options(options: Vec<String>) -> Result<SetDrvBufferOptions, Error> {
let mut value = SetDrvBufferOptions::empty();
for option in options.iter() {
if let Ok::<i32,_>(v) = option.parse() {
value |= SetDrvBufferOptions::from_bits_truncate(v);
} else if let Some(v) = DRIVE_OPTIONS.get(option) {
value |= *v;
} else {
let option = option.to_lowercase().replace("_", "-");
if let Some(v) = DRIVE_OPTIONS.get(&option) {
value |= *v;
} else {
bail!("unknown drive option {}", option);
}
}
}
Ok(value)
}
fn get_tape_handle(param: &Value) -> Result<LinuxTapeHandle, Error> {
fn get_tape_handle(param: &Value) -> Result<LtoTapeHandle, Error> {
if let Some(name) = param["drive"].as_str() {
let (config, _digest) = config::drive::config()?;
let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
return Ok(LinuxTapeHandle::new(open_linux_tape_device(&drive.path)?))
return LtoTapeHandle::new(open_lto_tape_device(&drive.path)?);
}
if let Some(device) = param["device"].as_str() {
eprintln!("using device {}", device);
return Ok(LinuxTapeHandle::new(open_linux_tape_device(&device)?))
return LtoTapeHandle::new(open_lto_tape_device(&device)?);
}
if let Ok(name) = std::env::var("PROXMOX_TAPE_DRIVE") {
let (config, _digest) = config::drive::config()?;
let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
return Ok(LinuxTapeHandle::new(open_linux_tape_device(&drive.path)?))
return LtoTapeHandle::new(open_lto_tape_device(&drive.path)?);
}
if let Ok(device) = std::env::var("TAPE") {
eprintln!("using device {}", device);
return Ok(LinuxTapeHandle::new(open_linux_tape_device(&device)?))
return LtoTapeHandle::new(open_lto_tape_device(&device)?);
}
let (config, _digest) = config::drive::config()?;
let mut drive_names = Vec::new();
for (name, (section_type, _)) in config.sections.iter() {
if section_type != "linux" { continue; }
if section_type != "lto" { continue; }
drive_names.push(name);
}
if drive_names.len() == 1 {
let name = drive_names[0];
let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
return Ok(LinuxTapeHandle::new(open_linux_tape_device(&drive.path)?))
return LtoTapeHandle::new(open_lto_tape_device(&drive.path)?);
}
bail!("no drive/device specified");
@ -167,7 +124,7 @@ fn get_tape_handle(param: &Value) -> Result<LinuxTapeHandle, Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -200,7 +157,7 @@ fn asf(count: usize, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -230,7 +187,7 @@ fn bsf(count: usize, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -243,11 +200,12 @@ fn bsf(count: usize, param: Value) -> Result<(), Error> {
///
/// This leaves the tape positioned at the first block of the file
/// that is count - 1 files before the current file.
fn bsfm(count: i32, param: Value) -> Result<(), Error> {
fn bsfm(count: usize, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTBSFM, count, "bsfm")?;
handle.backward_space_count_files(count)?;
handle.forward_space_count_files(1)?;
Ok(())
}
@ -261,7 +219,7 @@ fn bsfm(count: i32, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -275,7 +233,9 @@ fn bsr(count: i32, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTBSR, count, "backward space records")?;
unimplemented!();
// fixme: handle.mtop(MTCmd::MTBSR, count, "backward space records")?;
Ok(())
}
@ -289,7 +249,7 @@ fn bsr(count: i32, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
"output-format": {
@ -340,7 +300,7 @@ fn cartridge_memory(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
"output-format": {
@ -389,7 +349,7 @@ fn tape_alert_flags(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
},
@ -413,7 +373,7 @@ fn eject(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
},
@ -437,7 +397,7 @@ fn eod(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
fast: {
@ -466,7 +426,7 @@ fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -495,7 +455,7 @@ fn fsf(count: usize, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -508,11 +468,12 @@ fn fsf(count: usize, param: Value) -> Result<(), Error> {
///
/// This leaves the tape positioned at the last block of the file that
/// is count - 1 files past the current file.
fn fsfm(count: i32, param: Value) -> Result<(), Error> {
fn fsfm(count: usize, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTFSFM, count, "fsfm")?;
handle.forward_space_count_files(count)?;
handle.backward_space_count_files(1)?;
Ok(())
}
@ -526,7 +487,7 @@ fn fsfm(count: i32, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -540,7 +501,8 @@ fn fsr(count: i32, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTFSR, count, "forward space records")?;
unimplemented!();
// fixme: handle.mtop(MTCmd::MTFSR, count, "forward space records")?;
Ok(())
}
@ -554,7 +516,7 @@ fn fsr(count: i32, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
},
@ -564,7 +526,7 @@ fn fsr(count: i32, param: Value) -> Result<(), Error> {
fn load(param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtload()?;
handle.load()?;
Ok(())
}
@ -578,7 +540,7 @@ fn load(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
},
@ -589,7 +551,8 @@ fn lock(param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTLOCK, 1, "lock tape drive door")?;
unimplemented!();
// fixme: handle.mtop(MTCmd::MTLOCK, 1, "lock tape drive door")?;
Ok(())
}
@ -603,7 +566,7 @@ fn lock(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
},
@ -634,7 +597,7 @@ fn scan(param: Value) -> Result<(), Error> {
let output_format = get_output_format(&param);
let list = linux_tape_device_list();
let list = lto_tape_device_list();
if output_format == "json-pretty" {
println!("{}", serde_json::to_string_pretty(&list)?);
@ -657,7 +620,6 @@ fn scan(param: Value) -> Result<(), Error> {
Ok(())
}
#[api(
input: {
properties: {
@ -666,36 +628,7 @@ fn scan(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
size: {
description: "Block size in bytes.",
minimum: 0,
},
},
},
)]
/// Set the block size of the drive
fn setblk(size: i32, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTSETBLK, size, "set block size")?;
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
"output-format": {
@ -737,122 +670,6 @@ fn status(param: Value) -> Result<(), Error> {
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
options: {
schema: DRIVE_OPTION_LIST_SCHEMA,
optional: true,
},
defaults: {
description: "Set default options (buffer-writes async-writes read-ahead can-bsr).",
type: bool,
optional: true,
},
},
},
)]
/// Set device driver options (root only)
fn st_options(
options: Option<Vec<String>>,
defaults: Option<bool>,
param: Value) -> Result<(), Error> {
let handle = get_tape_handle(&param)?;
let options = match defaults {
Some(true) => {
if options.is_some() {
bail!("option --defaults conflicts with specified options");
}
let mut list = Vec::new();
list.push(String::from("buffer-writes"));
list.push(String::from("async-writes"));
list.push(String::from("read-ahead"));
list.push(String::from("can-bsr"));
list
}
Some(false) | None => {
options.unwrap_or_else(|| Vec::new())
}
};
let value = parse_drive_options(options)?;
handle.set_drive_buffer_options(value)?;
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
options: {
schema: DRIVE_OPTION_LIST_SCHEMA,
},
},
},
)]
/// Set selected device driver options bits (root only)
fn st_set_options(options: Vec<String>, param: Value) -> Result<(), Error> {
let handle = get_tape_handle(&param)?;
let value = parse_drive_options(options)?;
handle.drive_buffer_set_options(value)?;
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
options: {
schema: DRIVE_OPTION_LIST_SCHEMA,
},
},
},
)]
/// Clear selected device driver options bits (root only)
fn st_clear_options(options: Vec<String>, param: Value) -> Result<(), Error> {
let handle = get_tape_handle(&param)?;
let value = parse_drive_options(options)?;
handle.drive_buffer_clear_options(value)?;
Ok(())
}
#[api(
input: {
properties: {
@ -861,7 +678,7 @@ fn st_clear_options(options: Vec<String>, param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
},
@ -872,7 +689,8 @@ fn unlock(param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTUNLOCK, 1, "unlock tape drive door")?;
unimplemented!();
//handle.mtop(MTCmd::MTUNLOCK, 1, "unlock tape drive door")?;
Ok(())
}
@ -886,7 +704,7 @@ fn unlock(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
"output-format": {
@ -935,7 +753,7 @@ fn volume_statistics(param: Value) -> Result<(), Error> {
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
count: {
@ -946,10 +764,13 @@ fn volume_statistics(param: Value) -> Result<(), Error> {
},
)]
/// Write count (default 1) EOF marks at current position.
fn weof(count: Option<i32>, param: Value) -> Result<(), Error> {
fn weof(count: Option<usize>, param: Value) -> Result<(), Error> {
let count = count.unwrap_or(1);
let mut handle = get_tape_handle(&param)?;
handle.mtop(MTCmd::MTWEOF, count.unwrap_or(1), "write EOF mark")?;
handle.write_filemarks(count)?;
Ok(())
}
@ -967,7 +788,6 @@ fn main() -> Result<(), Error> {
CliCommand::new(method)
.completion_cb("drive", complete_drive_name)
.completion_cb("device", complete_drive_path)
.completion_cb("options", complete_option_name)
};
let cmd_def = CliCommandMap::new()
@ -987,11 +807,7 @@ fn main() -> Result<(), Error> {
.insert("lock", std_cmd(&API_METHOD_LOCK))
.insert("rewind", std_cmd(&API_METHOD_REWIND))
.insert("scan", CliCommand::new(&API_METHOD_SCAN))
.insert("setblk", CliCommand::new(&API_METHOD_SETBLK).arg_param(&["size"]))
.insert("status", std_cmd(&API_METHOD_STATUS))
.insert("stoptions", std_cmd(&API_METHOD_ST_OPTIONS).arg_param(&["options"]))
.insert("stsetoptions", std_cmd(&API_METHOD_ST_SET_OPTIONS).arg_param(&["options"]))
.insert("stclearoptions", std_cmd(&API_METHOD_ST_CLEAR_OPTIONS).arg_param(&["options"]))
.insert("tape-alert-flags", std_cmd(&API_METHOD_TAPE_ALERT_FLAGS))
.insert("unlock", std_cmd(&API_METHOD_UNLOCK))
.insert("volume-statistics", std_cmd(&API_METHOD_VOLUME_STATISTICS))
@ -1005,11 +821,3 @@ fn main() -> Result<(), Error> {
Ok(())
}
// Completion helpers
pub fn complete_option_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
DRIVE_OPTIONS
.keys()
.map(String::from)
.collect()
}

View File

@ -33,7 +33,7 @@ use proxmox_backup::{
SCSI_CHANGER_PATH_SCHEMA,
CHANGER_NAME_SCHEMA,
ScsiTapeChanger,
LinuxTapeDrive,
LtoTapeDrive,
},
tape::{
linux_tape_changer_list,
@ -67,7 +67,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 drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
if let Some(changer) = drive.changer {
let changer_config: ScsiTapeChanger = config.lookup("changer", &changer)?;
eprintln!("using device {}", changer_config.path);

View File

@ -21,7 +21,7 @@ use proxmox_backup::{
config::drive::{
complete_drive_name,
complete_changer_name,
complete_linux_drive_name,
complete_lto_drive_name,
},
};
@ -33,13 +33,13 @@ pub fn drive_commands() -> CommandLineInterface {
.insert("config",
CliCommand::new(&API_METHOD_GET_CONFIG)
.arg_param(&["name"])
.completion_cb("name", complete_linux_drive_name)
.completion_cb("name", complete_lto_drive_name)
)
.insert(
"remove",
CliCommand::new(&api2::config::drive::API_METHOD_DELETE_DRIVE)
.arg_param(&["name"])
.completion_cb("name", complete_linux_drive_name)
.completion_cb("name", complete_lto_drive_name)
)
.insert(
"create",
@ -53,7 +53,7 @@ pub fn drive_commands() -> CommandLineInterface {
"update",
CliCommand::new(&api2::config::drive::API_METHOD_UPDATE_DRIVE)
.arg_param(&["name"])
.completion_cb("name", complete_linux_drive_name)
.completion_cb("name", complete_lto_drive_name)
.completion_cb("path", complete_drive_path)
.completion_cb("changer", complete_changer_name)
)

View File

@ -1,7 +1,5 @@
/// Tape command implemented using scsi-generic raw commands
///
/// SCSI-generic command needs root privileges, so this binary need
/// to be setuid root.
/// Helper to run tape commands as root. Currently only required
/// to read and set the encryption key.
///
/// This command can use STDIN as tape device handle.
@ -24,41 +22,41 @@ use proxmox_backup::{
config,
backup::Fingerprint,
api2::types::{
LINUX_DRIVE_PATH_SCHEMA,
LTO_DRIVE_PATH_SCHEMA,
DRIVE_NAME_SCHEMA,
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
MEDIA_SET_UUID_SCHEMA,
LinuxTapeDrive,
LtoTapeDrive,
},
tape::{
drive::{
TapeDriver,
LinuxTapeHandle,
open_linux_tape_device,
check_tape_is_linux_tape_device,
LtoTapeHandle,
open_lto_tape_device,
check_tape_is_lto_tape_device,
},
},
};
fn get_tape_handle(param: &Value) -> Result<LinuxTapeHandle, Error> {
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 drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
drive.open()?
} else if let Some(device) = param["device"].as_str() {
eprintln!("using device {}", device);
LinuxTapeHandle::new(open_linux_tape_device(&device)?)
LtoTapeHandle::new(open_lto_tape_device(&device)?)?
} else if let Some(true) = param["stdin"].as_bool() {
eprintln!("using stdin");
let fd = std::io::stdin().as_raw_fd();
let file = unsafe { File::from_raw_fd(fd) };
check_tape_is_linux_tape_device(&file)?;
LinuxTapeHandle::new(file)
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 drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
drive.open()?
} else {
@ -66,13 +64,13 @@ fn get_tape_handle(param: &Value) -> Result<LinuxTapeHandle, Error> {
let mut drive_names = Vec::new();
for (name, (section_type, _)) in config.sections.iter() {
if section_type != "linux" { continue; }
if section_type != "lto" { continue; }
drive_names.push(name);
}
if drive_names.len() == 1 {
let name = drive_names[0];
let drive: LinuxTapeDrive = config.lookup("linux", &name)?;
let drive: LtoTapeDrive = config.lookup("lto", &name)?;
eprintln!("using device {}", drive.path);
drive.open()?
} else {
@ -83,111 +81,6 @@ fn get_tape_handle(param: &Value) -> Result<LinuxTapeHandle, Error> {
Ok(handle)
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
stdin: {
description: "Use standard input as device handle.",
type: bool,
optional: true,
},
},
},
)]
/// Tape/Media Status
fn status(
param: Value,
) -> Result<(), Error> {
let result = proxmox::try_block!({
let mut handle = get_tape_handle(&param)?;
handle.get_drive_and_media_status()
}).map_err(|err: Error| err.to_string());
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
stdin: {
description: "Use standard input as device handle.",
type: bool,
optional: true,
},
},
},
)]
/// Read Cartridge Memory (Medium auxiliary memory attributes)
fn cartridge_memory(
param: Value,
) -> Result<(), Error> {
let result = proxmox::try_block!({
let mut handle = get_tape_handle(&param)?;
handle.cartridge_memory()
}).map_err(|err| err.to_string());
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
stdin: {
description: "Use standard input as device handle.",
type: bool,
optional: true,
},
},
},
)]
/// Read Tape Alert Flags
fn tape_alert_flags(
param: Value,
) -> Result<(), Error> {
let result = proxmox::try_block!({
let mut handle = get_tape_handle(&param)?;
let flags = handle.tape_alert_flags()?;
Ok(flags.bits())
}).map_err(|err: Error| err.to_string());
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
#[api(
input: {
properties: {
@ -204,7 +97,7 @@ fn tape_alert_flags(
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
stdin: {
@ -245,40 +138,6 @@ fn set_encryption(
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
stdin: {
description: "Use standard input as device handle.",
type: bool,
optional: true,
},
},
},
)]
/// Read volume statistics
fn volume_statistics(
param: Value,
) -> Result<(), Error> {
let result = proxmox::try_block!({
let mut handle = get_tape_handle(&param)?;
handle.volume_statistics()
}).map_err(|err: Error| err.to_string());
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
fn main() -> Result<(), Error> {
// check if we are user root or backup
@ -300,22 +159,6 @@ fn main() -> Result<(), Error> {
}
let cmd_def = CliCommandMap::new()
.insert(
"status",
CliCommand::new(&API_METHOD_STATUS)
)
.insert(
"cartridge-memory",
CliCommand::new(&API_METHOD_CARTRIDGE_MEMORY)
)
.insert(
"tape-alert-flags",
CliCommand::new(&API_METHOD_TAPE_ALERT_FLAGS)
)
.insert(
"volume-statistics",
CliCommand::new(&API_METHOD_VOLUME_STATISTICS)
)
.insert(
"encryption",
CliCommand::new(&API_METHOD_SET_ENCRYPTION)