public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock
@ 2021-02-18 14:40 Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 02/11] tape/drive: add get/set status functions Dominik Csapak
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

this makes it possible to detect if the drive was locked in a
non-blocking manner

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/tape/drive/mod.rs | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index e44e0e36..faa89953 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -496,3 +496,28 @@ fn lock_device_path(device_path: &str) -> Result<DeviceLockGuard, Error> {
 
     Ok(DeviceLockGuard(file))
 }
+
+// Same logic as lock_device_path, but uses a timeout of 0, making it
+// non-blocking, and returning if the file is locked or not
+fn test_device_path_lock(device_path: &str) -> Result<bool, Error> {
+
+    let lock_name = crate::tools::systemd::escape_unit(device_path, true);
+
+    let mut path = std::path::PathBuf::from("/var/lock");
+    path.push(lock_name);
+
+    let timeout = std::time::Duration::new(0, 0);
+    let mut file = std::fs::OpenOptions::new().create(true).append(true).open(path)?;
+    match proxmox::tools::fs::lock_file(&mut file, true, Some(timeout)) {
+        // file was not locked, continue
+        Ok(()) => {},
+        // file was locked, return true
+        Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => return Ok(true),
+        Err(err) => bail!("{}", err),
+    }
+
+    let backup_user = crate::backup::backup_user()?;
+    fchown(file.as_raw_fd(), Some(backup_user.uid), Some(backup_user.gid))?;
+
+    Ok(false)
+}
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 02/11] tape/drive: add get/set status functions
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 03/11] api2/tape/drive: add run_drive_worker wrapper Dominik Csapak
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

simply writes into/reads from a file in /run, we will use this
for writing the upid (or potential other states) per drive

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/tape/drive/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 3 deletions(-)

diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs
index faa89953..4abb95de 100644
--- a/src/tape/drive/mod.rs
+++ b/src/tape/drive/mod.rs
@@ -29,7 +29,12 @@ use proxmox::{
     tools::{
         Uuid,
         io::ReadExt,
-        fs::fchown,
+        fs::{
+            fchown,
+            file_read_optional_string,
+            replace_file,
+            CreateOptions,
+       }
     },
     api::section_config::SectionConfigData,
 };
@@ -453,7 +458,53 @@ pub fn lock_tape_device(
     config: &SectionConfigData,
     drive: &str,
 ) -> Result<DeviceLockGuard, Error> {
+    let path = tape_device_path(config, drive)?;
+    lock_device_path(&path)
+        .map_err(|err| format_err!("unable to lock drive '{}' - {}", drive, err))
+}
+
+/// Writes the given state for the specified drive
+///
+/// This function does not lock, so make sure the drive is locked
+pub fn set_tape_device_state(
+    drive: &str,
+    state: &str,
+) -> Result<(), Error> {
+    let mut path = "/run/proxmox-backup/drive-state".to_string();
+    std::fs::create_dir_all(&path)?;
+    use std::fmt::Write;
+    write!(path, "/{}", drive)?;
+
+    let backup_user = crate::backup::backup_user()?;
+    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
+    let options = CreateOptions::new()
+        .perm(mode)
+        .owner(backup_user.uid)
+        .group(backup_user.gid);
+
+    replace_file(path, state.as_bytes(), options)
+}
 
+/// Get the device state
+pub fn get_tape_device_state(
+    config: &SectionConfigData,
+    drive: &str,
+) -> Result<Option<String>, Error> {
+    let path = format!("/run/proxmox-backup/drive-state/{}", drive);
+    let state = file_read_optional_string(path)?;
+
+    let device_path = tape_device_path(config, drive)?;
+    if test_device_path_lock(&device_path)? {
+        Ok(state)
+    } else {
+        Ok(None)
+    }
+}
+
+fn tape_device_path(
+    config: &SectionConfigData,
+    drive: &str,
+) -> Result<String, Error> {
     match config.sections.get(drive) {
         Some((section_type_name, config)) => {
             let path = match section_type_name.as_ref() {
@@ -465,8 +516,7 @@ pub fn lock_tape_device(
                 }
                 _ => bail!("unknown drive type '{}' - internal error"),
             };
-            lock_device_path(&path)
-                .map_err(|err| format_err!("unable to lock drive '{}' - {}", drive, err))
+            Ok(path)
         }
         None => {
             bail!("no such drive '{}'", drive);
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 03/11] api2/tape/drive: add run_drive_worker wrapper
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 02/11] tape/drive: add get/set status functions Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 04/11] api2/tape/drive: use 'run_drive_worker' where possible Dominik Csapak
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

a wrapper for locking, starting the worker and writing the state

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/tape/drive.rs | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index 8de8d39d..f4977612 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -1,7 +1,8 @@
+use std::panic::UnwindSafe;
 use std::path::Path;
 use std::sync::Arc;
 
-use anyhow::{bail, Error};
+use anyhow::{bail, format_err, Error};
 use serde_json::Value;
 
 use proxmox::{
@@ -62,11 +63,44 @@ use crate::{
             required_media_changer,
             open_drive,
             lock_tape_device,
+            set_tape_device_state,
         },
         changer::update_changer_online_status,
     },
 };
 
