tape: add drive status api
This commit is contained in:
		@ -1,7 +1,7 @@
 | 
			
		||||
use std::path::Path;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
use anyhow::{bail, Error};
 | 
			
		||||
use anyhow::{bail, format_err, Error};
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
 | 
			
		||||
use proxmox::{
 | 
			
		||||
@ -35,6 +35,7 @@ use crate::{
 | 
			
		||||
        MediaIdFlat,
 | 
			
		||||
        LabelUuidMap,
 | 
			
		||||
        MamAttribute,
 | 
			
		||||
        LinuxDriveStatusFlat,
 | 
			
		||||
    },
 | 
			
		||||
    server::WorkerTask,
 | 
			
		||||
    tape::{
 | 
			
		||||
@ -796,6 +797,33 @@ pub fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
 | 
			
		||||
    read_mam_attributes(&drive_config.path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[api(
 | 
			
		||||
    input: {
 | 
			
		||||
        properties: {
 | 
			
		||||
            drive: {
 | 
			
		||||
                schema: DRIVE_NAME_SCHEMA,
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    returns: {
 | 
			
		||||
        type: LinuxDriveStatusFlat,
 | 
			
		||||
    },
 | 
			
		||||
)]
 | 
			
		||||
/// Get drive status
 | 
			
		||||
pub fn status(drive: String) -> Result<LinuxDriveStatusFlat, Error> {
 | 
			
		||||
 | 
			
		||||
    let (config, _digest) = config::drive::config()?;
 | 
			
		||||
 | 
			
		||||
    let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
 | 
			
		||||
 | 
			
		||||
    let handle = drive_config.open()
 | 
			
		||||
        .map_err(|err| format_err!("open drive '{}' ({}) failed - {}", drive, drive_config.path, err))?;
 | 
			
		||||
 | 
			
		||||
    let drive_status = handle.get_drive_status()?;
 | 
			
		||||
 | 
			
		||||
    Ok(drive_status.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[sortable]
 | 
			
		||||
pub const SUBDIRS: SubdirMap = &sorted!([
 | 
			
		||||
    (
 | 
			
		||||
@ -849,6 +877,11 @@ pub const SUBDIRS: SubdirMap = &sorted!([
 | 
			
		||||
        &Router::new()
 | 
			
		||||
            .get(&API_METHOD_SCAN_DRIVES)
 | 
			
		||||
    ),
 | 
			
		||||
    (
 | 
			
		||||
        "status",
 | 
			
		||||
        &Router::new()
 | 
			
		||||
            .get(&API_METHOD_STATUS)
 | 
			
		||||
    ),
 | 
			
		||||
    (
 | 
			
		||||
        "unload",
 | 
			
		||||
        &Router::new()
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
//! Types for tape drive API
 | 
			
		||||
use std::convert::TryFrom;
 | 
			
		||||
 | 
			
		||||
use anyhow::{bail, Error};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use proxmox::api::{
 | 
			
		||||
@ -111,3 +113,69 @@ pub struct MamAttribute {
 | 
			
		||||
    /// Attribute value
 | 
			
		||||
    pub value: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[api()]
 | 
			
		||||
#[derive(Serialize,Deserialize,Debug)]
 | 
			
		||||
pub enum TapeDensity {
 | 
			
		||||
    /// No tape loaded
 | 
			
		||||
    None,
 | 
			
		||||
    /// 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::None,
 | 
			
		||||
            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,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
)]
 | 
			
		||||
#[derive(Serialize,Deserialize)]
 | 
			
		||||
#[serde(rename_all = "kebab-case")]
 | 
			
		||||
/// Drive status for Linux SCSI drives.
 | 
			
		||||
pub struct LinuxDriveStatusFlat {
 | 
			
		||||
    /// Block size (0 is variable size)
 | 
			
		||||
    pub blocksize: u32,
 | 
			
		||||
    /// Tape density
 | 
			
		||||
    pub density: TapeDensity,
 | 
			
		||||
    /// Status flags
 | 
			
		||||
    pub status: String,
 | 
			
		||||
    /// Current file number
 | 
			
		||||
    pub file_number: i32,
 | 
			
		||||
    /// Current block number
 | 
			
		||||
    pub block_number: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -554,6 +554,50 @@ fn cartridge_memory(
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[api(
 | 
			
		||||
    input: {
 | 
			
		||||
        properties: {
 | 
			
		||||
            drive: {
 | 
			
		||||
                schema: DRIVE_NAME_SCHEMA,
 | 
			
		||||
                optional: true,
 | 
			
		||||
            },
 | 
			
		||||
             "output-format": {
 | 
			
		||||
                schema: OUTPUT_FORMAT,
 | 
			
		||||
                optional: true,
 | 
			
		||||
             },
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
)]
 | 
			
		||||
/// Get drive status
 | 
			
		||||
fn status(
 | 
			
		||||
    mut param: Value,
 | 
			
		||||
    rpcenv: &mut dyn RpcEnvironment,
 | 
			
		||||
) -> Result<(), Error> {
 | 
			
		||||
 | 
			
		||||
    let (config, _digest) = config::drive::config()?;
 | 
			
		||||
 | 
			
		||||
    param["drive"] = lookup_drive_name(¶m, &config)?.into();
 | 
			
		||||
 | 
			
		||||
    let output_format = get_output_format(¶m);
 | 
			
		||||
    let info = &api2::tape::drive::API_METHOD_STATUS;
 | 
			
		||||
 | 
			
		||||
    let mut data = match info.handler {
 | 
			
		||||
        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
 | 
			
		||||
        _ => unreachable!(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let options = default_table_format_options()
 | 
			
		||||
        .column(ColumnConfig::new("blocksize"))
 | 
			
		||||
        .column(ColumnConfig::new("density"))
 | 
			
		||||
        .column(ColumnConfig::new("status"))
 | 
			
		||||
        .column(ColumnConfig::new("file-number"))
 | 
			
		||||
        .column(ColumnConfig::new("block-number"))
 | 
			
		||||
        ;
 | 
			
		||||
 | 
			
		||||
    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[api(
 | 
			
		||||
   input: {
 | 
			
		||||
        properties: {
 | 
			
		||||
@ -610,6 +654,11 @@ fn main() {
 | 
			
		||||
            CliCommand::new(&API_METHOD_DEBUG_SCAN)
 | 
			
		||||
                .completion_cb("drive", complete_drive_name)
 | 
			
		||||
        )
 | 
			
		||||
        .insert(
 | 
			
		||||
            "status",
 | 
			
		||||
            CliCommand::new(&API_METHOD_STATUS)
 | 
			
		||||
                .completion_cb("drive", complete_drive_name)
 | 
			
		||||
        )
 | 
			
		||||
        .insert(
 | 
			
		||||
            "eod",
 | 
			
		||||
            CliCommand::new(&API_METHOD_MOVE_TO_EOM)
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,10 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag};
 | 
			
		||||
use proxmox::sys::error::SysResult;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    api2::types::{
 | 
			
		||||
        TapeDensity,
 | 
			
		||||
        LinuxDriveStatusFlat,
 | 
			
		||||
    },
 | 
			
		||||
    tape::{
 | 
			
		||||
        TapeRead,
 | 
			
		||||
        TapeWrite,
 | 
			
		||||
@ -31,40 +35,7 @@ use crate::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum TapeDensity {
 | 
			
		||||
    None, // no tape loaded
 | 
			
		||||
    LTO2,
 | 
			
		||||
    LTO3,
 | 
			
		||||
    LTO4,
 | 
			
		||||
    LTO5,
 | 
			
		||||
    LTO6,
 | 
			
		||||
    LTO7,
 | 
			
		||||
    LTO7M8,
 | 
			
		||||
    LTO8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<u8> for TapeDensity {
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: u8) -> Result<Self, Self::Error> {
 | 
			
		||||
        let density = match value {
 | 
			
		||||
            0x00 => TapeDensity::None,
 | 
			
		||||
            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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct DriveStatus {
 | 
			
		||||
pub struct LinuxDriveStatus {
 | 
			
		||||
    pub blocksize: u32,
 | 
			
		||||
    pub density: TapeDensity,
 | 
			
		||||
    pub status: GMTStatusFlags,
 | 
			
		||||
@ -72,13 +43,25 @@ pub struct DriveStatus {
 | 
			
		||||
    pub block_number: i32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DriveStatus {
 | 
			
		||||
impl LinuxDriveStatus {
 | 
			
		||||
    pub fn tape_is_ready(&self) -> bool {
 | 
			
		||||
        self.status.contains(GMTStatusFlags::ONLINE) &&
 | 
			
		||||
            !self.status.contains(GMTStatusFlags::DRIVE_OPEN)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<LinuxDriveStatus> for LinuxDriveStatusFlat {
 | 
			
		||||
    fn from(status: LinuxDriveStatus) -> Self {
 | 
			
		||||
         LinuxDriveStatusFlat {
 | 
			
		||||
             blocksize: status.blocksize,
 | 
			
		||||
             density: status.density,
 | 
			
		||||
             status: format!("{:?}", status.status),
 | 
			
		||||
             file_number: status.file_number,
 | 
			
		||||
             block_number: status.block_number,
 | 
			
		||||
         }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LinuxTapeDrive {
 | 
			
		||||
 | 
			
		||||
    /// This needs to lock the drive
 | 
			
		||||
@ -242,7 +225,7 @@ impl LinuxTapeHandle {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get Tape configuration with MTIOCGET ioctl
 | 
			
		||||
    pub fn get_drive_status(&self) -> Result<DriveStatus, Error> {
 | 
			
		||||
    pub fn get_drive_status(&self) -> Result<LinuxDriveStatus, Error> {
 | 
			
		||||
 | 
			
		||||
        self.mtnop()?;
 | 
			
		||||
 | 
			
		||||
@ -268,7 +251,7 @@ impl LinuxTapeHandle {
 | 
			
		||||
 | 
			
		||||
        let density = TapeDensity::try_from(density)?;
 | 
			
		||||
 | 
			
		||||
        Ok(DriveStatus {
 | 
			
		||||
        Ok(LinuxDriveStatus {
 | 
			
		||||
            blocksize,
 | 
			
		||||
            density,
 | 
			
		||||
            status: gmt,
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
mod virtual_tape;
 | 
			
		||||
mod linux_mtio;
 | 
			
		||||
mod linux_tape;
 | 
			
		||||
pub mod linux_tape;
 | 
			
		||||
 | 
			
		||||
mod mam;
 | 
			
		||||
pub use mam::*;
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user