tape: add drive status api
This commit is contained in:
		@ -1,7 +1,7 @@
 | 
				
			|||||||
use std::path::Path;
 | 
					use std::path::Path;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::{bail, Error};
 | 
					use anyhow::{bail, format_err, Error};
 | 
				
			||||||
use serde_json::Value;
 | 
					use serde_json::Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use proxmox::{
 | 
					use proxmox::{
 | 
				
			||||||
@ -35,6 +35,7 @@ use crate::{
 | 
				
			|||||||
        MediaIdFlat,
 | 
					        MediaIdFlat,
 | 
				
			||||||
        LabelUuidMap,
 | 
					        LabelUuidMap,
 | 
				
			||||||
        MamAttribute,
 | 
					        MamAttribute,
 | 
				
			||||||
 | 
					        LinuxDriveStatusFlat,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    server::WorkerTask,
 | 
					    server::WorkerTask,
 | 
				
			||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
@ -796,6 +797,33 @@ pub fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
 | 
				
			|||||||
    read_mam_attributes(&drive_config.path)
 | 
					    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]
 | 
					#[sortable]
 | 
				
			||||||
pub const SUBDIRS: SubdirMap = &sorted!([
 | 
					pub const SUBDIRS: SubdirMap = &sorted!([
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
@ -849,6 +877,11 @@ pub const SUBDIRS: SubdirMap = &sorted!([
 | 
				
			|||||||
        &Router::new()
 | 
					        &Router::new()
 | 
				
			||||||
            .get(&API_METHOD_SCAN_DRIVES)
 | 
					            .get(&API_METHOD_SCAN_DRIVES)
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        "status",
 | 
				
			||||||
 | 
					        &Router::new()
 | 
				
			||||||
 | 
					            .get(&API_METHOD_STATUS)
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    (
 | 
					    (
 | 
				
			||||||
        "unload",
 | 
					        "unload",
 | 
				
			||||||
        &Router::new()
 | 
					        &Router::new()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,7 @@
 | 
				
			|||||||
//! Types for tape drive API
 | 
					//! Types for tape drive API
 | 
				
			||||||
 | 
					use std::convert::TryFrom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::{bail, Error};
 | 
				
			||||||
use serde::{Deserialize, Serialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use proxmox::api::{
 | 
					use proxmox::api::{
 | 
				
			||||||
@ -111,3 +113,69 @@ pub struct MamAttribute {
 | 
				
			|||||||
    /// Attribute value
 | 
					    /// Attribute value
 | 
				
			||||||
    pub value: String,
 | 
					    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(())
 | 
					    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(
 | 
					#[api(
 | 
				
			||||||
   input: {
 | 
					   input: {
 | 
				
			||||||
        properties: {
 | 
					        properties: {
 | 
				
			||||||
@ -610,6 +654,11 @@ fn main() {
 | 
				
			|||||||
            CliCommand::new(&API_METHOD_DEBUG_SCAN)
 | 
					            CliCommand::new(&API_METHOD_DEBUG_SCAN)
 | 
				
			||||||
                .completion_cb("drive", complete_drive_name)
 | 
					                .completion_cb("drive", complete_drive_name)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        .insert(
 | 
				
			||||||
 | 
					            "status",
 | 
				
			||||||
 | 
					            CliCommand::new(&API_METHOD_STATUS)
 | 
				
			||||||
 | 
					                .completion_cb("drive", complete_drive_name)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "eod",
 | 
					            "eod",
 | 
				
			||||||
            CliCommand::new(&API_METHOD_MOVE_TO_EOM)
 | 
					            CliCommand::new(&API_METHOD_MOVE_TO_EOM)
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,10 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag};
 | 
				
			|||||||
use proxmox::sys::error::SysResult;
 | 
					use proxmox::sys::error::SysResult;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
 | 
					    api2::types::{
 | 
				
			||||||
 | 
					        TapeDensity,
 | 
				
			||||||
 | 
					        LinuxDriveStatusFlat,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
        TapeRead,
 | 
					        TapeRead,
 | 
				
			||||||
        TapeWrite,
 | 
					        TapeWrite,
 | 
				
			||||||
@ -31,40 +35,7 @@ use crate::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum TapeDensity {
 | 
					pub struct LinuxDriveStatus {
 | 
				
			||||||
    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 blocksize: u32,
 | 
					    pub blocksize: u32,
 | 
				
			||||||
    pub density: TapeDensity,
 | 
					    pub density: TapeDensity,
 | 
				
			||||||
    pub status: GMTStatusFlags,
 | 
					    pub status: GMTStatusFlags,
 | 
				
			||||||
@ -72,13 +43,25 @@ pub struct DriveStatus {
 | 
				
			|||||||
    pub block_number: i32,
 | 
					    pub block_number: i32,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl DriveStatus {
 | 
					impl LinuxDriveStatus {
 | 
				
			||||||
    pub fn tape_is_ready(&self) -> bool {
 | 
					    pub fn tape_is_ready(&self) -> bool {
 | 
				
			||||||
        self.status.contains(GMTStatusFlags::ONLINE) &&
 | 
					        self.status.contains(GMTStatusFlags::ONLINE) &&
 | 
				
			||||||
            !self.status.contains(GMTStatusFlags::DRIVE_OPEN)
 | 
					            !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 {
 | 
					impl LinuxTapeDrive {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// This needs to lock the drive
 | 
					    /// This needs to lock the drive
 | 
				
			||||||
@ -242,7 +225,7 @@ impl LinuxTapeHandle {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get Tape configuration with MTIOCGET ioctl
 | 
					    /// 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()?;
 | 
					        self.mtnop()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -268,7 +251,7 @@ impl LinuxTapeHandle {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let density = TapeDensity::try_from(density)?;
 | 
					        let density = TapeDensity::try_from(density)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(DriveStatus {
 | 
					        Ok(LinuxDriveStatus {
 | 
				
			||||||
            blocksize,
 | 
					            blocksize,
 | 
				
			||||||
            density,
 | 
					            density,
 | 
				
			||||||
            status: gmt,
 | 
					            status: gmt,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
mod virtual_tape;
 | 
					mod virtual_tape;
 | 
				
			||||||
mod linux_mtio;
 | 
					mod linux_mtio;
 | 
				
			||||||
mod linux_tape;
 | 
					pub mod linux_tape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod mam;
 | 
					mod mam;
 | 
				
			||||||
pub use mam::*;
 | 
					pub use mam::*;
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user