+fn run_drive_worker<F>(
+    rpcenv: &dyn RpcEnvironment,
+    drive: String,
+    worker_type: &str,
+    job_id: Option<String>,
+    f: F,
+) -> Result<String, Error>
+where
+    F: Send
+        + UnwindSafe
+        + 'static
+        + FnOnce(Arc<WorkerTask>, SectionConfigData) -> Result<(), Error>,
+{
+    // early check/lock before starting worker
+    let (config, _digest) = config::drive::config()?;
+    let lock_guard = lock_tape_device(&config, &drive)?;
+
+    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
+    WorkerTask::new_thread(worker_type, job_id, auth_id, to_stdout, move |worker| {
+        let _lock_guard = lock_guard;
+        set_tape_device_state(&drive, &worker.upid().to_string())
+            .map_err(|err| format_err!("could not set tape device state: {}", err))?;
+
+        let result = f(worker, config);
+        set_tape_device_state(&drive, "")
+            .map_err(|err| format_err!("could not unset tape device state: {}", err))?;
+        result
+    })
+}
+
 #[api(
     input: {
         properties: {
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 04/11] api2/tape/drive: use 'run_drive_worker' where possible
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 02/11] tape/drive: add get/set status functions Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 03/11] api2/tape/drive: add run_drive_worker wrapper Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 05/11] api2/tape/drive: add wrapper for tokio::task::spawn_blocking Dominik Csapak
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/tape/drive.rs | 223 ++++++++++-------------------------------
 1 file changed, 52 insertions(+), 171 deletions(-)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index f4977612..7a6461b0 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -124,32 +124,20 @@ pub fn load_media(
     label_text: String,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
     let job_id = format!("{}:{}", drive, label_text);
 
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "load-media",
         Some(job_id),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             task_log!(worker, "loading media '{}' into drive '{}'", label_text, drive);
             let (mut changer, _) = required_media_changer(&config, &drive)?;
             changer.load_media(&label_text)?;
 
             Ok(())
-        }
+        },
     )?;
 
     Ok(upid_str.into())
@@ -241,29 +229,18 @@ pub fn unload(
     target_slot: Option<u64>,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "unload-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             task_log!(worker, "unloading media from drive '{}'", drive);
 
             let (mut changer, _) = required_media_changer(&config, &drive)?;
             changer.unload_media(target_slot)?;
             Ok(())
-        }
+        },
     )?;
 
     Ok(upid_str.into())
@@ -298,23 +275,12 @@ pub fn erase_media(
     label_text: Option<String>,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "erase-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             if let Some(ref label) = label_text {
                 task_log!(worker, "try to load media '{}'", label);
                 if let Some((mut changer, _)) = media_changer(&config, &drive)? {
@@ -368,7 +334,7 @@ pub fn erase_media(
             }
 
             Ok(())
-        }
+        },
     )?;
 
     Ok(upid_str.into())
@@ -391,27 +357,16 @@ pub fn rewind(
     drive: String,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "rewind-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |_worker| {
-            let _lock_guard = lock_guard; // keep lock guard
+        move |_worker, config| {
             let mut drive = open_drive(&config, &drive)?;
             drive.rewind()?;
             Ok(())
-        }
+        },
     )?;
 
     Ok(upid_str.into())
@@ -434,32 +389,21 @@ pub fn eject_media(
     drive: String,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "eject-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |_worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
-             if let Some((mut changer, _)) = media_changer(&config, &drive)? {
+        move |_worker, config| {
+            if let Some((mut changer, _)) = media_changer(&config, &drive)? {
                 changer.unload_media(None)?;
             } else {
                 let mut drive = open_drive(&config, &drive)?;
                 drive.eject_media()?;
             }
             Ok(())
-        })?;
+        },
+    )?;
 
     Ok(upid_str.into())
 }
@@ -495,9 +439,6 @@ pub fn label_media(
     label_text: String,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
     if let Some(ref pool) = pool {
         let (pool_config, _digest) = config::media_pool::config()?;
 
@@ -505,22 +446,12 @@ pub fn label_media(
             bail!("no such pool ('{}')", pool);
         }
     }
-
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "label-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             let mut drive = open_drive(&config, &drive)?;
 
             drive.rewind()?;
@@ -545,7 +476,7 @@ pub fn label_media(
             };
 
             write_media_label(worker, &mut drive, label, pool)
-        }
+        },
     )?;
 
     Ok(upid_str.into())
@@ -757,24 +688,12 @@ pub fn clean_drive(
     drive: String,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "clean-drive",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             let (mut changer, _changer_name) = required_media_changer(&config, &drive)?;
 
             worker.log("Starting drive clean");
@@ -784,7 +703,8 @@ pub fn clean_drive(
             worker.log("Drive cleaned sucessfully");
 
             Ok(())
-        })?;
+        },
+    )?;
 
     Ok(upid_str.into())
 }
@@ -891,24 +811,12 @@ pub fn update_inventory(
     read_all_labels: Option<bool>,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "inventory-update",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 
             let label_text_list = changer.online_media_label_texts()?;
@@ -960,7 +868,7 @@ pub fn update_inventory(
                 changer.unload_media(None)?;
             }
             Ok(())
-        }
+        },
     )?;
 
     Ok(upid_str.into())
@@ -989,7 +897,6 @@ pub fn barcode_label_media(
     pool: Option<String>,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
     if let Some(ref pool) = pool {
         let (pool_config, _digest) = config::media_pool::config()?;
 
@@ -998,24 +905,12 @@ pub fn barcode_label_media(
         }
     }
 
-    let (drive_config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&drive_config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "barcode-label-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-            barcode_label_media_worker(worker, drive, &drive_config, pool)
-        }
+        move |worker, config| barcode_label_media_worker(worker, drive, &config, pool),
     )?;
 
     Ok(upid_str.into())
