From 6dbad5b4b54e2f06bfeaa691c6c5cb5d5b385208 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 11 Dec 2020 09:10:22 +0100 Subject: [PATCH] tape: run label commands as WorkerTask (threads) --- src/api2/tape/drive.rs | 107 +++++++++++++++++++++++++++++----------- src/bin/proxmox-tape.rs | 34 +++++++++++-- www/Utils.js | 2 + 3 files changed, 110 insertions(+), 33 deletions(-) diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs index 784b24a6..4e9fea6f 100644 --- a/src/api2/tape/drive.rs +++ b/src/api2/tape/drive.rs @@ -1,4 +1,6 @@ use std::path::Path; +use std::sync::Arc; + use anyhow::{bail, Error}; use serde_json::Value; @@ -10,6 +12,7 @@ use proxmox::{ sys::error::SysError, api::{ api, + RpcEnvironment, Router, SubdirMap, }, @@ -18,15 +21,18 @@ use proxmox::{ use crate::{ config, api2::types::{ + UPID_SCHEMA, DRIVE_ID_SCHEMA, MEDIA_LABEL_SCHEMA, MEDIA_POOL_NAME_SCHEMA, + Authid, LinuxTapeDrive, ScsiTapeChanger, TapeDeviceInfo, MediaLabelInfoFlat, LabelUuidMap, }, + server::WorkerTask, tape::{ TAPE_STATUS_DIR, TapeDriver, @@ -255,6 +261,9 @@ pub fn eject_media(drive: String) -> Result<(), Error> { }, }, }, + returns: { + schema: UPID_SCHEMA, + }, )] /// Label media /// @@ -266,7 +275,10 @@ pub fn label_media( drive: String, pool: Option, changer_id: String, -) -> Result<(), Error> { + rpcenv: &mut dyn RpcEnvironment, +) -> Result { + + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; if let Some(ref pool) = pool { let (pool_config, _digest) = config::media_pool::config()?; @@ -278,33 +290,45 @@ pub fn label_media( let (config, _digest) = config::drive::config()?; - let mut drive = open_drive(&config, &drive)?; + let upid_str = WorkerTask::new_thread( + "label-media", + Some(drive.clone()), + auth_id, + true, + move |worker| { - drive.rewind()?; + let mut drive = open_drive(&config, &drive)?; - match drive.read_next_file() { - Ok(Some(_file)) => bail!("media is not empty (erase first)"), - Ok(None) => { /* EOF mark at BOT, assume tape is empty */ }, - Err(err) => { - if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) { - /* assume tape is empty */ - } else { - bail!("media read error - {}", err); + drive.rewind()?; + + match drive.read_next_file() { + Ok(Some(_file)) => bail!("media is not empty (erase first)"), + Ok(None) => { /* EOF mark at BOT, assume tape is empty */ }, + Err(err) => { + if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) { + /* assume tape is empty */ + } else { + bail!("media read error - {}", err); + } + } } + + let ctime = proxmox::tools::time::epoch_i64(); + let label = DriveLabel { + changer_id: changer_id.to_string(), + uuid: Uuid::generate(), + ctime, + }; + + write_media_label(worker, &mut drive, label, pool) } - } + )?; - let ctime = proxmox::tools::time::epoch_i64(); - let label = DriveLabel { - changer_id: changer_id.to_string(), - uuid: Uuid::generate(), - ctime, - }; - - write_media_label(&mut drive, label, pool) + Ok(upid_str.into()) } fn write_media_label( + worker: Arc, drive: &mut Box, label: DriveLabel, pool: Option, @@ -316,13 +340,13 @@ fn write_media_label( if let Some(ref pool) = pool { // assign media to pool by writing special media set label - println!("Label media '{}' for pool '{}'", label.changer_id, pool); + worker.log(format!("Label media '{}' for pool '{}'", label.changer_id, pool)); let set = MediaSetLabel::with_data(&pool, [0u8; 16].into(), 0, label.ctime); drive.write_media_set_label(&set)?; media_set_label = Some(set); } else { - println!("Label media '{}' (no pool assignment)", label.changer_id); + worker.log(format!("Label media '{}' (no pool assignment)", label.changer_id)); } let media_id = MediaId { label, media_set_label }; @@ -534,12 +558,16 @@ pub fn inventory( }, }, }, + returns: { + schema: UPID_SCHEMA, + }, )] /// Label media with barcodes from changer device pub fn barcode_label_media( drive: String, pool: Option, -) -> Result<(), Error> { + rpcenv: &mut dyn RpcEnvironment, +) -> Result { if let Some(ref pool) = pool { let (pool_config, _digest) = config::media_pool::config()?; @@ -549,6 +577,27 @@ pub fn barcode_label_media( } } + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + + let upid_str = WorkerTask::new_thread( + "barcode-label-media", + Some(drive.clone()), + auth_id, + true, + move |worker| { + barcode_label_media_worker(worker, drive, pool) + } + )?; + + Ok(upid_str.into()) +} + +fn barcode_label_media_worker( + worker: Arc, + drive: String, + pool: Option, +) -> Result<(), Error> { + let (config, _digest) = config::drive::config()?; let (mut changer, changer_name) = media_changer(&config, &drive, false)?; @@ -571,14 +620,14 @@ pub fn barcode_label_media( inventory.reload()?; if inventory.find_media_by_changer_id(&changer_id).is_some() { - println!("media '{}' already inventoried (already labeled)", changer_id); + worker.log(format!("media '{}' already inventoried (already labeled)", changer_id)); continue; } - println!("checking/loading media '{}'", changer_id); + worker.log(format!("checking/loading media '{}'", changer_id)); if let Err(err) = changer.load_media(&changer_id) { - eprintln!("unable to load media '{}' - {}", changer_id, err); + worker.warn(format!("unable to load media '{}' - {}", changer_id, err)); continue; } @@ -587,7 +636,7 @@ pub fn barcode_label_media( match drive.read_next_file() { Ok(Some(_file)) => { - println!("media '{}' is not empty (erase first)", changer_id); + worker.log(format!("media '{}' is not empty (erase first)", changer_id)); continue; } Ok(None) => { /* EOF mark at BOT, assume tape is empty */ }, @@ -595,7 +644,7 @@ pub fn barcode_label_media( if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) { /* assume tape is empty */ } else { - println!("media '{}' read error (maybe not empty - erase first)", changer_id); + worker.warn(format!("media '{}' read error (maybe not empty - erase first)", changer_id)); continue; } } @@ -608,7 +657,7 @@ pub fn barcode_label_media( ctime, }; - write_media_label(&mut drive, label, pool.clone())? + write_media_label(worker.clone(), &mut drive, label, pool.clone())? } Ok(()) diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs index c97fed53..b4aaa0fa 100644 --- a/src/bin/proxmox-tape.rs +++ b/src/bin/proxmox-tape.rs @@ -13,6 +13,10 @@ use proxmox::{ use proxmox_backup::{ tools::format::render_epoch, + server::{ + UPID, + worker_is_active_local, + }, api2::{ self, types::{ @@ -34,6 +38,24 @@ use proxmox_backup::{ mod proxmox_tape; use proxmox_tape::*; +// Note: local workers should print logs to stdout, so there is no need +// to fetch/display logs. We just wait for the worker to finish. +pub async fn wait_for_local_worker(upid_str: &str) -> Result<(), Error> { + + let upid: UPID = upid_str.parse()?; + + let sleep_duration = core::time::Duration::new(0, 100_000_000); + + loop { + if worker_is_active_local(&upid) { + tokio::time::delay_for(sleep_duration).await; + } else { + break; + } + } + Ok(()) +} + fn lookup_drive_name( param: &Value, config: &SectionConfigData, @@ -211,7 +233,7 @@ fn load_media( }, )] /// Label media -fn label_media( +async fn label_media( mut param: Value, rpcenv: &mut dyn RpcEnvironment, ) -> Result<(), Error> { @@ -222,11 +244,13 @@ fn label_media( let info = &api2::tape::drive::API_METHOD_LABEL_MEDIA; - match info.handler { + let result = match info.handler { ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?, _ => unreachable!(), }; + wait_for_local_worker(result.as_str().unwrap()).await?; + Ok(()) } @@ -341,7 +365,7 @@ fn inventory( }, )] /// Label media with barcodes from changer device -fn barcode_label_media( +async fn barcode_label_media( mut param: Value, rpcenv: &mut dyn RpcEnvironment, ) -> Result<(), Error> { @@ -352,11 +376,13 @@ fn barcode_label_media( let info = &api2::tape::drive::API_METHOD_BARCODE_LABEL_MEDIA; - match info.handler { + let result = match info.handler { ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?, _ => unreachable!(), }; + wait_for_local_worker(result.as_str().unwrap()).await?; + Ok(()) } diff --git a/www/Utils.js b/www/Utils.js index 775c03d7..a7e9c5fb 100644 --- a/www/Utils.js +++ b/www/Utils.js @@ -274,9 +274,11 @@ Ext.define('PBS.Utils', { // do whatever you want here Proxmox.Utils.override_task_descriptions({ backup: (type, id) => PBS.Utils.render_datastore_worker_id(id, gettext('Backup')), + "barcode-label-media": [gettext('Drive'), gettext('Barcode label media')], dircreate: [gettext('Directory Storage'), gettext('Create')], dirremove: [gettext('Directory'), gettext('Remove')], garbage_collection: ['Datastore', gettext('Garbage collect')], + "label-media": [gettext('Drive'), gettext('Label media')], logrotate: [null, gettext('Log Rotation')], prune: (type, id) => PBS.Utils.render_datastore_worker_id(id, gettext('Prune')), reader: (type, id) => PBS.Utils.render_datastore_worker_id(id, gettext('Read objects')),