tape: rename changer_id to label_text
This commit is contained in:
		@ -170,7 +170,7 @@ fn update_media_online_status(drive: &str) -> Result<bool, Error> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        has_changer = true;
 | 
					        has_changer = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let changer_id_list = changer.online_media_changer_ids()?;
 | 
					        let label_text_list = changer.online_media_label_texts()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let status_path = Path::new(TAPE_STATUS_DIR);
 | 
					        let status_path = Path::new(TAPE_STATUS_DIR);
 | 
				
			||||||
        let mut inventory = Inventory::load(status_path)?;
 | 
					        let mut inventory = Inventory::load(status_path)?;
 | 
				
			||||||
@ -179,7 +179,7 @@ fn update_media_online_status(drive: &str) -> Result<bool, Error> {
 | 
				
			|||||||
            &config,
 | 
					            &config,
 | 
				
			||||||
            &mut inventory,
 | 
					            &mut inventory,
 | 
				
			||||||
            &changer_name,
 | 
					            &changer_name,
 | 
				
			||||||
            &changer_id_list,
 | 
					            &label_text_list,
 | 
				
			||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -70,7 +70,7 @@ pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
 | 
				
			|||||||
        let entry = MtxStatusEntry {
 | 
					        let entry = MtxStatusEntry {
 | 
				
			||||||
            entry_kind: MtxEntryKind::Drive,
 | 
					            entry_kind: MtxEntryKind::Drive,
 | 
				
			||||||
            entry_id: id as u64,
 | 
					            entry_id: id as u64,
 | 
				
			||||||
            changer_id: match &drive_status.status {
 | 
					            label_text: match &drive_status.status {
 | 
				
			||||||
                ElementStatus::Empty => None,
 | 
					                ElementStatus::Empty => None,
 | 
				
			||||||
                ElementStatus::Full => Some(String::new()),
 | 
					                ElementStatus::Full => Some(String::new()),
 | 
				
			||||||
                ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
 | 
					                ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
 | 
				
			||||||
@ -88,7 +88,7 @@ pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
 | 
				
			|||||||
                MtxEntryKind::Slot
 | 
					                MtxEntryKind::Slot
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            entry_id: id as u64 + 1,
 | 
					            entry_id: id as u64 + 1,
 | 
				
			||||||
            changer_id: match &slot_status {
 | 
					            label_text: match &slot_status {
 | 
				
			||||||
                ElementStatus::Empty => None,
 | 
					                ElementStatus::Empty => None,
 | 
				
			||||||
                ElementStatus::Full => Some(String::new()),
 | 
					                ElementStatus::Full => Some(String::new()),
 | 
				
			||||||
                ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
 | 
					                ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
 | 
				
			||||||
 | 
				
			|||||||
@ -70,7 +70,7 @@ use crate::{
 | 
				
			|||||||
            drive: {
 | 
					            drive: {
 | 
				
			||||||
                schema: DRIVE_NAME_SCHEMA,
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -79,13 +79,13 @@ use crate::{
 | 
				
			|||||||
/// Load media with specified label
 | 
					/// Load media with specified label
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Issue a media load request to the associated changer device.
 | 
					/// Issue a media load request to the associated changer device.
 | 
				
			||||||
pub async fn load_media(drive: String, changer_id: String) -> Result<(), Error> {
 | 
					pub async fn load_media(drive: String, label_text: String) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let (config, _digest) = config::drive::config()?;
 | 
					    let (config, _digest) = config::drive::config()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tokio::task::spawn_blocking(move || {
 | 
					    tokio::task::spawn_blocking(move || {
 | 
				
			||||||
        let (mut changer, _) = required_media_changer(&config, &drive)?;
 | 
					        let (mut changer, _) = required_media_changer(&config, &drive)?;
 | 
				
			||||||
        changer.load_media(&changer_id)
 | 
					        changer.load_media(&label_text)
 | 
				
			||||||
    }).await?
 | 
					    }).await?
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -121,7 +121,7 @@ pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
 | 
				
			|||||||
            drive: {
 | 
					            drive: {
 | 
				
			||||||
                schema: DRIVE_NAME_SCHEMA,
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -133,15 +133,15 @@ pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
)]
 | 
					)]
 | 
				
			||||||
/// Export media with specified label
 | 
					/// Export media with specified label
 | 
				
			||||||
pub async fn export_media(drive: String, changer_id: String) -> Result<u64, Error> {
 | 
					pub async fn export_media(drive: String, label_text: String) -> Result<u64, Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let (config, _digest) = config::drive::config()?;
 | 
					    let (config, _digest) = config::drive::config()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tokio::task::spawn_blocking(move || {
 | 
					    tokio::task::spawn_blocking(move || {
 | 
				
			||||||
        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
					        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
				
			||||||
        match changer.export_media(&changer_id)? {
 | 
					        match changer.export_media(&label_text)? {
 | 
				
			||||||
            Some(slot) => Ok(slot),
 | 
					            Some(slot) => Ok(slot),
 | 
				
			||||||
            None => bail!("media '{}' is not online (via changer '{}')", changer_id, changer_name),
 | 
					            None => bail!("media '{}' is not online (via changer '{}')", label_text, changer_name),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }).await?
 | 
					    }).await?
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -315,7 +315,7 @@ pub async fn eject_media(drive: String) -> Result<(), Error> {
 | 
				
			|||||||
            drive: {
 | 
					            drive: {
 | 
				
			||||||
                schema: DRIVE_NAME_SCHEMA,
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            pool: {
 | 
					            pool: {
 | 
				
			||||||
@ -337,7 +337,7 @@ pub async fn eject_media(drive: String) -> Result<(), Error> {
 | 
				
			|||||||
pub fn label_media(
 | 
					pub fn label_media(
 | 
				
			||||||
    drive: String,
 | 
					    drive: String,
 | 
				
			||||||
    pool: Option<String>,
 | 
					    pool: Option<String>,
 | 
				
			||||||
    changer_id: String,
 | 
					    label_text: String,
 | 
				
			||||||
    rpcenv: &mut dyn RpcEnvironment,
 | 
					    rpcenv: &mut dyn RpcEnvironment,
 | 
				
			||||||
) -> Result<Value, Error> {
 | 
					) -> Result<Value, Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -380,7 +380,7 @@ pub fn label_media(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let ctime = proxmox::tools::time::epoch_i64();
 | 
					            let ctime = proxmox::tools::time::epoch_i64();
 | 
				
			||||||
            let label = MediaLabel {
 | 
					            let label = MediaLabel {
 | 
				
			||||||
                changer_id: changer_id.to_string(),
 | 
					                label_text: label_text.to_string(),
 | 
				
			||||||
                uuid: Uuid::generate(),
 | 
					                uuid: Uuid::generate(),
 | 
				
			||||||
                ctime,
 | 
					                ctime,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
@ -405,13 +405,13 @@ fn write_media_label(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if let Some(ref pool) = pool {
 | 
					    if let Some(ref pool) = pool {
 | 
				
			||||||
        // assign media to pool by writing special media set label
 | 
					        // assign media to pool by writing special media set label
 | 
				
			||||||
        worker.log(format!("Label media '{}' for pool '{}'", label.changer_id, pool));
 | 
					        worker.log(format!("Label media '{}' for pool '{}'", label.label_text, pool));
 | 
				
			||||||
        let set = MediaSetLabel::with_data(&pool, [0u8; 16].into(), 0, label.ctime);
 | 
					        let set = MediaSetLabel::with_data(&pool, [0u8; 16].into(), 0, label.ctime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        drive.write_media_set_label(&set)?;
 | 
					        drive.write_media_set_label(&set)?;
 | 
				
			||||||
        media_set_label = Some(set);
 | 
					        media_set_label = Some(set);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        worker.log(format!("Label media '{}' (no pool assignment)", label.changer_id));
 | 
					        worker.log(format!("Label media '{}' (no pool assignment)", label.label_text));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let media_id = MediaId { label, media_set_label };
 | 
					    let media_id = MediaId { label, media_set_label };
 | 
				
			||||||
@ -482,7 +482,7 @@ pub async fn read_label(drive: String) -> Result<MediaIdFlat, Error> {
 | 
				
			|||||||
            Some(media_id) => {
 | 
					            Some(media_id) => {
 | 
				
			||||||
                let mut flat = MediaIdFlat {
 | 
					                let mut flat = MediaIdFlat {
 | 
				
			||||||
                    uuid: media_id.label.uuid.to_string(),
 | 
					                    uuid: media_id.label.uuid.to_string(),
 | 
				
			||||||
                    changer_id: media_id.label.changer_id.clone(),
 | 
					                    label_text: media_id.label.label_text.clone(),
 | 
				
			||||||
                    ctime: media_id.label.ctime,
 | 
					                    ctime: media_id.label.ctime,
 | 
				
			||||||
                    media_set_ctime: None,
 | 
					                    media_set_ctime: None,
 | 
				
			||||||
                    media_set_uuid: None,
 | 
					                    media_set_uuid: None,
 | 
				
			||||||
@ -585,7 +585,7 @@ pub async fn inventory(
 | 
				
			|||||||
    tokio::task::spawn_blocking(move || {
 | 
					    tokio::task::spawn_blocking(move || {
 | 
				
			||||||
        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
					        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let changer_id_list = changer.online_media_changer_ids()?;
 | 
					        let label_text_list = changer.online_media_label_texts()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let state_path = Path::new(TAPE_STATUS_DIR);
 | 
					        let state_path = Path::new(TAPE_STATUS_DIR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -595,23 +595,23 @@ pub async fn inventory(
 | 
				
			|||||||
            &config,
 | 
					            &config,
 | 
				
			||||||
            &mut inventory,
 | 
					            &mut inventory,
 | 
				
			||||||
            &changer_name,
 | 
					            &changer_name,
 | 
				
			||||||
            &changer_id_list,
 | 
					            &label_text_list,
 | 
				
			||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut list = Vec::new();
 | 
					        let mut list = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for changer_id in changer_id_list.iter() {
 | 
					        for label_text in label_text_list.iter() {
 | 
				
			||||||
            if changer_id.starts_with("CLN") {
 | 
					            if label_text.starts_with("CLN") {
 | 
				
			||||||
                // skip cleaning unit
 | 
					                // skip cleaning unit
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let changer_id = changer_id.to_string();
 | 
					            let label_text = label_text.to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(media_id) = inventory.find_media_by_changer_id(&changer_id) {
 | 
					            if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
 | 
				
			||||||
                list.push(LabelUuidMap { changer_id, uuid: Some(media_id.label.uuid.to_string()) });
 | 
					                list.push(LabelUuidMap { label_text, uuid: Some(media_id.label.uuid.to_string()) });
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                list.push(LabelUuidMap { changer_id, uuid: None });
 | 
					                list.push(LabelUuidMap { label_text, uuid: None });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -668,8 +668,8 @@ pub fn update_inventory(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
					            let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let changer_id_list = changer.online_media_changer_ids()?;
 | 
					            let label_text_list = changer.online_media_label_texts()?;
 | 
				
			||||||
            if changer_id_list.is_empty() {
 | 
					            if label_text_list.is_empty() {
 | 
				
			||||||
                worker.log(format!("changer device does not list any media labels"));
 | 
					                worker.log(format!("changer device does not list any media labels"));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -677,42 +677,42 @@ pub fn update_inventory(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let mut inventory = Inventory::load(state_path)?;
 | 
					            let mut inventory = Inventory::load(state_path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            update_changer_online_status(&config, &mut inventory, &changer_name, &changer_id_list)?;
 | 
					            update_changer_online_status(&config, &mut inventory, &changer_name, &label_text_list)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for changer_id in changer_id_list.iter() {
 | 
					            for label_text in label_text_list.iter() {
 | 
				
			||||||
                if changer_id.starts_with("CLN") {
 | 
					                if label_text.starts_with("CLN") {
 | 
				
			||||||
                    worker.log(format!("skip cleaning unit '{}'", changer_id));
 | 
					                    worker.log(format!("skip cleaning unit '{}'", label_text));
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let changer_id = changer_id.to_string();
 | 
					                let label_text = label_text.to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if !read_all_labels.unwrap_or(false) {
 | 
					                if !read_all_labels.unwrap_or(false) {
 | 
				
			||||||
                    if let Some(_) = inventory.find_media_by_changer_id(&changer_id) {
 | 
					                    if let Some(_) = inventory.find_media_by_label_text(&label_text) {
 | 
				
			||||||
                        worker.log(format!("media '{}' already inventoried", changer_id));
 | 
					                        worker.log(format!("media '{}' already inventoried", label_text));
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if let Err(err) = changer.load_media(&changer_id) {
 | 
					                if let Err(err) = changer.load_media(&label_text) {
 | 
				
			||||||
                    worker.warn(format!("unable to load media '{}' - {}", changer_id, err));
 | 
					                    worker.warn(format!("unable to load media '{}' - {}", label_text, err));
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let mut drive = open_drive(&config, &drive)?;
 | 
					                let mut drive = open_drive(&config, &drive)?;
 | 
				
			||||||
                match drive.read_label() {
 | 
					                match drive.read_label() {
 | 
				
			||||||
                    Err(err) => {
 | 
					                    Err(err) => {
 | 
				
			||||||
                        worker.warn(format!("unable to read label form media '{}' - {}", changer_id, err));
 | 
					                        worker.warn(format!("unable to read label form media '{}' - {}", label_text, err));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Ok(None) => {
 | 
					                    Ok(None) => {
 | 
				
			||||||
                        worker.log(format!("media '{}' is empty", changer_id));
 | 
					                        worker.log(format!("media '{}' is empty", label_text));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Ok(Some(media_id)) => {
 | 
					                    Ok(Some(media_id)) => {
 | 
				
			||||||
                        if changer_id != media_id.label.changer_id {
 | 
					                        if label_text != media_id.label.label_text {
 | 
				
			||||||
                            worker.warn(format!("label changer ID missmatch ({} != {})", changer_id, media_id.label.changer_id));
 | 
					                            worker.warn(format!("label text missmatch ({} != {})", label_text, media_id.label.label_text));
 | 
				
			||||||
                            continue;
 | 
					                            continue;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        worker.log(format!("inventorize media '{}' with uuid '{}'", changer_id, media_id.label.uuid));
 | 
					                        worker.log(format!("inventorize media '{}' with uuid '{}'", label_text, media_id.label.uuid));
 | 
				
			||||||
                        inventory.store(media_id, false)?;
 | 
					                        inventory.store(media_id, false)?;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -784,31 +784,31 @@ fn barcode_label_media_worker(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
					    let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let changer_id_list = changer.online_media_changer_ids()?;
 | 
					    let label_text_list = changer.online_media_label_texts()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let state_path = Path::new(TAPE_STATUS_DIR);
 | 
					    let state_path = Path::new(TAPE_STATUS_DIR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut inventory = Inventory::load(state_path)?;
 | 
					    let mut inventory = Inventory::load(state_path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update_changer_online_status(&config, &mut inventory, &changer_name, &changer_id_list)?;
 | 
					    update_changer_online_status(&config, &mut inventory, &changer_name, &label_text_list)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if changer_id_list.is_empty() {
 | 
					    if label_text_list.is_empty() {
 | 
				
			||||||
        bail!("changer device does not list any media labels");
 | 
					        bail!("changer device does not list any media labels");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for changer_id in changer_id_list {
 | 
					    for label_text in label_text_list {
 | 
				
			||||||
        if changer_id.starts_with("CLN") { continue; }
 | 
					        if label_text.starts_with("CLN") { continue; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        inventory.reload()?;
 | 
					        inventory.reload()?;
 | 
				
			||||||
        if inventory.find_media_by_changer_id(&changer_id).is_some() {
 | 
					        if inventory.find_media_by_label_text(&label_text).is_some() {
 | 
				
			||||||
            worker.log(format!("media '{}' already inventoried (already labeled)", changer_id));
 | 
					            worker.log(format!("media '{}' already inventoried (already labeled)", label_text));
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        worker.log(format!("checking/loading media '{}'", changer_id));
 | 
					        worker.log(format!("checking/loading media '{}'", label_text));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Err(err) = changer.load_media(&changer_id) {
 | 
					        if let Err(err) = changer.load_media(&label_text) {
 | 
				
			||||||
            worker.warn(format!("unable to load media '{}' - {}", changer_id, err));
 | 
					            worker.warn(format!("unable to load media '{}' - {}", label_text, err));
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -817,7 +817,7 @@ fn barcode_label_media_worker(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        match drive.read_next_file() {
 | 
					        match drive.read_next_file() {
 | 
				
			||||||
            Ok(Some(_file)) => {
 | 
					            Ok(Some(_file)) => {
 | 
				
			||||||
                worker.log(format!("media '{}' is not empty (erase first)", changer_id));
 | 
					                worker.log(format!("media '{}' is not empty (erase first)", label_text));
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
 | 
					            Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
 | 
				
			||||||
@ -825,7 +825,7 @@ fn barcode_label_media_worker(
 | 
				
			|||||||
                if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) {
 | 
					                if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) {
 | 
				
			||||||
                    /* assume tape is empty */
 | 
					                    /* assume tape is empty */
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    worker.warn(format!("media '{}' read error (maybe not empty - erase first)", changer_id));
 | 
					                    worker.warn(format!("media '{}' read error (maybe not empty - erase first)", label_text));
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -833,7 +833,7 @@ fn barcode_label_media_worker(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let ctime = proxmox::tools::time::epoch_i64();
 | 
					        let ctime = proxmox::tools::time::epoch_i64();
 | 
				
			||||||
        let label = MediaLabel {
 | 
					        let label = MediaLabel {
 | 
				
			||||||
            changer_id: changer_id.to_string(),
 | 
					            label_text: label_text.to_string(),
 | 
				
			||||||
            uuid: Uuid::generate(),
 | 
					            uuid: Uuid::generate(),
 | 
				
			||||||
            ctime,
 | 
					            ctime,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
				
			|||||||
@ -112,7 +112,7 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            list.push(MediaListEntry {
 | 
					            list.push(MediaListEntry {
 | 
				
			||||||
                uuid: media.uuid().to_string(),
 | 
					                uuid: media.uuid().to_string(),
 | 
				
			||||||
                changer_id: media.changer_id().to_string(),
 | 
					                label_text: media.label_text().to_string(),
 | 
				
			||||||
                ctime: media.ctime(),
 | 
					                ctime: media.ctime(),
 | 
				
			||||||
                pool: Some(pool_name.to_string()),
 | 
					                pool: Some(pool_name.to_string()),
 | 
				
			||||||
                location: media.location().clone(),
 | 
					                location: media.location().clone(),
 | 
				
			||||||
@ -142,7 +142,7 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
            list.push(MediaListEntry {
 | 
					            list.push(MediaListEntry {
 | 
				
			||||||
                uuid: media_id.label.uuid.to_string(),
 | 
					                uuid: media_id.label.uuid.to_string(),
 | 
				
			||||||
                ctime: media_id.label.ctime,
 | 
					                ctime: media_id.label.ctime,
 | 
				
			||||||
                changer_id: media_id.label.changer_id.to_string(),
 | 
					                label_text: media_id.label.label_text.to_string(),
 | 
				
			||||||
                location,
 | 
					                location,
 | 
				
			||||||
                status,
 | 
					                status,
 | 
				
			||||||
                catalog: true, // empty, so we do not need a catalog
 | 
					                catalog: true, // empty, so we do not need a catalog
 | 
				
			||||||
@ -162,7 +162,7 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
#[api(
 | 
					#[api(
 | 
				
			||||||
    input: {
 | 
					    input: {
 | 
				
			||||||
        properties: {
 | 
					        properties: {
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            force: {
 | 
					            force: {
 | 
				
			||||||
@ -174,21 +174,21 @@ pub async fn list_media(pool: Option<String>) -> Result<Vec<MediaListEntry>, Err
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
)]
 | 
					)]
 | 
				
			||||||
/// Destroy media (completely remove from database)
 | 
					/// Destroy media (completely remove from database)
 | 
				
			||||||
pub fn destroy_media(changer_id: String, force: Option<bool>,) -> Result<(), Error> {
 | 
					pub fn destroy_media(label_text: String, force: Option<bool>,) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let force = force.unwrap_or(false);
 | 
					    let force = force.unwrap_or(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let status_path = Path::new(TAPE_STATUS_DIR);
 | 
					    let status_path = Path::new(TAPE_STATUS_DIR);
 | 
				
			||||||
    let mut inventory = Inventory::load(status_path)?;
 | 
					    let mut inventory = Inventory::load(status_path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let media_id = inventory.find_media_by_changer_id(&changer_id)
 | 
					    let media_id = inventory.find_media_by_label_text(&label_text)
 | 
				
			||||||
        .ok_or_else(|| format_err!("no such media '{}'", changer_id))?;
 | 
					        .ok_or_else(|| format_err!("no such media '{}'", label_text))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if !force {
 | 
					    if !force {
 | 
				
			||||||
        if let Some(ref set) = media_id.media_set_label {
 | 
					        if let Some(ref set) = media_id.media_set_label {
 | 
				
			||||||
            let is_empty = set.uuid.as_ref() == [0u8;16];
 | 
					            let is_empty = set.uuid.as_ref() == [0u8;16];
 | 
				
			||||||
            if !is_empty {
 | 
					            if !is_empty {
 | 
				
			||||||
                bail!("media '{}' contains data (please use 'force' flag to remove.", changer_id);
 | 
					                bail!("media '{}' contains data (please use 'force' flag to remove.", label_text);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -207,7 +207,7 @@ pub fn destroy_media(changer_id: String, force: Option<bool>,) -> Result<(), Err
 | 
				
			|||||||
            schema: MEDIA_POOL_NAME_SCHEMA,
 | 
					            schema: MEDIA_POOL_NAME_SCHEMA,
 | 
				
			||||||
            optional: true,
 | 
					            optional: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "changer-id": {
 | 
					        "label-text": {
 | 
				
			||||||
            schema: MEDIA_LABEL_SCHEMA,
 | 
					            schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            optional: true,
 | 
					            optional: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -236,7 +236,7 @@ pub fn destroy_media(changer_id: String, force: Option<bool>,) -> Result<(), Err
 | 
				
			|||||||
/// Content list filter parameters
 | 
					/// Content list filter parameters
 | 
				
			||||||
pub struct MediaContentListFilter {
 | 
					pub struct MediaContentListFilter {
 | 
				
			||||||
    pub pool: Option<String>,
 | 
					    pub pool: Option<String>,
 | 
				
			||||||
    pub changer_id: Option<String>,
 | 
					    pub label_text: Option<String>,
 | 
				
			||||||
    pub media: Option<String>,
 | 
					    pub media: Option<String>,
 | 
				
			||||||
    pub media_set: Option<String>,
 | 
					    pub media_set: Option<String>,
 | 
				
			||||||
    pub backup_type: Option<String>,
 | 
					    pub backup_type: Option<String>,
 | 
				
			||||||
@ -278,8 +278,8 @@ pub fn list_content(
 | 
				
			|||||||
    for media_id in inventory.list_used_media() {
 | 
					    for media_id in inventory.list_used_media() {
 | 
				
			||||||
        let set = media_id.media_set_label.as_ref().unwrap();
 | 
					        let set = media_id.media_set_label.as_ref().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(ref changer_id) = filter.changer_id {
 | 
					        if let Some(ref label_text) = filter.label_text {
 | 
				
			||||||
            if &media_id.label.changer_id != changer_id { continue; }
 | 
					            if &media_id.label.label_text != label_text { continue; }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(ref pool) = filter.pool {
 | 
					        if let Some(ref pool) = filter.pool {
 | 
				
			||||||
@ -314,7 +314,7 @@ pub fn list_content(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            list.push(MediaContentEntry {
 | 
					            list.push(MediaContentEntry {
 | 
				
			||||||
                uuid: media_id.label.uuid.to_string(),
 | 
					                uuid: media_id.label.uuid.to_string(),
 | 
				
			||||||
                changer_id: media_id.label.changer_id.to_string(),
 | 
					                label_text: media_id.label.label_text.to_string(),
 | 
				
			||||||
                pool: set.pool.clone(),
 | 
					                pool: set.pool.clone(),
 | 
				
			||||||
                media_set_name: media_set_name.clone(),
 | 
					                media_set_name: media_set_name.clone(),
 | 
				
			||||||
                media_set_uuid: set.uuid.to_string(),
 | 
					                media_set_uuid: set.uuid.to_string(),
 | 
				
			||||||
 | 
				
			|||||||
@ -152,7 +152,7 @@ pub fn restore(
 | 
				
			|||||||
            worker.log(format!(
 | 
					            worker.log(format!(
 | 
				
			||||||
                "Required media list: {}",
 | 
					                "Required media list: {}",
 | 
				
			||||||
                media_id_list.iter()
 | 
					                media_id_list.iter()
 | 
				
			||||||
                    .map(|media_id| media_id.label.changer_id.as_str())
 | 
					                    .map(|media_id| media_id.label.label_text.as_str())
 | 
				
			||||||
                    .collect::<Vec<&str>>()
 | 
					                    .collect::<Vec<&str>>()
 | 
				
			||||||
                    .join(";")
 | 
					                    .join(";")
 | 
				
			||||||
            ));
 | 
					            ));
 | 
				
			||||||
@ -196,12 +196,12 @@ pub fn request_and_restore_media(
 | 
				
			|||||||
    match info.media_set_label {
 | 
					    match info.media_set_label {
 | 
				
			||||||
        None => {
 | 
					        None => {
 | 
				
			||||||
            bail!("missing media set label on media {} ({})",
 | 
					            bail!("missing media set label on media {} ({})",
 | 
				
			||||||
                  media_id.label.changer_id, media_id.label.uuid);
 | 
					                  media_id.label.label_text, media_id.label.uuid);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Some(ref set) => {
 | 
					        Some(ref set) => {
 | 
				
			||||||
            if &set.uuid != media_set_uuid {
 | 
					            if &set.uuid != media_set_uuid {
 | 
				
			||||||
                bail!("wrong media set label on media {} ({} != {})",
 | 
					                bail!("wrong media set label on media {} ({} != {})",
 | 
				
			||||||
                      media_id.label.changer_id, media_id.label.uuid,
 | 
					                      media_id.label.label_text, media_id.label.uuid,
 | 
				
			||||||
                      media_set_uuid);
 | 
					                      media_set_uuid);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -87,7 +87,7 @@ pub enum MtxEntryKind {
 | 
				
			|||||||
        "entry-kind": {
 | 
					        "entry-kind": {
 | 
				
			||||||
            type: MtxEntryKind,
 | 
					            type: MtxEntryKind,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "changer-id": {
 | 
					        "label-text": {
 | 
				
			||||||
            schema: MEDIA_LABEL_SCHEMA,
 | 
					            schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            optional: true,
 | 
					            optional: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -102,7 +102,7 @@ pub struct MtxStatusEntry {
 | 
				
			|||||||
    pub entry_id: u64,
 | 
					    pub entry_id: u64,
 | 
				
			||||||
    /// The media label (volume tag) if the slot/drive is full
 | 
					    /// The media label (volume tag) if the slot/drive is full
 | 
				
			||||||
    #[serde(skip_serializing_if="Option::is_none")]
 | 
					    #[serde(skip_serializing_if="Option::is_none")]
 | 
				
			||||||
    pub changer_id: Option<String>,
 | 
					    pub label_text: Option<String>,
 | 
				
			||||||
    /// The slot the drive was loaded from
 | 
					    /// The slot the drive was loaded from
 | 
				
			||||||
    #[serde(skip_serializing_if="Option::is_none")]
 | 
					    #[serde(skip_serializing_if="Option::is_none")]
 | 
				
			||||||
    pub loaded_slot: Option<u64>,
 | 
					    pub loaded_slot: Option<u64>,
 | 
				
			||||||
 | 
				
			|||||||
@ -21,8 +21,8 @@ use super::{
 | 
				
			|||||||
#[serde(rename_all = "kebab-case")]
 | 
					#[serde(rename_all = "kebab-case")]
 | 
				
			||||||
/// Media list entry
 | 
					/// Media list entry
 | 
				
			||||||
pub struct MediaListEntry {
 | 
					pub struct MediaListEntry {
 | 
				
			||||||
    /// Media changer ID
 | 
					    /// Media label text (or Barcode)
 | 
				
			||||||
    pub changer_id: String,
 | 
					    pub label_text: String,
 | 
				
			||||||
    /// Media Uuid
 | 
					    /// Media Uuid
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
    /// Creation time stamp
 | 
					    /// Creation time stamp
 | 
				
			||||||
@ -57,8 +57,8 @@ pub struct MediaListEntry {
 | 
				
			|||||||
pub struct MediaIdFlat {
 | 
					pub struct MediaIdFlat {
 | 
				
			||||||
    /// Unique ID
 | 
					    /// Unique ID
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
    /// Media Changer ID or Barcode
 | 
					    /// Media label text (or Barcode)
 | 
				
			||||||
    pub changer_id: String,
 | 
					    pub label_text: String,
 | 
				
			||||||
    /// Creation time stamp
 | 
					    /// Creation time stamp
 | 
				
			||||||
    pub ctime: i64,
 | 
					    pub ctime: i64,
 | 
				
			||||||
    // All MediaSet properties are optional here
 | 
					    // All MediaSet properties are optional here
 | 
				
			||||||
@ -81,8 +81,8 @@ pub struct MediaIdFlat {
 | 
				
			|||||||
#[serde(rename_all = "kebab-case")]
 | 
					#[serde(rename_all = "kebab-case")]
 | 
				
			||||||
/// Label with optional Uuid
 | 
					/// Label with optional Uuid
 | 
				
			||||||
pub struct LabelUuidMap {
 | 
					pub struct LabelUuidMap {
 | 
				
			||||||
    /// Changer ID (label)
 | 
					    /// Changer label text (or Barcode)
 | 
				
			||||||
    pub changer_id: String,
 | 
					    pub label_text: String,
 | 
				
			||||||
    /// Associated Uuid (if any)
 | 
					    /// Associated Uuid (if any)
 | 
				
			||||||
    pub uuid: Option<String>,
 | 
					    pub uuid: Option<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -92,8 +92,8 @@ pub struct LabelUuidMap {
 | 
				
			|||||||
#[serde(rename_all = "kebab-case")]
 | 
					#[serde(rename_all = "kebab-case")]
 | 
				
			||||||
/// Media content list entry
 | 
					/// Media content list entry
 | 
				
			||||||
pub struct MediaContentEntry {
 | 
					pub struct MediaContentEntry {
 | 
				
			||||||
    /// Media changer ID
 | 
					    /// Media label text (or Barcode)
 | 
				
			||||||
    pub changer_id: String,
 | 
					    pub label_text: String,
 | 
				
			||||||
    /// Media Uuid
 | 
					    /// Media Uuid
 | 
				
			||||||
    pub uuid: String,
 | 
					    pub uuid: String,
 | 
				
			||||||
    /// Media set name
 | 
					    /// Media set name
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ use proxmox_backup::{
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
        open_drive,
 | 
					        open_drive,
 | 
				
			||||||
        complete_media_changer_id,
 | 
					        complete_media_label_text,
 | 
				
			||||||
        complete_media_set_uuid,
 | 
					        complete_media_set_uuid,
 | 
				
			||||||
        file_formats::{
 | 
					        file_formats::{
 | 
				
			||||||
            PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
 | 
					            PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
 | 
				
			||||||
@ -210,7 +210,7 @@ async fn eject_media(
 | 
				
			|||||||
                schema: DRIVE_NAME_SCHEMA,
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
                optional: true,
 | 
					                optional: true,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -243,7 +243,7 @@ async fn load_media(
 | 
				
			|||||||
                schema: DRIVE_NAME_SCHEMA,
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
                optional: true,
 | 
					                optional: true,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -351,7 +351,7 @@ async fn unload_media(
 | 
				
			|||||||
                schema: DRIVE_NAME_SCHEMA,
 | 
					                schema: DRIVE_NAME_SCHEMA,
 | 
				
			||||||
                optional: true,
 | 
					                optional: true,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "changer-id": {
 | 
					            "label-text": {
 | 
				
			||||||
                schema: MEDIA_LABEL_SCHEMA,
 | 
					                schema: MEDIA_LABEL_SCHEMA,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
       },
 | 
					       },
 | 
				
			||||||
@ -411,7 +411,7 @@ async fn read_label(
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let options = default_table_format_options()
 | 
					    let options = default_table_format_options()
 | 
				
			||||||
        .column(ColumnConfig::new("changer-id"))
 | 
					        .column(ColumnConfig::new("label-text"))
 | 
				
			||||||
        .column(ColumnConfig::new("uuid"))
 | 
					        .column(ColumnConfig::new("uuid"))
 | 
				
			||||||
        .column(ColumnConfig::new("ctime").renderer(render_epoch))
 | 
					        .column(ColumnConfig::new("ctime").renderer(render_epoch))
 | 
				
			||||||
        .column(ColumnConfig::new("pool"))
 | 
					        .column(ColumnConfig::new("pool"))
 | 
				
			||||||
@ -487,7 +487,7 @@ async fn inventory(
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let options = default_table_format_options()
 | 
					    let options = default_table_format_options()
 | 
				
			||||||
        .column(ColumnConfig::new("changer-id"))
 | 
					        .column(ColumnConfig::new("label-text"))
 | 
				
			||||||
        .column(ColumnConfig::new("uuid"))
 | 
					        .column(ColumnConfig::new("uuid"))
 | 
				
			||||||
        ;
 | 
					        ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -950,9 +950,9 @@ fn main() {
 | 
				
			|||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "load-media",
 | 
					            "load-media",
 | 
				
			||||||
            CliCommand::new(&API_METHOD_LOAD_MEDIA)
 | 
					            CliCommand::new(&API_METHOD_LOAD_MEDIA)
 | 
				
			||||||
                .arg_param(&["changer-id"])
 | 
					                .arg_param(&["label-text"])
 | 
				
			||||||
                .completion_cb("drive", complete_drive_name)
 | 
					                .completion_cb("drive", complete_drive_name)
 | 
				
			||||||
                .completion_cb("changer-id", complete_media_changer_id)
 | 
					                .completion_cb("label-text", complete_media_label_text)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "load-media-from-slot",
 | 
					            "load-media-from-slot",
 | 
				
			||||||
@ -968,9 +968,9 @@ fn main() {
 | 
				
			|||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "export-media",
 | 
					            "export-media",
 | 
				
			||||||
            CliCommand::new(&API_METHOD_EXPORT_MEDIA)
 | 
					            CliCommand::new(&API_METHOD_EXPORT_MEDIA)
 | 
				
			||||||
                .arg_param(&["changer-id"])
 | 
					                .arg_param(&["label-text"])
 | 
				
			||||||
                .completion_cb("drive", complete_drive_name)
 | 
					                .completion_cb("drive", complete_drive_name)
 | 
				
			||||||
                .completion_cb("changer-id", complete_media_changer_id)
 | 
					                .completion_cb("label-text", complete_media_label_text)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        ;
 | 
					        ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -233,7 +233,7 @@ async fn get_status(
 | 
				
			|||||||
        _ => unreachable!(),
 | 
					        _ => unreachable!(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let render_changer_id = |value: &Value, _record: &Value| -> Result<String, Error> {
 | 
					    let render_label_text = |value: &Value, _record: &Value| -> Result<String, Error> {
 | 
				
			||||||
        if value.is_null() {
 | 
					        if value.is_null() {
 | 
				
			||||||
            return Ok(String::new());
 | 
					            return Ok(String::new());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -250,7 +250,7 @@ async fn get_status(
 | 
				
			|||||||
        .sortby("entry-id", false)
 | 
					        .sortby("entry-id", false)
 | 
				
			||||||
        .column(ColumnConfig::new("entry-kind"))
 | 
					        .column(ColumnConfig::new("entry-kind"))
 | 
				
			||||||
        .column(ColumnConfig::new("entry-id"))
 | 
					        .column(ColumnConfig::new("entry-id"))
 | 
				
			||||||
        .column(ColumnConfig::new("changer-id").renderer(render_changer_id))
 | 
					        .column(ColumnConfig::new("label-text").renderer(render_label_text))
 | 
				
			||||||
        .column(ColumnConfig::new("loaded-slot"))
 | 
					        .column(ColumnConfig::new("loaded-slot"))
 | 
				
			||||||
        ;
 | 
					        ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ use proxmox_backup::{
 | 
				
			|||||||
        tape::media::MediaContentListFilter,
 | 
					        tape::media::MediaContentListFilter,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    tape::{
 | 
					    tape::{
 | 
				
			||||||
        complete_media_changer_id,
 | 
					        complete_media_label_text,
 | 
				
			||||||
        complete_media_uuid,
 | 
					        complete_media_uuid,
 | 
				
			||||||
        complete_media_set_uuid,
 | 
					        complete_media_set_uuid,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -41,14 +41,14 @@ pub fn media_commands() -> CommandLineInterface {
 | 
				
			|||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "destroy",
 | 
					            "destroy",
 | 
				
			||||||
            CliCommand::new(&api2::tape::media::API_METHOD_DESTROY_MEDIA)
 | 
					            CliCommand::new(&api2::tape::media::API_METHOD_DESTROY_MEDIA)
 | 
				
			||||||
                .arg_param(&["changer-id"])
 | 
					                .arg_param(&["label-text"])
 | 
				
			||||||
                .completion_cb("changer-id", complete_media_changer_id)
 | 
					                .completion_cb("label-text", complete_media_label_text)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .insert(
 | 
					        .insert(
 | 
				
			||||||
            "content",
 | 
					            "content",
 | 
				
			||||||
            CliCommand::new(&API_METHOD_LIST_CONTENT)
 | 
					            CliCommand::new(&API_METHOD_LIST_CONTENT)
 | 
				
			||||||
                .completion_cb("pool", complete_pool_name)
 | 
					                .completion_cb("pool", complete_pool_name)
 | 
				
			||||||
                .completion_cb("changer-id", complete_media_changer_id)
 | 
					                .completion_cb("label-text", complete_media_label_text)
 | 
				
			||||||
                .completion_cb("media", complete_media_uuid)
 | 
					                .completion_cb("media", complete_media_uuid)
 | 
				
			||||||
                .completion_cb("media-set", complete_media_set_uuid)
 | 
					                .completion_cb("media-set", complete_media_set_uuid)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@ -116,8 +116,8 @@ async fn list_media(
 | 
				
			|||||||
        .sortby("pool", false)
 | 
					        .sortby("pool", false)
 | 
				
			||||||
        .sortby("media-set-uuid", false)
 | 
					        .sortby("media-set-uuid", false)
 | 
				
			||||||
        .sortby("seq-nr", false)
 | 
					        .sortby("seq-nr", false)
 | 
				
			||||||
        .sortby("changer-id", false)
 | 
					        .sortby("label-text", false)
 | 
				
			||||||
        .column(ColumnConfig::new("changer-id"))
 | 
					        .column(ColumnConfig::new("label-text"))
 | 
				
			||||||
        .column(ColumnConfig::new("pool"))
 | 
					        .column(ColumnConfig::new("pool"))
 | 
				
			||||||
        .column(ColumnConfig::new("media-set-name"))
 | 
					        .column(ColumnConfig::new("media-set-name"))
 | 
				
			||||||
        .column(ColumnConfig::new("seq-nr"))
 | 
					        .column(ColumnConfig::new("seq-nr"))
 | 
				
			||||||
@ -165,7 +165,7 @@ fn list_content(
 | 
				
			|||||||
        .sortby("seq-nr", false)
 | 
					        .sortby("seq-nr", false)
 | 
				
			||||||
        .sortby("snapshot", false)
 | 
					        .sortby("snapshot", false)
 | 
				
			||||||
        .sortby("backup-time", false)
 | 
					        .sortby("backup-time", false)
 | 
				
			||||||
        .column(ColumnConfig::new("changer-id"))
 | 
					        .column(ColumnConfig::new("label-text"))
 | 
				
			||||||
        .column(ColumnConfig::new("pool"))
 | 
					        .column(ColumnConfig::new("pool"))
 | 
				
			||||||
        .column(ColumnConfig::new("media-set-name"))
 | 
					        .column(ColumnConfig::new("media-set-name"))
 | 
				
			||||||
        .column(ColumnConfig::new("seq-nr"))
 | 
					        .column(ColumnConfig::new("seq-nr"))
 | 
				
			||||||
 | 
				
			|||||||
@ -5,18 +5,18 @@ use proxmox::tools::email::sendmail;
 | 
				
			|||||||
/// Send email to a person to request a manual media change
 | 
					/// Send email to a person to request a manual media change
 | 
				
			||||||
pub fn send_load_media_email(
 | 
					pub fn send_load_media_email(
 | 
				
			||||||
    drive: &str,
 | 
					    drive: &str,
 | 
				
			||||||
    changer_id: &str,
 | 
					    label_text: &str,
 | 
				
			||||||
    to: &str,
 | 
					    to: &str,
 | 
				
			||||||
) -> Result<(), Error> {
 | 
					) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let subject = format!("Load Media '{}' request for drive '{}'", changer_id, drive);
 | 
					    let subject = format!("Load Media '{}' request for drive '{}'", label_text, drive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut text = String::new();
 | 
					    let mut text = String::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    text.push_str("Please insert the requested media into the backup drive.\n\n");
 | 
					    text.push_str("Please insert the requested media into the backup drive.\n\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    text.push_str(&format!("Drive: {}\n", drive));
 | 
					    text.push_str(&format!("Drive: {}\n", drive));
 | 
				
			||||||
    text.push_str(&format!("Media: {}\n", changer_id));
 | 
					    text.push_str(&format!("Media: {}\n", label_text));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sendmail(
 | 
					    sendmail(
 | 
				
			||||||
        &[to],
 | 
					        &[to],
 | 
				
			||||||
 | 
				
			|||||||
@ -32,17 +32,17 @@ pub trait MediaChange {
 | 
				
			|||||||
    /// Load media from storage slot into drive
 | 
					    /// Load media from storage slot into drive
 | 
				
			||||||
    fn load_media_from_slot(&mut self, slot: u64) -> Result<(), Error>;
 | 
					    fn load_media_from_slot(&mut self, slot: u64) -> Result<(), Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Load media by changer-id into drive
 | 
					    /// Load media by label-text into drive
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This unloads first if the drive is already loaded with another media.
 | 
					    /// This unloads first if the drive is already loaded with another media.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// Note: This refuses to load media inside import/export
 | 
					    /// Note: This refuses to load media inside import/export
 | 
				
			||||||
    /// slots. Also, you cannot load cleaning units with this
 | 
					    /// slots. Also, you cannot load cleaning units with this
 | 
				
			||||||
    /// interface.
 | 
					    /// interface.
 | 
				
			||||||
    fn load_media(&mut self, changer_id: &str) -> Result<(), Error> {
 | 
					    fn load_media(&mut self, label_text: &str) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if changer_id.starts_with("CLN") {
 | 
					        if label_text.starts_with("CLN") {
 | 
				
			||||||
            bail!("unable to load media '{}' (seems top be a a cleaning units)", changer_id);
 | 
					            bail!("unable to load media '{}' (seems top be a a cleaning units)", label_text);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut status = self.status()?;
 | 
					        let mut status = self.status()?;
 | 
				
			||||||
@ -52,10 +52,10 @@ pub trait MediaChange {
 | 
				
			|||||||
        // already loaded?
 | 
					        // already loaded?
 | 
				
			||||||
        for (i, drive_status) in status.drives.iter().enumerate() {
 | 
					        for (i, drive_status) in status.drives.iter().enumerate() {
 | 
				
			||||||
            if let ElementStatus::VolumeTag(ref tag) = drive_status.status {
 | 
					            if let ElementStatus::VolumeTag(ref tag) = drive_status.status {
 | 
				
			||||||
                if *tag == changer_id {
 | 
					                if *tag == label_text {
 | 
				
			||||||
                    if i as u64 != self.drive_number() {
 | 
					                    if i as u64 != self.drive_number() {
 | 
				
			||||||
                        bail!("unable to load media '{}' - media in wrong drive ({} != {})",
 | 
					                        bail!("unable to load media '{}' - media in wrong drive ({} != {})",
 | 
				
			||||||
                              changer_id, i, self.drive_number());
 | 
					                              label_text, i, self.drive_number());
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    return Ok(()) // already loaded
 | 
					                    return Ok(()) // already loaded
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -76,9 +76,9 @@ pub trait MediaChange {
 | 
				
			|||||||
        let mut slot = None;
 | 
					        let mut slot = None;
 | 
				
			||||||
        for (i, (import_export, element_status)) in status.slots.iter().enumerate() {
 | 
					        for (i, (import_export, element_status)) in status.slots.iter().enumerate() {
 | 
				
			||||||
            if let ElementStatus::VolumeTag(tag) = element_status {
 | 
					            if let ElementStatus::VolumeTag(tag) = element_status {
 | 
				
			||||||
                if *tag == changer_id {
 | 
					                if *tag == label_text {
 | 
				
			||||||
                    if *import_export {
 | 
					                    if *import_export {
 | 
				
			||||||
                        bail!("unable to load media '{}' - inside import/export slot", changer_id);
 | 
					                        bail!("unable to load media '{}' - inside import/export slot", label_text);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    slot = Some(i+1);
 | 
					                    slot = Some(i+1);
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
@ -87,7 +87,7 @@ pub trait MediaChange {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let slot = match slot {
 | 
					        let slot = match slot {
 | 
				
			||||||
            None => bail!("unable to find media '{}' (offline?)", changer_id),
 | 
					            None => bail!("unable to find media '{}' (offline?)", label_text),
 | 
				
			||||||
            Some(slot) => slot,
 | 
					            Some(slot) => slot,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -97,11 +97,11 @@ pub trait MediaChange {
 | 
				
			|||||||
    /// Unload media from drive (eject media if necessary)
 | 
					    /// Unload media from drive (eject media if necessary)
 | 
				
			||||||
    fn unload_media(&mut self, target_slot: Option<u64>) -> Result<(), Error>;
 | 
					    fn unload_media(&mut self, target_slot: Option<u64>) -> Result<(), Error>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// List online media changer IDs (barcodes)
 | 
					    /// List online media labels (label_text/barcodes)
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// List acessible (online) changer IDs. This does not include
 | 
					    /// List acessible (online) label texts. This does not include
 | 
				
			||||||
    /// media inside import-export slots or cleaning media.
 | 
					    /// media inside import-export slots or cleaning media.
 | 
				
			||||||
    fn online_media_changer_ids(&mut self) -> Result<Vec<String>, Error> {
 | 
					    fn online_media_label_texts(&mut self) -> Result<Vec<String>, Error> {
 | 
				
			||||||
        let status = self.status()?;
 | 
					        let status = self.status()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut list = Vec::new();
 | 
					        let mut list = Vec::new();
 | 
				
			||||||
@ -166,13 +166,13 @@ pub trait MediaChange {
 | 
				
			|||||||
    /// By moving the media to an empty import-export slot. Returns
 | 
					    /// By moving the media to an empty import-export slot. Returns
 | 
				
			||||||
    /// Some(slot) if the media was exported. Returns None if the media is
 | 
					    /// Some(slot) if the media was exported. Returns None if the media is
 | 
				
			||||||
    /// not online (already exported).
 | 
					    /// not online (already exported).
 | 
				
			||||||
    fn export_media(&mut self, changer_id: &str) -> Result<Option<u64>, Error> {
 | 
					    fn export_media(&mut self, label_text: &str) -> Result<Option<u64>, Error> {
 | 
				
			||||||
        let status = self.status()?;
 | 
					        let status = self.status()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut unload_from_drive = false;
 | 
					        let mut unload_from_drive = false;
 | 
				
			||||||
        if let Some(drive_status) = status.drives.get(self.drive_number() as usize) {
 | 
					        if let Some(drive_status) = status.drives.get(self.drive_number() as usize) {
 | 
				
			||||||
            if let ElementStatus::VolumeTag(ref tag) = drive_status.status {
 | 
					            if let ElementStatus::VolumeTag(ref tag) = drive_status.status {
 | 
				
			||||||
                if tag == changer_id {
 | 
					                if tag == label_text {
 | 
				
			||||||
                    unload_from_drive = true;
 | 
					                    unload_from_drive = true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -189,7 +189,7 @@ pub trait MediaChange {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if let ElementStatus::VolumeTag(ref tag) = element_status {
 | 
					                if let ElementStatus::VolumeTag(ref tag) = element_status {
 | 
				
			||||||
                    if tag == changer_id {
 | 
					                    if tag == label_text {
 | 
				
			||||||
                        from = Some(i as u64 + 1);
 | 
					                        from = Some(i as u64 + 1);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -269,7 +269,7 @@ pub fn request_and_load_media(
 | 
				
			|||||||
        if let Ok(Some(media_id)) = handle.read_label() {
 | 
					        if let Ok(Some(media_id)) = handle.read_label() {
 | 
				
			||||||
            worker.log(format!(
 | 
					            worker.log(format!(
 | 
				
			||||||
                "found media label {} ({})",
 | 
					                "found media label {} ({})",
 | 
				
			||||||
                media_id.label.changer_id,
 | 
					                media_id.label.label_text,
 | 
				
			||||||
                media_id.label.uuid.to_string(),
 | 
					                media_id.label.uuid.to_string(),
 | 
				
			||||||
            ));
 | 
					            ));
 | 
				
			||||||
            if media_id.label.uuid == *uuid {
 | 
					            if media_id.label.uuid == *uuid {
 | 
				
			||||||
@ -285,9 +285,9 @@ pub fn request_and_load_media(
 | 
				
			|||||||
                "virtual" => {
 | 
					                "virtual" => {
 | 
				
			||||||
                    let mut tape = VirtualTapeDrive::deserialize(config)?;
 | 
					                    let mut tape = VirtualTapeDrive::deserialize(config)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let changer_id = label.changer_id.clone();
 | 
					                    let label_text = label.label_text.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    tape.load_media(&changer_id)?;
 | 
					                    tape.load_media(&label_text)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let mut handle: Box<dyn TapeDriver> = Box::new(tape.open()?);
 | 
					                    let mut handle: Box<dyn TapeDriver> = Box::new(tape.open()?);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -298,12 +298,12 @@ pub fn request_and_load_media(
 | 
				
			|||||||
                "linux" => {
 | 
					                "linux" => {
 | 
				
			||||||
                    let drive_config = LinuxTapeDrive::deserialize(config)?;
 | 
					                    let drive_config = LinuxTapeDrive::deserialize(config)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let changer_id = label.changer_id.clone();
 | 
					                    let label_text = label.label_text.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if drive_config.changer.is_some() {
 | 
					                    if drive_config.changer.is_some() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        let mut changer = MtxMediaChanger::with_drive_config(&drive_config)?;
 | 
					                        let mut changer = MtxMediaChanger::with_drive_config(&drive_config)?;
 | 
				
			||||||
                        changer.load_media(&changer_id)?;
 | 
					                        changer.load_media(&label_text)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        let mut handle: Box<dyn TapeDriver> = Box::new(drive_config.open()?);
 | 
					                        let mut handle: Box<dyn TapeDriver> = Box::new(drive_config.open()?);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -312,11 +312,11 @@ pub fn request_and_load_media(
 | 
				
			|||||||
                        return Ok((handle, media_id));
 | 
					                        return Ok((handle, media_id));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    worker.log(format!("Please insert media '{}' into drive '{}'", changer_id, drive));
 | 
					                    worker.log(format!("Please insert media '{}' into drive '{}'", label_text, drive));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let to = "root@localhost"; // fixme
 | 
					                    let to = "root@localhost"; // fixme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    send_load_media_email(drive, &changer_id, to)?;
 | 
					                    send_load_media_email(drive, &label_text, to)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let mut last_media_uuid = None;
 | 
					                    let mut last_media_uuid = None;
 | 
				
			||||||
                    let mut last_error = None;
 | 
					                    let mut last_error = None;
 | 
				
			||||||
@ -340,7 +340,7 @@ pub fn request_and_load_media(
 | 
				
			|||||||
                                if media_id.label.uuid == label.uuid {
 | 
					                                if media_id.label.uuid == label.uuid {
 | 
				
			||||||
                                    worker.log(format!(
 | 
					                                    worker.log(format!(
 | 
				
			||||||
                                        "found media label {} ({})",
 | 
					                                        "found media label {} ({})",
 | 
				
			||||||
                                        media_id.label.changer_id,
 | 
					                                        media_id.label.label_text,
 | 
				
			||||||
                                        media_id.label.uuid.to_string(),
 | 
					                                        media_id.label.uuid.to_string(),
 | 
				
			||||||
                                    ));
 | 
					                                    ));
 | 
				
			||||||
                                    return Ok((Box::new(handle), media_id));
 | 
					                                    return Ok((Box::new(handle), media_id));
 | 
				
			||||||
@ -348,7 +348,7 @@ pub fn request_and_load_media(
 | 
				
			|||||||
                                    if Some(media_id.label.uuid.clone()) != last_media_uuid {
 | 
					                                    if Some(media_id.label.uuid.clone()) != last_media_uuid {
 | 
				
			||||||
                                        worker.log(format!(
 | 
					                                        worker.log(format!(
 | 
				
			||||||
                                            "wrong media label {} ({})",
 | 
					                                            "wrong media label {} ({})",
 | 
				
			||||||
                                            media_id.label.changer_id,
 | 
					                                            media_id.label.label_text,
 | 
				
			||||||
                                            media_id.label.uuid.to_string(),
 | 
					                                            media_id.label.uuid.to_string(),
 | 
				
			||||||
                                        ));
 | 
					                                        ));
 | 
				
			||||||
                                        last_media_uuid = Some(media_id.label.uuid);
 | 
					                                        last_media_uuid = Some(media_id.label.uuid);
 | 
				
			||||||
 | 
				
			|||||||
@ -157,7 +157,7 @@ impl VirtualTapeHandle {
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn online_media_changer_ids(&self) -> Result<Vec<String>, Error> {
 | 
					    fn online_media_label_texts(&self) -> Result<Vec<String>, Error> {
 | 
				
			||||||
        let mut list = Vec::new();
 | 
					        let mut list = Vec::new();
 | 
				
			||||||
        for entry in std::fs::read_dir(&self.path)? {
 | 
					        for entry in std::fs::read_dir(&self.path)? {
 | 
				
			||||||
            let entry = entry?;
 | 
					            let entry = entry?;
 | 
				
			||||||
@ -389,12 +389,12 @@ impl MediaChange for VirtualTapeHandle {
 | 
				
			|||||||
        // slot-assignment here.
 | 
					        // slot-assignment here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut slots = Vec::new();
 | 
					        let mut slots = Vec::new();
 | 
				
			||||||
        let changer_ids = self.online_media_changer_ids()?;
 | 
					        let label_texts = self.online_media_label_texts()?;
 | 
				
			||||||
        let max_slots = ((changer_ids.len() + 7)/8) * 8;
 | 
					        let max_slots = ((label_texts.len() + 7)/8) * 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i in 0..max_slots {
 | 
					        for i in 0..max_slots {
 | 
				
			||||||
            if let Some(changer_id) = changer_ids.get(i) {
 | 
					            if let Some(label_text) = label_texts.get(i) {
 | 
				
			||||||
                slots.push((false,  ElementStatus::VolumeTag(changer_id.clone())));
 | 
					                slots.push((false,  ElementStatus::VolumeTag(label_text.clone())));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                slots.push((false,  ElementStatus::Empty));
 | 
					                slots.push((false,  ElementStatus::Empty));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -407,7 +407,7 @@ impl MediaChange for VirtualTapeHandle {
 | 
				
			|||||||
        bail!("media tranfer is not implemented!");
 | 
					        bail!("media tranfer is not implemented!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn export_media(&mut self, _changer_id: &str) -> Result<Option<u64>, Error> {
 | 
					    fn export_media(&mut self, _label_text: &str) -> Result<Option<u64>, Error> {
 | 
				
			||||||
        bail!("media export is not implemented!");
 | 
					        bail!("media export is not implemented!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -416,13 +416,13 @@ impl MediaChange for VirtualTapeHandle {
 | 
				
			|||||||
            bail!("invalid slot ID {}", slot);
 | 
					            bail!("invalid slot ID {}", slot);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let changer_ids = self.online_media_changer_ids()?;
 | 
					        let label_texts = self.online_media_label_texts()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if slot > changer_ids.len() as u64 {
 | 
					        if slot > label_texts.len() as u64 {
 | 
				
			||||||
            bail!("slot {} is empty", slot);
 | 
					            bail!("slot {} is empty", slot);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load_media(&changer_ids[slot as usize - 1])
 | 
					        self.load_media(&label_texts[slot as usize - 1])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Try to load media
 | 
					    /// Try to load media
 | 
				
			||||||
@ -479,9 +479,9 @@ impl MediaChange for VirtualTapeDrive {
 | 
				
			|||||||
        handle.transfer_media(from, to)
 | 
					        handle.transfer_media(from, to)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn export_media(&mut self, changer_id: &str) -> Result<Option<u64>, Error> {
 | 
					    fn export_media(&mut self, label_text: &str) -> Result<Option<u64>, Error> {
 | 
				
			||||||
        let mut handle = self.open()?;
 | 
					        let mut handle = self.open()?;
 | 
				
			||||||
        handle.export_media(changer_id)
 | 
					        handle.export_media(label_text)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn load_media_from_slot(&mut self, slot: u64) -> Result<(), Error> {
 | 
					    fn load_media_from_slot(&mut self, slot: u64) -> Result<(), Error> {
 | 
				
			||||||
@ -489,9 +489,9 @@ impl MediaChange for VirtualTapeDrive {
 | 
				
			|||||||
        handle.load_media_from_slot(slot)
 | 
					        handle.load_media_from_slot(slot)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn load_media(&mut self, changer_id: &str) -> Result<(), Error> {
 | 
					    fn load_media(&mut self, label_text: &str) -> Result<(), Error> {
 | 
				
			||||||
        let mut handle = self.open()?;
 | 
					        let mut handle = self.open()?;
 | 
				
			||||||
        handle.load_media(changer_id)
 | 
					        handle.load_media(label_text)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn unload_media(&mut self, target_slot: Option<u64>) -> Result<(), Error> {
 | 
					    fn unload_media(&mut self, target_slot: Option<u64>) -> Result<(), Error> {
 | 
				
			||||||
@ -500,9 +500,9 @@ impl MediaChange for VirtualTapeDrive {
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn online_media_changer_ids(&mut self) -> Result<Vec<String>, Error> {
 | 
					    fn online_media_label_texts(&mut self) -> Result<Vec<String>, Error> {
 | 
				
			||||||
        let handle = self.open()?;
 | 
					        let handle = self.open()?;
 | 
				
			||||||
        handle.online_media_changer_ids()
 | 
					        handle.online_media_label_texts()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn clean_drive(&mut self) -> Result<(), Error> {
 | 
					    fn clean_drive(&mut self) -> Result<(), Error> {
 | 
				
			||||||
 | 
				
			|||||||
@ -164,8 +164,8 @@ pub struct ChunkArchiveEntryHeader {
 | 
				
			|||||||
pub struct MediaLabel {
 | 
					pub struct MediaLabel {
 | 
				
			||||||
    /// Unique ID
 | 
					    /// Unique ID
 | 
				
			||||||
    pub uuid: Uuid,
 | 
					    pub uuid: Uuid,
 | 
				
			||||||
    /// Media Changer ID or Barcode
 | 
					    /// Media label text (or Barcode)
 | 
				
			||||||
    pub changer_id: String,
 | 
					    pub label_text: String,
 | 
				
			||||||
    /// Creation time stamp
 | 
					    /// Creation time stamp
 | 
				
			||||||
    pub ctime: i64,
 | 
					    pub ctime: i64,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -213,10 +213,10 @@ impl Inventory {
 | 
				
			|||||||
        self.map.get(uuid).map(|entry| &entry.id)
 | 
					        self.map.get(uuid).map(|entry| &entry.id)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// find media by changer_id
 | 
					    /// find media by label_text
 | 
				
			||||||
    pub fn find_media_by_changer_id(&self, changer_id: &str) -> Option<&MediaId> {
 | 
					    pub fn find_media_by_label_text(&self, label_text: &str) -> Option<&MediaId> {
 | 
				
			||||||
        for (_uuid, entry) in &self.map {
 | 
					        for (_uuid, entry) in &self.map {
 | 
				
			||||||
            if entry.id.label.changer_id == changer_id {
 | 
					            if entry.id.label.label_text == label_text {
 | 
				
			||||||
                return Some(&entry.id);
 | 
					                return Some(&entry.id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -534,10 +534,10 @@ impl Inventory {
 | 
				
			|||||||
    // Helpers to simplify testing
 | 
					    // Helpers to simplify testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Genreate and insert a new free tape (test helper)
 | 
					    /// Genreate and insert a new free tape (test helper)
 | 
				
			||||||
    pub fn generate_free_tape(&mut self, changer_id: &str, ctime: i64) -> Uuid {
 | 
					    pub fn generate_free_tape(&mut self, label_text: &str, ctime: i64) -> Uuid {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let label = MediaLabel {
 | 
					        let label = MediaLabel {
 | 
				
			||||||
            changer_id: changer_id.to_string(),
 | 
					            label_text: label_text.to_string(),
 | 
				
			||||||
            uuid: Uuid::generate(),
 | 
					            uuid: Uuid::generate(),
 | 
				
			||||||
            ctime,
 | 
					            ctime,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -552,13 +552,13 @@ impl Inventory {
 | 
				
			|||||||
    /// (test helper)
 | 
					    /// (test helper)
 | 
				
			||||||
    pub fn generate_assigned_tape(
 | 
					    pub fn generate_assigned_tape(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        changer_id: &str,
 | 
					        label_text: &str,
 | 
				
			||||||
        pool: &str,
 | 
					        pool: &str,
 | 
				
			||||||
        ctime: i64,
 | 
					        ctime: i64,
 | 
				
			||||||
    ) -> Uuid {
 | 
					    ) -> Uuid {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let label = MediaLabel {
 | 
					        let label = MediaLabel {
 | 
				
			||||||
            changer_id: changer_id.to_string(),
 | 
					            label_text: label_text.to_string(),
 | 
				
			||||||
            uuid: Uuid::generate(),
 | 
					            uuid: Uuid::generate(),
 | 
				
			||||||
            ctime,
 | 
					            ctime,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -575,12 +575,12 @@ impl Inventory {
 | 
				
			|||||||
    /// Genreate and insert a used tape (test helper)
 | 
					    /// Genreate and insert a used tape (test helper)
 | 
				
			||||||
    pub fn generate_used_tape(
 | 
					    pub fn generate_used_tape(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        changer_id: &str,
 | 
					        label_text: &str,
 | 
				
			||||||
        set: MediaSetLabel,
 | 
					        set: MediaSetLabel,
 | 
				
			||||||
        ctime: i64,
 | 
					        ctime: i64,
 | 
				
			||||||
    ) -> Uuid {
 | 
					    ) -> Uuid {
 | 
				
			||||||
        let label = MediaLabel {
 | 
					        let label = MediaLabel {
 | 
				
			||||||
            changer_id: changer_id.to_string(),
 | 
					            label_text: label_text.to_string(),
 | 
				
			||||||
            uuid: Uuid::generate(),
 | 
					            uuid: Uuid::generate(),
 | 
				
			||||||
            ctime,
 | 
					            ctime,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
@ -735,7 +735,7 @@ pub fn complete_media_set_uuid(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// List of known media labels (barcodes)
 | 
					/// List of known media labels (barcodes)
 | 
				
			||||||
pub fn complete_media_changer_id(
 | 
					pub fn complete_media_label_text(
 | 
				
			||||||
    _arg: &str,
 | 
					    _arg: &str,
 | 
				
			||||||
    _param: &HashMap<String, String>,
 | 
					    _param: &HashMap<String, String>,
 | 
				
			||||||
) -> Vec<String> {
 | 
					) -> Vec<String> {
 | 
				
			||||||
@ -745,5 +745,5 @@ pub fn complete_media_changer_id(
 | 
				
			|||||||
        Err(_) => return Vec::new(),
 | 
					        Err(_) => return Vec::new(),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inventory.map.values().map(|entry| entry.id.label.changer_id.clone()).collect()
 | 
					    inventory.map.values().map(|entry| entry.id.label.label_text.clone()).collect()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -317,7 +317,7 @@ impl MediaPool {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if self.media_is_expired(&media, current_time) {
 | 
					            if self.media_is_expired(&media, current_time) {
 | 
				
			||||||
                println!("found expired media on media '{}'", media.changer_id());
 | 
					                println!("found expired media on media '{}'", media.label_text());
 | 
				
			||||||
                expired_media.push(media);
 | 
					                expired_media.push(media);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -335,7 +335,7 @@ impl MediaPool {
 | 
				
			|||||||
                bail!("alloc writable media in pool '{}' failed: no usable media found", self.name());
 | 
					                bail!("alloc writable media in pool '{}' failed: no usable media found", self.name());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Some(media) => {
 | 
					            Some(media) => {
 | 
				
			||||||
                println!("reuse expired media '{}'", media.changer_id());
 | 
					                println!("reuse expired media '{}'", media.label_text());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let seq_nr = self.current_media_set.media_list().len() as u64;
 | 
					                let seq_nr = self.current_media_set.media_list().len() as u64;
 | 
				
			||||||
                let set = MediaSetLabel::with_data(&pool, self.current_media_set.uuid().clone(), seq_nr, current_time);
 | 
					                let set = MediaSetLabel::with_data(&pool, self.current_media_set.uuid().clone(), seq_nr, current_time);
 | 
				
			||||||
@ -494,7 +494,7 @@ impl BackupMedia {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Returns the media label (Barcode)
 | 
					    /// Returns the media label (Barcode)
 | 
				
			||||||
    pub fn changer_id(&self) -> &str {
 | 
					    pub fn label_text(&self) -> &str {
 | 
				
			||||||
        &self.id.label.changer_id
 | 
					        &self.id.label.label_text
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -99,8 +99,8 @@ pub fn mtx_status_to_online_set(status: &MtxStatus, inventory: &Inventory) -> Ha
 | 
				
			|||||||
    let mut online_set = HashSet::new();
 | 
					    let mut online_set = HashSet::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for drive_status in status.drives.iter() {
 | 
					    for drive_status in status.drives.iter() {
 | 
				
			||||||
        if let ElementStatus::VolumeTag(ref changer_id) = drive_status.status {
 | 
					        if let ElementStatus::VolumeTag(ref label_text) = drive_status.status {
 | 
				
			||||||
            if let Some(media_id) = inventory.find_media_by_changer_id(changer_id) {
 | 
					            if let Some(media_id) = inventory.find_media_by_label_text(label_text) {
 | 
				
			||||||
                online_set.insert(media_id.label.uuid.clone());
 | 
					                online_set.insert(media_id.label.uuid.clone());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -108,8 +108,8 @@ pub fn mtx_status_to_online_set(status: &MtxStatus, inventory: &Inventory) -> Ha
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    for (import_export, slot_status) in status.slots.iter() {
 | 
					    for (import_export, slot_status) in status.slots.iter() {
 | 
				
			||||||
        if *import_export { continue; }
 | 
					        if *import_export { continue; }
 | 
				
			||||||
        if let ElementStatus::VolumeTag(ref changer_id) = slot_status {
 | 
					        if let ElementStatus::VolumeTag(ref label_text) = slot_status {
 | 
				
			||||||
            if let Some(media_id) = inventory.find_media_by_changer_id(changer_id) {
 | 
					            if let Some(media_id) = inventory.find_media_by_label_text(label_text) {
 | 
				
			||||||
                online_set.insert(media_id.label.uuid.clone());
 | 
					                online_set.insert(media_id.label.uuid.clone());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -146,7 +146,7 @@ pub fn update_online_status(state_path: &Path) -> Result<OnlineStatusMap, Error>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let vtapes: Vec<VirtualTapeDrive> = config.convert_to_typed_array("virtual")?;
 | 
					    let vtapes: Vec<VirtualTapeDrive> = config.convert_to_typed_array("virtual")?;
 | 
				
			||||||
    for mut vtape in vtapes {
 | 
					    for mut vtape in vtapes {
 | 
				
			||||||
        let media_list = match vtape.online_media_changer_ids() {
 | 
					        let media_list = match vtape.online_media_label_texts() {
 | 
				
			||||||
            Ok(media_list) => media_list,
 | 
					            Ok(media_list) => media_list,
 | 
				
			||||||
            Err(err) => {
 | 
					            Err(err) => {
 | 
				
			||||||
                eprintln!("unable to get changer '{}' status - {}", vtape.name, err);
 | 
					                eprintln!("unable to get changer '{}' status - {}", vtape.name, err);
 | 
				
			||||||
@ -155,8 +155,8 @@ pub fn update_online_status(state_path: &Path) -> Result<OnlineStatusMap, Error>
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut online_set = HashSet::new();
 | 
					        let mut online_set = HashSet::new();
 | 
				
			||||||
        for changer_id in media_list {
 | 
					        for label_text in media_list {
 | 
				
			||||||
            if let Some(media_id) = inventory.find_media_by_changer_id(&changer_id) {
 | 
					            if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
 | 
				
			||||||
                online_set.insert(media_id.label.uuid.clone());
 | 
					                online_set.insert(media_id.label.uuid.clone());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -173,13 +173,13 @@ pub fn update_changer_online_status(
 | 
				
			|||||||
    drive_config: &SectionConfigData,
 | 
					    drive_config: &SectionConfigData,
 | 
				
			||||||
    inventory: &mut Inventory,
 | 
					    inventory: &mut Inventory,
 | 
				
			||||||
    changer_name: &str,
 | 
					    changer_name: &str,
 | 
				
			||||||
    changer_id_list: &Vec<String>,
 | 
					    label_text_list: &Vec<String>,
 | 
				
			||||||
) -> Result<(), Error> {
 | 
					) -> Result<(), Error> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut online_map = OnlineStatusMap::new(drive_config)?;
 | 
					    let mut online_map = OnlineStatusMap::new(drive_config)?;
 | 
				
			||||||
    let mut online_set = HashSet::new();
 | 
					    let mut online_set = HashSet::new();
 | 
				
			||||||
    for changer_id in changer_id_list.iter() {
 | 
					    for label_text in label_text_list.iter() {
 | 
				
			||||||
        if let Some(media_id) = inventory.find_media_by_changer_id(&changer_id) {
 | 
					        if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
 | 
				
			||||||
            online_set.insert(media_id.label.uuid.clone());
 | 
					            online_set.insert(media_id.label.uuid.clone());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -150,11 +150,11 @@ impl PoolWriter {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            for media_uuid in self.pool.current_media_list()? {
 | 
					            for media_uuid in self.pool.current_media_list()? {
 | 
				
			||||||
                let media = self.pool.lookup_media(media_uuid)?;
 | 
					                let media = self.pool.lookup_media(media_uuid)?;
 | 
				
			||||||
                let changer_id = media.changer_id();
 | 
					                let label_text = media.label_text();
 | 
				
			||||||
                if let Some(slot) = changer.export_media(changer_id)? {
 | 
					                if let Some(slot) = changer.export_media(label_text)? {
 | 
				
			||||||
                    worker.log(format!("exported media '{}' to import/export slot {}", changer_id, slot));
 | 
					                    worker.log(format!("exported media '{}' to import/export slot {}", label_text, slot));
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    worker.warn(format!("export failed - media '{}' is not online", changer_id));
 | 
					                    worker.warn(format!("export failed - media '{}' is not online", label_text));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user