@@ -1027,7 +922,6 @@ fn barcode_label_media_worker(
     drive_config: &SectionConfigData,
     pool: Option<String>,
 ) -> Result<(), Error> {
-
     let (mut changer, changer_name) = required_media_changer(drive_config, &drive)?;
 
     let label_text_list = changer.online_media_label_texts()?;
@@ -1202,27 +1096,15 @@ pub fn catalog_media(
     verbose: Option<bool>,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Value, Error> {
-
     let verbose = verbose.unwrap_or(false);
     let force = force.unwrap_or(false);
 
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
-    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
-
-    let upid_str = WorkerTask::new_thread(
+    let upid_str = run_drive_worker(
+        rpcenv,
+        drive.clone(),
         "catalog-media",
         Some(drive.clone()),
-        auth_id,
-        to_stdout,
-        move |worker| {
-            let _lock_guard = lock_guard; // keep lock guard
-
+        move |worker, config| {
             let mut drive = open_drive(&config, &drive)?;
 
             drive.rewind()?;
@@ -1279,8 +1161,7 @@ pub fn catalog_media(
             restore_media(&worker, &mut drive, &media_id, None, verbose)?;
 
             Ok(())
-
-        }
+        },
     )?;
 
     Ok(upid_str.into())
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 05/11] api2/tape/drive: add wrapper for tokio::task::spawn_blocking
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (2 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 04/11] api2/tape/drive: use 'run_drive_worker' where possible Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 06/11] api2/tape/drive: use run_drive_blocking_task where possible Dominik Csapak
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

similar to the worker wrapper, lock, write status, run code, unset status

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/tape/drive.rs | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index 7a6461b0..b4cd4aa6 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -101,6 +101,26 @@ where
     })
 }
 
+async fn run_drive_blocking_task<F, R>(drive: String, state: String, f: F) -> Result<R, Error>
+where
+    F: Send + 'static + FnOnce(SectionConfigData) -> Result<R, Error>,
+    R: Send + 'static,
+{
+    // early check/lock before starting worker
+    let (config, _digest) = config::drive::config()?;
+    let lock_guard = lock_tape_device(&config, &drive)?;
+    tokio::task::spawn_blocking(move || {
+        let _lock_guard = lock_guard;
+        set_tape_device_state(&drive, &state)
+            .map_err(|err| format_err!("could not set tape device state: {}", err))?;
+        let result = f(config);
+        set_tape_device_state(&drive, "")
+            .map_err(|err| format_err!("could not unset tape device state: {}", err))?;
+        result
+    })
+    .await?
+}
+
 #[api(
     input: {
         properties: {
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 06/11] api2/tape/drive: use run_drive_blocking_task where possible
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (3 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 05/11] api2/tape/drive: add wrapper for tokio::task::spawn_blocking Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 07/11] api2/tape/drive: wrap some api calls in run_drive_blocking_task Dominik Csapak
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
best viewed with '-w'
 src/api2/tape/drive.rs | 245 ++++++++++++++++++++---------------------
 1 file changed, 119 insertions(+), 126 deletions(-)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index b4cd4aa6..5ee56441 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -180,16 +180,15 @@ pub fn load_media(
 ///
 /// Issue a media load request to the associated changer device.
 pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
-
-    let (config, _digest) = config::drive::config()?;
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    tokio::task::spawn_blocking(move || {
-        let _lock_guard = lock_guard; // keep lock guard
-
-        let (mut changer, _) = required_media_changer(&config, &drive)?;
-        changer.load_media_from_slot(source_slot)
-    }).await?
+    run_drive_blocking_task(
+        drive.clone(),
+        format!("load from slot {}", source_slot),
+        move |config| {
+            let (mut changer, _) = required_media_changer(&config, &drive)?;
+            changer.load_media_from_slot(source_slot)
+        },
+    )
+    .await
 }
 
 #[api(
@@ -211,19 +210,22 @@ pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
 )]
 /// Export media with specified label
 pub async fn export_media(drive: String, label_text: String) -> Result<u64, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    tokio::task::spawn_blocking(move || {
-        let _lock_guard = lock_guard; // keep lock guard
-
-        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
-        match changer.export_media(&label_text)? {
-            Some(slot) => Ok(slot),
-            None => bail!("media '{}' is not online (via changer '{}')", label_text, changer_name),
+    run_drive_blocking_task(
+        drive.clone(),
+        format!("export media {}", label_text),
+        move |config| {
+            let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
+            match changer.export_media(&label_text)? {
+                Some(slot) => Ok(slot),
+                None => bail!(
+                    "media '{}' is not online (via changer '{}')",
+                    label_text,
+                    changer_name
+                ),
+            }
         }
-    }).await?
+    )
+    .await
 }
 
 #[api(
