tape: cleanup MediaLocation type for direct use with API
This commit is contained in:
		@ -17,32 +17,16 @@ use crate::{
 | 
				
			|||||||
        MediaPoolConfig,
 | 
					        MediaPoolConfig,
 | 
				
			||||||
        MediaListEntry,
 | 
					        MediaListEntry,
 | 
				
			||||||
        MediaStatus,
 | 
					        MediaStatus,
 | 
				
			||||||
        MediaLocationKind,
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
        TAPE_STATUS_DIR,
 | 
					        TAPE_STATUS_DIR,
 | 
				
			||||||
        Inventory,
 | 
					        Inventory,
 | 
				
			||||||
        MediaStateDatabase,
 | 
					        MediaStateDatabase,
 | 
				
			||||||
        MediaLocation,
 | 
					 | 
				
			||||||
        MediaPool,
 | 
					        MediaPool,
 | 
				
			||||||
        update_online_status,
 | 
					        update_online_status,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn split_location(location: &MediaLocation) -> (MediaLocationKind, Option<String>) {
 | 
					 | 
				
			||||||
    match location {
 | 
					 | 
				
			||||||
        MediaLocation::Online(changer_name) => {
 | 
					 | 
				
			||||||
            (MediaLocationKind::Online, Some(changer_name.to_string()))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        MediaLocation::Offline => {
 | 
					 | 
				
			||||||
            (MediaLocationKind::Offline, None)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        MediaLocation::Vault(vault) => {
 | 
					 | 
				
			||||||
            (MediaLocationKind::Vault, Some(vault.to_string()))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[api(
 | 
					#[api(
 | 
				
			||||||
    input: {
 | 
					    input: {
 | 
				
			||||||
        properties: {
 | 
					        properties: {
 | 
				
			||||||
@ -94,8 +78,6 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
        let current_time = proxmox::tools::time::epoch_i64();
 | 
					        let current_time = proxmox::tools::time::epoch_i64();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for media in pool.list_media() {
 | 
					        for media in pool.list_media() {
 | 
				
			||||||
            let (location, location_hint) = split_location(&media.location());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let expired = pool.media_is_expired(&media, current_time);
 | 
					            let expired = pool.media_is_expired(&media, current_time);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let media_set_uuid = media.media_set_label().as_ref()
 | 
					            let media_set_uuid = media.media_set_label().as_ref()
 | 
				
			||||||
@ -114,8 +96,7 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
                uuid: media.uuid().to_string(),
 | 
					                uuid: media.uuid().to_string(),
 | 
				
			||||||
                changer_id: media.changer_id().to_string(),
 | 
					                changer_id: media.changer_id().to_string(),
 | 
				
			||||||
                pool: Some(pool_name.to_string()),
 | 
					                pool: Some(pool_name.to_string()),
 | 
				
			||||||
                location,
 | 
					                location: media.location().clone(),
 | 
				
			||||||
                location_hint,
 | 
					 | 
				
			||||||
                status: *media.status(),
 | 
					                status: *media.status(),
 | 
				
			||||||
                expired,
 | 
					                expired,
 | 
				
			||||||
                media_set_uuid,
 | 
					                media_set_uuid,
 | 
				
			||||||
@ -133,7 +114,6 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
        for media_id in inventory.list_unassigned_media() {
 | 
					        for media_id in inventory.list_unassigned_media() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let (mut status, location) = state_db.status_and_location(&media_id.label.uuid);
 | 
					            let (mut status, location) = state_db.status_and_location(&media_id.label.uuid);
 | 
				
			||||||
            let (location, location_hint) = split_location(&location);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if status == MediaStatus::Unknown {
 | 
					            if status == MediaStatus::Unknown {
 | 
				
			||||||
                status = MediaStatus::Writable;
 | 
					                status = MediaStatus::Writable;
 | 
				
			||||||
@ -143,7 +123,6 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
                uuid: media_id.label.uuid.to_string(),
 | 
					                uuid: media_id.label.uuid.to_string(),
 | 
				
			||||||
                changer_id: media_id.label.changer_id.to_string(),
 | 
					                changer_id: media_id.label.changer_id.to_string(),
 | 
				
			||||||
                location,
 | 
					                location,
 | 
				
			||||||
                location_hint,
 | 
					 | 
				
			||||||
                status,
 | 
					                status,
 | 
				
			||||||
                expired: false,
 | 
					                expired: false,
 | 
				
			||||||
                media_set_uuid: None,
 | 
					                media_set_uuid: None,
 | 
				
			||||||
 | 
				
			|||||||
@ -4,26 +4,13 @@ use proxmox::api::api;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    MediaStatus,
 | 
					    MediaStatus,
 | 
				
			||||||
 | 
					    MediaLocation,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[api()]
 | 
					 | 
				
			||||||
#[derive(Serialize,Deserialize)]
 | 
					 | 
				
			||||||
#[serde(rename_all = "lowercase")]
 | 
					 | 
				
			||||||
/// Media location
 | 
					 | 
				
			||||||
pub enum MediaLocationKind {
 | 
					 | 
				
			||||||
    /// Ready for use (inside tape library)
 | 
					 | 
				
			||||||
    Online,
 | 
					 | 
				
			||||||
    /// Local available, but need to be mounted (insert into tape
 | 
					 | 
				
			||||||
    /// drive)
 | 
					 | 
				
			||||||
    Offline,
 | 
					 | 
				
			||||||
    /// Media is inside a Vault
 | 
					 | 
				
			||||||
    Vault,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[api(
 | 
					#[api(
 | 
				
			||||||
    properties: {
 | 
					    properties: {
 | 
				
			||||||
        location: {
 | 
					        location: {
 | 
				
			||||||
            type: MediaLocationKind,
 | 
					            type: MediaLocation,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        status: {
 | 
					        status: {
 | 
				
			||||||
            type: MediaStatus,
 | 
					            type: MediaStatus,
 | 
				
			||||||
@ -38,9 +25,7 @@ pub struct MediaListEntry {
 | 
				
			|||||||
    pub changer_id: String,
 | 
					    pub changer_id: String,
 | 
				
			||||||
    /// Media Uuid
 | 
					    /// Media Uuid
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
    pub location: MediaLocationKind,
 | 
					    pub location: MediaLocation,
 | 
				
			||||||
    /// Media location hint (vault name, changer name)
 | 
					 | 
				
			||||||
    pub location_hint: Option<String>,
 | 
					 | 
				
			||||||
    pub status: MediaStatus,
 | 
					    pub status: MediaStatus,
 | 
				
			||||||
    /// Expired flag
 | 
					    /// Expired flag
 | 
				
			||||||
    pub expired: bool,
 | 
					    pub expired: bool,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										91
									
								
								src/api2/types/tape/media_location.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/api2/types/tape/media_location.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					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 MediaLocation {
 | 
				
			||||||
 | 
					    pub 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::Online(vault.to_string()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bail!("MediaLocation parse error");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -15,5 +15,8 @@ pub use media_pool::*;
 | 
				
			|||||||
mod media_status;
 | 
					mod media_status;
 | 
				
			||||||
pub use media_status::*;
 | 
					pub use media_status::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod media_location;
 | 
				
			||||||
 | 
					pub use media_location::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod media;
 | 
					mod media;
 | 
				
			||||||
pub use media::*;
 | 
					pub use media::*;
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,6 @@ use proxmox_backup::{
 | 
				
			|||||||
        self,
 | 
					        self,
 | 
				
			||||||
        types::{
 | 
					        types::{
 | 
				
			||||||
            MEDIA_POOL_NAME_SCHEMA,
 | 
					            MEDIA_POOL_NAME_SCHEMA,
 | 
				
			||||||
            MediaLocationKind,
 | 
					 | 
				
			||||||
            MediaStatus,
 | 
					            MediaStatus,
 | 
				
			||||||
            MediaListEntry,
 | 
					            MediaListEntry,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -72,19 +71,6 @@ async fn list_media(
 | 
				
			|||||||
        _ => unreachable!(),
 | 
					        _ => unreachable!(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn render_location(_value: &Value, record: &Value) -> Result<String, Error> {
 | 
					 | 
				
			||||||
        let record: MediaListEntry = serde_json::from_value(record.clone())?;
 | 
					 | 
				
			||||||
        Ok(match record.location {
 | 
					 | 
				
			||||||
            MediaLocationKind::Online =>  {
 | 
					 | 
				
			||||||
                record.location_hint.unwrap_or(String::from("-"))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            MediaLocationKind::Offline => String::from("offline"),
 | 
					 | 
				
			||||||
            MediaLocationKind::Vault => {
 | 
					 | 
				
			||||||
                format!("V({})", record.location_hint.unwrap_or(String::from("-")))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn render_status(_value: &Value, record: &Value) -> Result<String, Error> {
 | 
					    fn render_status(_value: &Value, record: &Value) -> Result<String, Error> {
 | 
				
			||||||
        let record: MediaListEntry = serde_json::from_value(record.clone())?;
 | 
					        let record: MediaListEntry = serde_json::from_value(record.clone())?;
 | 
				
			||||||
        Ok(match record.status {
 | 
					        Ok(match record.status {
 | 
				
			||||||
@ -115,7 +101,7 @@ async fn list_media(
 | 
				
			|||||||
        .column(ColumnConfig::new("media-set-name"))
 | 
					        .column(ColumnConfig::new("media-set-name"))
 | 
				
			||||||
        .column(ColumnConfig::new("seq-nr"))
 | 
					        .column(ColumnConfig::new("seq-nr"))
 | 
				
			||||||
        .column(ColumnConfig::new("status").renderer(render_status))
 | 
					        .column(ColumnConfig::new("status").renderer(render_status))
 | 
				
			||||||
        .column(ColumnConfig::new("location").renderer(render_location))
 | 
					        .column(ColumnConfig::new("location"))
 | 
				
			||||||
        .column(ColumnConfig::new("uuid"))
 | 
					        .column(ColumnConfig::new("uuid"))
 | 
				
			||||||
        .column(ColumnConfig::new("media-set-uuid"))
 | 
					        .column(ColumnConfig::new("media-set-uuid"))
 | 
				
			||||||
        ;
 | 
					        ;
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,7 @@ use proxmox::tools::Uuid;
 | 
				
			|||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    api2::types::{
 | 
					    api2::types::{
 | 
				
			||||||
        MediaStatus,
 | 
					        MediaStatus,
 | 
				
			||||||
 | 
					        MediaLocation,
 | 
				
			||||||
        MediaSetPolicy,
 | 
					        MediaSetPolicy,
 | 
				
			||||||
        RetentionPolicy,
 | 
					        RetentionPolicy,
 | 
				
			||||||
        MediaPoolConfig,
 | 
					        MediaPoolConfig,
 | 
				
			||||||
@ -24,7 +25,6 @@ use crate::{
 | 
				
			|||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
        MediaId,
 | 
					        MediaId,
 | 
				
			||||||
        MediaSet,
 | 
					        MediaSet,
 | 
				
			||||||
        MediaLocation,
 | 
					 | 
				
			||||||
        Inventory,
 | 
					        Inventory,
 | 
				
			||||||
        MediaStateDatabase,
 | 
					        MediaStateDatabase,
 | 
				
			||||||
        file_formats::{
 | 
					        file_formats::{
 | 
				
			||||||
 | 
				
			|||||||
@ -21,21 +21,10 @@ use crate::{
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    api2::types::{
 | 
					    api2::types::{
 | 
				
			||||||
        MediaStatus,
 | 
					        MediaStatus,
 | 
				
			||||||
 | 
					        MediaLocation,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
 | 
					 | 
				
			||||||
/// 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),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Serialize,Deserialize)]
 | 
					#[derive(Serialize,Deserialize)]
 | 
				
			||||||
struct MediaStateEntry {
 | 
					struct MediaStateEntry {
 | 
				
			||||||
    u: Uuid,
 | 
					    u: Uuid,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user