@@ -584,29 +586,26 @@ pub async fn restore_key(
     drive: String,
     password: String,
 ) -> Result<(), Error> {
+    run_drive_blocking_task(
+        drive.clone(),
+        "restore key".to_string(),
+        move |config| {
+            let mut drive = open_drive(&config, &drive)?;
 
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    tokio::task::spawn_blocking(move || {
-        let _lock_guard = lock_guard; // keep lock guard
-
-        let mut drive = open_drive(&config, &drive)?;
+            let (_media_id, key_config) = drive.read_label()?;
 
-        let (_media_id, key_config) = drive.read_label()?;
+            if let Some(key_config) = key_config {
+                let password_fn = || { Ok(password.as_bytes().to_vec()) };
+                let (key, ..) = key_config.decrypt(&password_fn)?;
+                config::tape_encryption_keys::insert_key(key, key_config, true)?;
+            } else {
+                bail!("media does not contain any encryption key configuration");
+            }
 
-        if let Some(key_config) = key_config {
-            let password_fn = || { Ok(password.as_bytes().to_vec()) };
-            let (key, ..) = key_config.decrypt(&password_fn)?;
-            config::tape_encryption_keys::insert_key(key, key_config, true)?;
-        } else {
-            bail!("media does not contain any encryption key configuration");
+            Ok(())
         }
-
-        Ok(())
-    }).await?
+    )
+    .await
 }
 
  #[api(
@@ -630,65 +629,62 @@ pub async fn read_label(
     drive: String,
     inventorize: Option<bool>,
 ) -> Result<MediaIdFlat, Error> {
+    run_drive_blocking_task(
+        drive.clone(),
+        "reading label".to_string(),
+        move |config| {
+            let mut drive = open_drive(&config, &drive)?;
 
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    tokio::task::spawn_blocking(move || {
-        let _lock_guard = lock_guard; // keep lock guard
-
-        let mut drive = open_drive(&config, &drive)?;
-
-        let (media_id, _key_config) = drive.read_label()?;
-
-        let media_id = match media_id {
-            Some(media_id) => {
-                let mut flat = MediaIdFlat {
-                    uuid: media_id.label.uuid.clone(),
-                    label_text: media_id.label.label_text.clone(),
-                    ctime: media_id.label.ctime,
-                    media_set_ctime: None,
-                    media_set_uuid: None,
-                    encryption_key_fingerprint: None,
-                    pool: None,
-                    seq_nr: None,
-                };
-                if let Some(ref set) = media_id.media_set_label {
-                    flat.pool = Some(set.pool.clone());
-                    flat.seq_nr = Some(set.seq_nr);
-                    flat.media_set_uuid = Some(set.uuid.clone());
-                    flat.media_set_ctime = Some(set.ctime);
-                    flat.encryption_key_fingerprint = set
-                        .encryption_key_fingerprint
-                        .as_ref()
-                        .map(|fp| crate::tools::format::as_fingerprint(fp.bytes()));
-
-                    let encrypt_fingerprint = set.encryption_key_fingerprint.clone()
-                        .map(|fp| (fp, set.uuid.clone()));
+            let (media_id, _key_config) = drive.read_label()?;
+
+            let media_id = match media_id {
+                Some(media_id) => {
+                    let mut flat = MediaIdFlat {
+                        uuid: media_id.label.uuid.clone(),
+                        label_text: media_id.label.label_text.clone(),
+                        ctime: media_id.label.ctime,
+                        media_set_ctime: None,
+                        media_set_uuid: None,
+                        encryption_key_fingerprint: None,
+                        pool: None,
+                        seq_nr: None,
+                    };
+                    if let Some(ref set) = media_id.media_set_label {
+                        flat.pool = Some(set.pool.clone());
+                        flat.seq_nr = Some(set.seq_nr);
+                        flat.media_set_uuid = Some(set.uuid.clone());
+                        flat.media_set_ctime = Some(set.ctime);
+                        flat.encryption_key_fingerprint = set
+                            .encryption_key_fingerprint
+                            .as_ref()
+                            .map(|fp| crate::tools::format::as_fingerprint(fp.bytes()));
+
+                        let encrypt_fingerprint = set.encryption_key_fingerprint.clone()
+                            .map(|fp| (fp, set.uuid.clone()));
+
+                        if let Err(err) = drive.set_encryption(encrypt_fingerprint) {
+                            // try, but ignore errors. just log to stderr
+                            eprintln!("uable to load encryption key: {}", err);
+                        }
+                    }
 
-                    if let Err(err) = drive.set_encryption(encrypt_fingerprint) {
-                        // try, but ignore errors. just log to stderr
-                        eprintln!("uable to load encryption key: {}", err);
+                    if let Some(true) = inventorize {
+                        let state_path = Path::new(TAPE_STATUS_DIR);
+                        let mut inventory = Inventory::load(state_path)?;
+                        inventory.store(media_id, false)?;
                     }
-                }
 
-                if let Some(true) = inventorize {
-                    let state_path = Path::new(TAPE_STATUS_DIR);
-                    let mut inventory = Inventory::load(state_path)?;
-                    inventory.store(media_id, false)?;
+                    flat
                 }
+                None => {
+                    bail!("Media is empty (no label).");
+                }
+            };
 
-                flat
-            }
-            None => {
-                bail!("Media is empty (no label).");
-            }
-        };
-
-        Ok(media_id)
-    }).await?
+            Ok(media_id)
+        }
+    )
+    .await
 }
 
 #[api(
@@ -755,49 +751,46 @@ pub fn clean_drive(
 pub async fn inventory(
     drive: String,
 ) -> Result<Vec<LabelUuidMap>, Error> {
+    run_drive_blocking_task(
+        drive.clone(),
+        "inventorize".to_string(),
+        move |config| {
+            let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
 
-    let (config, _digest) = config::drive::config()?;
-
-    // early check/lock before starting worker
-    let lock_guard = lock_tape_device(&config, &drive)?;
-
-    tokio::task::spawn_blocking(move || {
-        let _lock_guard = lock_guard; // keep lock guard
+            let label_text_list = changer.online_media_label_texts()?;
 
-        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
+            let state_path = Path::new(TAPE_STATUS_DIR);
 
-        let label_text_list = changer.online_media_label_texts()?;
+            let mut inventory = Inventory::load(state_path)?;
 
-        let state_path = Path::new(TAPE_STATUS_DIR);
+            update_changer_online_status(
+                &config,
+                &mut inventory,
+                &changer_name,
+                &label_text_list,
+            )?;
 
-        let mut inventory = Inventory::load(state_path)?;
+            let mut list = Vec::new();
 
-        update_changer_online_status(
-            &config,
-            &mut inventory,
-            &changer_name,
-            &label_text_list,
-        )?;
+            for label_text in label_text_list.iter() {
+                if label_text.starts_with("CLN") {
+                    // skip cleaning unit
+                    continue;
+                }
 
-        let mut list = Vec::new();
+                let label_text = label_text.to_string();
 
-        for label_text in label_text_list.iter() {
-            if label_text.starts_with("CLN") {
-                // skip cleaning unit
-                continue;
+                if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
+                    list.push(LabelUuidMap { label_text, uuid: Some(media_id.label.uuid.clone()) });
+                } else {
+                    list.push(LabelUuidMap { label_text, uuid: None });
+                }
             }
 
-            let label_text = label_text.to_string();
-
-            if let Some(media_id) = inventory.find_media_by_label_text(&label_text) {
-                list.push(LabelUuidMap { label_text, uuid: Some(media_id.label.uuid.clone()) });
-            } else {
-                list.push(LabelUuidMap { label_text, uuid: None });
-            }
+            Ok(list)
         }
-
-        Ok(list)
-    }).await?
+    )
+    .await
 }
 
 #[api(
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 07/11] api2/tape/drive: wrap some api calls in run_drive_blocking_task
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (4 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 06/11] api2/tape/drive: use run_drive_blocking_task where possible Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 08/11] api2/tape/changer: add drive state to changer status output Dominik Csapak
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

those calls could also block, so we have to run them in a blocking
tokio task, as to not block the current thread

nice side effect is that we now also update the state for that
drive in those instances

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/tape/drive.rs | 64 +++++++++++++++++++++++-------------------
 1 file changed, 35 insertions(+), 29 deletions(-)

diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs
index 5ee56441..eeda9e21 100644
--- a/src/api2/tape/drive.rs
+++ b/src/api2/tape/drive.rs
@@ -1014,16 +1014,18 @@ fn barcode_label_media_worker(
     },
 )]
 /// Read Cartridge Memory (Medium auxiliary memory attributes)
-pub fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    let _lock_guard = lock_tape_device(&config, &drive)?;
-
-    let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
-    let mut handle = drive_config.open()?;
+pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
+    run_drive_blocking_task(
+        drive.clone(),
+        "reading cartridge memory".to_string(),
+        move |config| {
+            let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
+            let mut handle = drive_config.open()?;
 
-    handle.cartridge_memory()
+            handle.cartridge_memory()
+        }
+    )
+    .await
 }
 
 #[api(
@@ -1039,16 +1041,18 @@ pub fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
     },
 )]
 /// Read Volume Statistics (SCSI log page 17h)
-pub fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    let _lock_guard = lock_tape_device(&config, &drive)?;
-
-    let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
-    let mut handle = drive_config.open()?;
+pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
+    run_drive_blocking_task(
+        drive.clone(),
+        "reading volume statistics".to_string(),
+        move |config| {
+            let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
+            let mut handle = drive_config.open()?;
 
-    handle.volume_statistics()
+            handle.volume_statistics()
+        }
+    )
+    .await
 }
 
 #[api(
@@ -1064,20 +1068,22 @@ pub fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
     },
 )]
 /// Get drive/media status
-pub fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
-
-    let (config, _digest) = config::drive::config()?;
-
-    let _lock_guard = lock_tape_device(&config, &drive)?;
-
-    let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
+pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
+    run_drive_blocking_task(
+        drive.clone(),
+        "reading drive status".to_string(),
+        move |config| {
+            let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
 
-    // Note: use open_linux_tape_device, because this also works if no medium loaded
-    let file = open_linux_tape_device(&drive_config.path)?;
+            // Note: use open_linux_tape_device, because this also works if no medium loaded
+            let file = open_linux_tape_device(&drive_config.path)?;
 
-    let mut handle = LinuxTapeHandle::new(file);
+            let mut handle = LinuxTapeHandle::new(file);
 
-    handle.get_drive_and_media_status()
+            handle.get_drive_and_media_status()
+        }
+    )
+    .await
 }
 
 #[api(
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 08/11] api2/tape/changer: add drive state to changer status output
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (5 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 07/11] api2/tape/drive: wrap some api calls in run_drive_blocking_task Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 09/11] api2/tape/{backup, restore}, proxmox-tape: set device state Dominik Csapak
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

if we can find the drive in the config and it has a state

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/tape/changer.rs       | 22 ++++++++++++++++++++++
 src/api2/types/tape/changer.rs |  3 +++
 2 files changed, 25 insertions(+)

diff --git a/src/api2/tape/changer.rs b/src/api2/tape/changer.rs
index dc98d85b..212ca8eb 100644
--- a/src/api2/tape/changer.rs
+++ b/src/api2/tape/changer.rs
@@ -1,3 +1,4 @@
+use std::collections::HashMap;
 use std::path::Path;
 
 use anyhow::Error;
@@ -11,6 +12,7 @@ use crate::{
     api2::types::{
         CHANGER_NAME_SCHEMA,
         ChangerListEntry,
+        LinuxTapeDrive,
         MtxEntryKind,
         MtxStatusEntry,
         ScsiTapeChanger,
@@ -25,6 +27,7 @@ use crate::{
             ScsiMediaChange,
             mtx_status_to_online_set,
         },
+        drive::get_tape_device_state,
         lookup_device_identification,
     },
 };
@@ -66,9 +69,26 @@ pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
 
     inventory.update_online_status(&map)?;
 
+    let drive_list: Vec<LinuxTapeDrive> = config.convert_to_typed_array("linux")?;
+    let mut drive_map: HashMap<u64, String> = HashMap::new();
+
+    for drive in drive_list {
+        if let Some(changer) = drive.changer {
+            if changer != name {
+                continue;
+            }
+            let num = drive.changer_drivenum.unwrap_or(0);
+            drive_map.insert(num, drive.name.clone());
+        }
+    }
+
     let mut list = Vec::new();
 
     for (id, drive_status) in status.drives.iter().enumerate() {
+        let mut state = None;
+        if let Some(drive) = drive_map.get(&(id as u64)) {
+            state = get_tape_device_state(&config, &drive)?;
+        }
         let entry = MtxStatusEntry {
             entry_kind: MtxEntryKind::Drive,
             entry_id: id as u64,
@@ -78,6 +98,7 @@ pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
                 ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
             },
             loaded_slot: drive_status.loaded_slot,
+            state,
         };
         list.push(entry);
     }
@@ -96,6 +117,7 @@ pub async fn get_status(name: String) -> Result<Vec<MtxStatusEntry>, Error> {
                 ElementStatus::VolumeTag(tag) => Some(tag.to_string()),
             },
             loaded_slot: None,
+            state: None,
         };
         list.push(entry);
     }
diff --git a/src/api2/types/tape/changer.rs b/src/api2/types/tape/changer.rs
index f9d30acd..780bf669 100644
--- a/src/api2/types/tape/changer.rs
+++ b/src/api2/types/tape/changer.rs
@@ -129,4 +129,7 @@ pub struct MtxStatusEntry {
     /// The slot the drive was loaded from
     #[serde(skip_serializing_if="Option::is_none")]
     pub loaded_slot: Option<u64>,
+    /// The current state of the drive
+    #[serde(skip_serializing_if="Option::is_none")]
+    pub state: Option<String>,
 }
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 09/11] api2/tape/{backup, restore}, proxmox-tape: set device state
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (6 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 08/11] api2/tape/changer: add drive state to changer status output Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 10/11] ui: tape: fix eslint warnings (trailing comma) Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 11/11] ui: tape/ChangerStatus: show the state of the drive Dominik Csapak
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

set the drive device state everywhere we lock it, so that we
know what it currently does

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/tape/backup.rs  | 13 +++++++++++++
 src/api2/tape/restore.rs | 13 +++++++++++++
 src/bin/proxmox-tape.rs  |  3 +++
 3 files changed, 29 insertions(+)

diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs
index 522b4097..a9c54e7d 100644
--- a/src/api2/tape/backup.rs
+++ b/src/api2/tape/backup.rs
@@ -45,6 +45,7 @@ use crate::{
         drive::{
             media_changer,
             lock_tape_device,
+            set_tape_device_state,
         },
         changer::update_changer_online_status,
     },
@@ -90,6 +91,7 @@ pub fn do_tape_backup_job(
         move |worker| {
             let _drive_lock = drive_lock; // keep lock guard
 
+            set_tape_device_state(&tape_job.drive, &worker.upid().to_string())?;
             job.start(&worker.upid().to_string())?;
 
             let eject_media = false;
@@ -119,6 +121,14 @@ pub fn do_tape_backup_job(
                 );
             }
 
+            if let Err(err) = set_tape_device_state(&tape_job.drive, "") {
+                eprintln!(
+                    "could not unset drive state for {}: {}",
+                    tape_job.drive,
+                    err
+                );
+            }
+
             job_result
         }
     )?;
@@ -216,7 +226,10 @@ pub fn backup(
         to_stdout,
         move |worker| {
             let _drive_lock = drive_lock; // keep lock guard
+            set_tape_device_state(&drive, &worker.upid().to_string())?;
             backup_worker(&worker, datastore, &drive, &pool_config, eject_media, export_media_set)?;
+            // ignore errors
+            let _ = set_tape_device_state(&drive, "");
             Ok(())
         }
     )?;
diff --git a/src/api2/tape/restore.rs b/src/api2/tape/restore.rs
index 498b49df..707b8f36 100644
--- a/src/api2/tape/restore.rs
+++ b/src/api2/tape/restore.rs
@@ -69,6 +69,7 @@ use crate::{
             TapeDriver,
             request_and_load_media,
             lock_tape_device,
+            set_tape_device_state,
         },
     },
 };
@@ -134,6 +135,8 @@ pub fn restore(
         move |worker| {
             let _drive_lock = drive_lock; // keep lock guard
 
+            set_tape_device_state(&drive, &worker.upid().to_string())?;
+
             let _lock = MediaPool::lock(status_path, &pool)?;
 
             let members = inventory.compute_media_set_members(&media_set_uuid)?;
@@ -189,6 +192,16 @@ pub fn restore(
             }
 
             task_log!(worker, "Restore mediaset '{}' done", media_set);
+
+            if let Err(err) = set_tape_device_state(&drive, "") {
+                task_log!(
+                    worker,
+                    "could not unset drive state for {}: {}",
+                    drive,
+                    err
+                );
+            }
+
             Ok(())
         }
     )?;
diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs
index eb792986..74b9c128 100644
--- a/src/bin/proxmox-tape.rs
+++ b/src/bin/proxmox-tape.rs
@@ -43,6 +43,7 @@ use proxmox_backup::{
         drive::{
             open_drive,
             lock_tape_device,
+            set_tape_device_state,
         },
         complete_media_label_text,
         complete_media_set_uuid,
@@ -543,6 +544,7 @@ fn move_to_eom(mut param: Value) -> Result<(), Error> {
     let drive = extract_drive_name(&mut param, &config)?;
 
     let _lock = lock_tape_device(&config, &drive)?;
+    set_tape_device_state(&drive, "moving to eom")?;
 
     let mut drive = open_drive(&config, &drive)?;
 
@@ -572,6 +574,7 @@ fn debug_scan(mut param: Value) -> Result<(), Error> {
     let drive = extract_drive_name(&mut param, &config)?;
 
     let _lock = lock_tape_device(&config, &drive)?;
+    set_tape_device_state(&drive, "debug scan")?;
 
     let mut drive = open_drive(&config, &drive)?;
 
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 10/11] ui: tape: fix eslint warnings (trailing comma)
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (7 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 09/11] api2/tape/{backup, restore}, proxmox-tape: set device state Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 11/11] ui: tape/ChangerStatus: show the state of the drive Dominik Csapak
  9 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/tape/BackupJobs.js    | 6 +++---
 www/tape/ChangerStatus.js | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/www/tape/BackupJobs.js b/www/tape/BackupJobs.js
index e2c03365..80ed99a5 100644
--- a/www/tape/BackupJobs.js
+++ b/www/tape/BackupJobs.js
@@ -3,7 +3,7 @@ Ext.define('pbs-tape-backup-job-status', {
     fields: [
 	'id', 'store', 'pool', 'drive', 'store', 'schedule', 'comment',
 	{ name: 'eject-media', type: 'boolean' },
-	{ name: 'export-media-set', type: 'boolean' }
+	{ name: 'export-media-set', type: 'boolean' },
     ],
     idProperty: 'id',
     proxy: {
@@ -32,7 +32,7 @@ Ext.define('PBS.config.TapeBackupJobView', {
 
 	init: function(view) {
 	    Proxmox.Utils.monStoreErrors(view, view.getStore().rstore);
-	}
+	},
     },
 
     listeners: {
@@ -107,5 +107,5 @@ Ext.define('PBS.config.TapeBackupJobView', {
 	let me = this;
 
 	me.callParent();
-    }
+    },
 });
diff --git a/www/tape/ChangerStatus.js b/www/tape/ChangerStatus.js
index d00d712f..436bae72 100644
--- a/www/tape/ChangerStatus.js
+++ b/www/tape/ChangerStatus.js
@@ -422,7 +422,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
 		    method: 'GET',
 		    params: {
 			"update-status": false,
-		    }
+		    },
 		});
 
 		let [status, drives, tapes_list] = await Promise.all([status_fut, drives_fut, tapes_fut]);
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 11/11] ui: tape/ChangerStatus: show the state of the drive
  2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
                   ` (8 preceding siblings ...)
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 10/11] ui: tape: fix eslint warnings (trailing comma) Dominik Csapak
@ 2021-02-18 14:40 ` Dominik Csapak
  2021-02-18 15:06   ` Dominik Csapak
  9 siblings, 1 reply; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 14:40 UTC (permalink / raw)
  To: pbs-devel

an optimize the columns for smaller layouts (1280 width)
we show either:
* Idle
* spinner + status (if no upid)
* spinner + rendered UPID (clickable, opens task viewer)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
we probably want to have an updatestore for only the drive state
of the changer (such an api call would not cost much, since it
only checks a lock + read from tmpfs) to have an up-to-date view
of the drive state. If we have that, we can enable/disable
buttons/options for busy drives.

 www/tape/ChangerStatus.js | 47 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/www/tape/ChangerStatus.js b/www/tape/ChangerStatus.js
index 436bae72..5a1c6073 100644
--- a/www/tape/ChangerStatus.js
+++ b/www/tape/ChangerStatus.js
@@ -498,6 +498,42 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
 	    }
 	    return status;
 	},
+
+	renderState: function(value, md, record) {
+	    if (!value) {
+		return gettext('Idle');
+	    }
+
+	    let icon = '<i class="fa fa-spinner fa-pulse fa-fw"></i>';
+
+	    if (value.startsWith("UPID")) {
+		let upid = Proxmox.Utils.parse_task_upid(value);
+		md.tdCls = "pointer";
+		return `${icon} ${upid.desc}`;
+	    }
+
+	    return `${icon} ${value}`;
+	},
+
+	control: {
+	    'grid[reference=drives]': {
+		cellclick: function(table, td, ci, rec, tr, ri, e) {
+		    if (!e.position.column.dataIndex === 'state') {
+			return;
+		    }
+
+		    let upid = rec.data.state;
+		    if (!upid || !upid.startsWith("UPID")) {
+			return;
+		    }
+
+		    Ext.create('Proxmox.window.TaskViewer', {
+			autoShow: true,
+			upid,
+		    });
+		},
+	    },
+	},
     },
 
     listeners: {
@@ -641,7 +677,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
 				    text: gettext('Inventory'),
 				    dataIndex: 'is-labeled',
 				    renderer: 'renderIsLabeled',
-				    flex: 1,
+				    flex: 1.5,
 				},
 				{
 				    text: gettext("Name"),
@@ -650,10 +686,17 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
 				    flex: 1,
 				    renderer: Ext.htmlEncode,
 				},
+				{
+				    text: gettext('State'),
+				    dataIndex: 'state',
+				    flex: 3,
+				    renderer: 'renderState',
+				},
 				{
 				    text: gettext("Vendor"),
 				    sortable: true,
 				    dataIndex: 'vendor',
+				    hidden: true,
 				    flex: 1,
 				    renderer: Ext.htmlEncode,
 				},
@@ -661,6 +704,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
 				    text: gettext("Model"),
 				    sortable: true,
 				    dataIndex: 'model',
+				    hidden: true,
 				    flex: 1,
 				    renderer: Ext.htmlEncode,
 				},
@@ -668,6 +712,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
 				    text: gettext("Serial"),
 				    sortable: true,
 				    dataIndex: 'serial',
+				    hidden: true,
 				    flex: 1,
 				    renderer: Ext.htmlEncode,
 				},
-- 
2.20.1





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup 11/11] ui: tape/ChangerStatus: show the state of the drive
  2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 11/11] ui: tape/ChangerStatus: show the state of the drive Dominik Csapak
@ 2021-02-18 15:06   ` Dominik Csapak
  0 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-02-18 15:06 UTC (permalink / raw)
  To: pbs-devel

On 2/18/21 15:40, Dominik Csapak wrote:
> an optimize the columns for smaller layouts (1280 width)
> we show either:
> * Idle
> * spinner + status (if no upid)
> * spinner + rendered UPID (clickable, opens task viewer)
> 
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> we probably want to have an updatestore for only the drive state
> of the changer (such an api call would not cost much, since it
> only checks a lock + read from tmpfs) to have an up-to-date view
> of the drive state. If we have that, we can enable/disable
> buttons/options for busy drives.
> 
>   www/tape/ChangerStatus.js | 47 ++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 46 insertions(+), 1 deletion(-)
> 
> diff --git a/www/tape/ChangerStatus.js b/www/tape/ChangerStatus.js
> index 436bae72..5a1c6073 100644
> --- a/www/tape/ChangerStatus.js
> +++ b/www/tape/ChangerStatus.js
> @@ -498,6 +498,42 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
>   	    }
>   	    return status;
>   	},
> +
> +	renderState: function(value, md, record) {
> +	    if (!value) {
> +		return gettext('Idle');
> +	    }
> +
> +	    let icon = '<i class="fa fa-spinner fa-pulse fa-fw"></i>';
> +
> +	    if (value.startsWith("UPID")) {
> +		let upid = Proxmox.Utils.parse_task_upid(value);
> +		md.tdCls = "pointer";
> +		return `${icon} ${upid.desc}`;
> +	    }
> +
> +	    return `${icon} ${value}`;
> +	},
> +
> +	control: {
> +	    'grid[reference=drives]': {
> +		cellclick: function(table, td, ci, rec, tr, ri, e) {
> +		    if (!e.position.column.dataIndex === 'state') {

this line should be

if (e.position.column.dataIndex !== 'state') {

i'll fix in a potential v2 or alternatively send a fixup

> +			return;
> +		    }
> +
> +		    let upid = rec.data.state;
> +		    if (!upid || !upid.startsWith("UPID")) {
> +			return;
> +		    }
> +
> +		    Ext.create('Proxmox.window.TaskViewer', {
> +			autoShow: true,
> +			upid,
> +		    });
> +		},
> +	    },
> +	},
>       },
>   
>       listeners: {
> @@ -641,7 +677,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
>   				    text: gettext('Inventory'),
>   				    dataIndex: 'is-labeled',
>   				    renderer: 'renderIsLabeled',
> -				    flex: 1,
> +				    flex: 1.5,
>   				},
>   				{
>   				    text: gettext("Name"),
> @@ -650,10 +686,17 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
>   				    flex: 1,
>   				    renderer: Ext.htmlEncode,
>   				},
> +				{
> +				    text: gettext('State'),
> +				    dataIndex: 'state',
> +				    flex: 3,
> +				    renderer: 'renderState',
> +				},
>   				{
>   				    text: gettext("Vendor"),
>   				    sortable: true,
>   				    dataIndex: 'vendor',
> +				    hidden: true,
>   				    flex: 1,
>   				    renderer: Ext.htmlEncode,
>   				},
> @@ -661,6 +704,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
>   				    text: gettext("Model"),
>   				    sortable: true,
>   				    dataIndex: 'model',
> +				    hidden: true,
>   				    flex: 1,
>   				    renderer: Ext.htmlEncode,
>   				},
> @@ -668,6 +712,7 @@ Ext.define('PBS.TapeManagement.ChangerStatus', {
>   				    text: gettext("Serial"),
>   				    sortable: true,
>   				    dataIndex: 'serial',
> +				    hidden: true,
>   				    flex: 1,
>   				    renderer: Ext.htmlEncode,
>   				},
> 





^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2021-02-18 15:06 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-18 14:40 [pbs-devel] [PATCH proxmox-backup 01/11] tape/drive: add test_device_path_lock Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 02/11] tape/drive: add get/set status functions Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 03/11] api2/tape/drive: add run_drive_worker wrapper Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 04/11] api2/tape/drive: use 'run_drive_worker' where possible Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 05/11] api2/tape/drive: add wrapper for tokio::task::spawn_blocking Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 06/11] api2/tape/drive: use run_drive_blocking_task where possible Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 07/11] api2/tape/drive: wrap some api calls in run_drive_blocking_task Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 08/11] api2/tape/changer: add drive state to changer status output Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 09/11] api2/tape/{backup, restore}, proxmox-tape: set device state Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 10/11] ui: tape: fix eslint warnings (trailing comma) Dominik Csapak
2021-02-18 14:40 ` [pbs-devel] [PATCH proxmox-backup 11/11] ui: tape/ChangerStatus: show the state of the drive Dominik Csapak
2021-02-18 15:06   ` Dominik Csapak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal