all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Hannes Laimer <h.laimer@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 10/26] api2: admin: add mount-device and list endpoint for RemavableDevices
Date: Tue,  5 Jul 2022 13:08:18 +0000	[thread overview]
Message-ID: <20220705130834.14285-13-h.laimer@proxmox.com> (raw)
In-Reply-To: <20220705130834.14285-1-h.laimer@proxmox.com>

*mount-device: mounts the removable at the path of the associated
datastore, only done if nothing else is already mounted at that path
*list: returns a list of removable devices and their mounting status

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
 pbs-api-types/src/removable_device.rs |  21 +++
 src/api2/admin/mod.rs                 |   2 +
 src/api2/admin/removable_device.rs    | 217 ++++++++++++++++++++++++++
 3 files changed, 240 insertions(+)
 create mode 100644 src/api2/admin/removable_device.rs

diff --git a/pbs-api-types/src/removable_device.rs b/pbs-api-types/src/removable_device.rs
index cb1dca11..94f7e977 100644
--- a/pbs-api-types/src/removable_device.rs
+++ b/pbs-api-types/src/removable_device.rs
@@ -56,3 +56,24 @@ impl RemovableDeviceConfig {
         vec!["datastore", &self.store]
     }
 }
+
+#[api(
+    properties: {
+        config: {
+            type: RemovableDeviceConfig,
+        },
+        mounted: {
+            type: bool,
+        },
+    },
+)]
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+/// Status of a removable device
+pub struct RemovableDeviceStatus {
+    #[serde(flatten)]
+    pub config: RemovableDeviceConfig,
+
+    /// The device is mounted
+    pub mounted: bool,
+}
diff --git a/src/api2/admin/mod.rs b/src/api2/admin/mod.rs
index 9b6fc9ad..00ff9131 100644
--- a/src/api2/admin/mod.rs
+++ b/src/api2/admin/mod.rs
@@ -8,6 +8,7 @@ pub mod datastore;
 pub mod metrics;
 pub mod namespace;
 pub mod prune;
+pub mod removable_device;
 pub mod sync;
 pub mod traffic_control;
 pub mod verify;
@@ -18,6 +19,7 @@ const SUBDIRS: SubdirMap = &sorted!([
     ("metrics", &metrics::ROUTER),
     ("prune", &prune::ROUTER),
     ("sync", &sync::ROUTER),
+    ("removable-device", &removable_device::ROUTER),
     ("traffic-control", &traffic_control::ROUTER),
     ("verify", &verify::ROUTER),
 ]);
diff --git a/src/api2/admin/removable_device.rs b/src/api2/admin/removable_device.rs
new file mode 100644
index 00000000..53b92ba7
--- /dev/null
+++ b/src/api2/admin/removable_device.rs
@@ -0,0 +1,217 @@
+use std::path::PathBuf;
+
+use anyhow::{bail, Error};
+use proxmox_router::{
+    list_subdirs_api_method, ApiMethod, Permission, Router, RpcEnvironment, RpcEnvironmentType,
+    SubdirMap,
+};
+use proxmox_schema::api;
+use proxmox_section_config::SectionConfigData;
+use proxmox_sys::{sortable, task_log, WorkerTaskContext};
+use serde_json::Value;
+
+use pbs_api_types::{
+    Authid, DataStoreConfig, RemovableDeviceConfig, RemovableDeviceStatus, DATASTORE_SCHEMA,
+    DEVICE_NAME_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY,
+};
+use pbs_config::{datastore, removable_device, BackupLockGuard, CachedUserInfo};
+use pbs_datastore::ChunkStore;
+use proxmox_rest_server::WorkerTask;
+
+use crate::tools::disks::{DiskManage, DiskUsageQuery};
+
+fn do_mount_removable(
+    _lock: BackupLockGuard,
+    mut device_section_config: SectionConfigData,
+    mut datastore_section_config: SectionConfigData,
+    mut datastore: DataStoreConfig,
+    mut device: RemovableDeviceConfig,
+    worker: Option<&dyn WorkerTaskContext>,
+) -> Result<(), Error> {
+    if DiskManage::new()
+        .mount_info()?
+        .iter()
+        .filter_map(|(_id, entry)| entry.mount_point.to_str())
+        .any(|mount_point| datastore.path.eq(mount_point))
+    {
+        bail!("something is already mounted at '{}'", &datastore.path);
+    };
+
+    crate::tools::disks::mount_by_uuid(&device.uuid, &datastore.path)?;
+    if !device.initialized {
+        if let Some(worker) = worker {
+            task_log!(
+                worker,
+                "Initializing '{}' {} for datastore {}",
+                device.name,
+                device.uuid,
+                datastore.name
+            );
+        }
+        let path: PathBuf = datastore.path.clone().into();
+
+        let backup_user = pbs_config::backup_user()?;
+        if let Err(e) = ChunkStore::create(
+            &datastore.name,
+            path,
+            backup_user.uid,
+            backup_user.gid,
+            worker,
+        ) {
+            crate::tools::disks::unmount_by_mountpoint(&datastore.path)?;
+            return Err(e);
+        }
+        device.initialized = true;
+    }
+    datastore.maintenance_mode = None;
+
+    device_section_config.set_data(&device.name, "removable-device", &device)?;
+    datastore_section_config.set_data(&datastore.name, "datastore", &datastore)?;
+
+    pbs_config::removable_device::save_config(&device_section_config)?;
+    pbs_config::datastore::save_config(&datastore_section_config)?;
+
+    Ok(())
+}
+
+#[api(
+    input: {
+        properties: {
+            store: {
+                schema: DATASTORE_SCHEMA,
+                optional: true,
+            },
+        },
+    },
+    returns: {
+        description: "List configured removable devices and their status.",
+        type: Array,
+        items: { type: RemovableDeviceStatus },
+    },
+    access: {
+        permission: &Permission::Anybody,
+        description: "Requires Datastore.Audit on datastore.",
+    },
+)]
+/// List all removable devices with their status
+pub fn list_removable_devices(
+    store: Option<String>,
+    _param: Value,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<RemovableDeviceStatus>, Error> {
+    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+    let user_info = CachedUserInfo::new()?;
+
+    let required_privs = PRIV_DATASTORE_AUDIT;
+    let (config, digest) = removable_device::config()?;
+
+    let device_config_iter = config
+        .convert_to_typed_array("removable-device")?
+        .into_iter()
+        .filter(|device: &RemovableDeviceConfig| {
+            let privs = user_info.lookup_privs(&auth_id, &device.acl_path());
+            if privs & required_privs == 0 {
+                return false;
+            }
+            store.as_ref().map_or(true, |store| device.store.eq(store))
+        });
+
+    let mut list = Vec::new();
+
+    for device in device_config_iter {
+        let mounted = DiskUsageQuery::new()
+            .partitions(true)
+            .query()?
+            .iter()
+            .filter_map(|(_path, info)| info.partitions.as_ref())
+            .flatten()
+            .any(|partition| {
+                partition
+                    .uuid
+                    .as_ref()
+                    .map_or(false, |p| p.eq(&device.uuid))
+                    && partition.mounted
+            });
+        list.push(RemovableDeviceStatus {
+            config: device,
+            mounted,
+        });
+    }
+
+    rpcenv["digest"] = hex::encode(&digest).into();
+
+    Ok(list)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            name: {
+                schema: DEVICE_NAME_SCHEMA,
+            }
+        }
+    },
+    access: {
+        permission: &Permission::Anybody,
+        description: "Requires Datastore.Modify on job's datastore.",
+    },
+)]
+/// Mount removable device.
+pub fn mount_removable_device(
+    name: String,
+    _info: &ApiMethod,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<String, Error> {
+    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+    let user_info = CachedUserInfo::new()?;
+
+    let lock = pbs_config::datastore::lock_config()?;
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
+    let (section_config, _digest) = removable_device::config()?;
+    let device_config: RemovableDeviceConfig = section_config.lookup("removable-device", &name)?;
+
+    user_info.check_privs(
+        &auth_id,
+        &device_config.acl_path(),
+        PRIV_DATASTORE_MODIFY,
+        true,
+    )?;
+
+    let (datastore_section_config, _digest) = datastore::config()?;
+    let store: DataStoreConfig =
+        datastore_section_config.lookup("datastore", &device_config.store)?;
+
+    let upid = WorkerTask::new_thread(
+        "mount-device",
+        Some(store.name.to_string()),
+        auth_id.to_string(),
+        to_stdout,
+        move |worker| {
+            do_mount_removable(
+                lock,
+                section_config,
+                datastore_section_config,
+                store,
+                device_config,
+                Some(&worker),
+            )
+        },
+    )?;
+    Ok(upid)
+}
+
+#[sortable]
+const DEVICE_INFO_SUBDIRS: SubdirMap = &[(
+    "mount",
+    &Router::new().post(&API_METHOD_MOUNT_REMOVABLE_DEVICE),
+)];
+
+const DEVICE_INFO_ROUTER: Router = Router::new()
+    .get(&list_subdirs_api_method!(DEVICE_INFO_SUBDIRS))
+    .subdirs(DEVICE_INFO_SUBDIRS);
+
+pub const ROUTER: Router = Router::new()
+    .get(&API_METHOD_LIST_REMOVABLE_DEVICES)
+    .match_all("name", &DEVICE_INFO_ROUTER);
-- 
2.30.2





  parent reply	other threads:[~2022-07-05 13:16 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-05 13:08 [pbs-devel] [SERIES proxmox-backup/proxmox/widget-toolkit 00/28] add removable datastore support Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-widget-toolkit 1/1] proxy: allow setting of reader config Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox 1/1] schema: add print_property_string function Hannes Laimer
2022-07-06 11:31   ` Wolfgang Bumiller
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 01/26] api-types: add RemovableDeviceConfig Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 02/26] config: make RemovableDeviceConfig savable to config file Hannes Laimer
2022-07-06 11:33   ` Wolfgang Bumiller
2022-07-06 11:44     ` Thomas Lamprecht
2022-07-07  8:35       ` Hannes Laimer
2022-07-29  8:26         ` Thomas Lamprecht
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 03/26] api-types: add "removable" field to DataStoreConfig Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 04/26] api2: add config endpoints for RemovableDeviceConfig Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 05/26] api-types: add "unplugged" maintenance type Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 06/26] api-types: add set_maintenance_mode function to DataStoreConfig Hannes Laimer
2022-07-06 11:40   ` Wolfgang Bumiller
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 07/26] api2: datastore create: don't init chunkstore if removable Hannes Laimer
2022-07-06 11:35   ` Wolfgang Bumiller
2022-07-07  9:06     ` Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 08/26] tools: disks: add mount and unmount helper Hannes Laimer
2022-07-06 11:42   ` Wolfgang Bumiller
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 09/26] api2: admin: add unmount-device endpoint to datastore Hannes Laimer
2022-07-06 11:43   ` Wolfgang Bumiller
2022-07-07  9:11     ` Hannes Laimer
2022-07-29  9:24       ` Wolfgang Bumiller
2022-07-05 13:08 ` Hannes Laimer [this message]
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 11/26] tools: disks: add uuid to PrtitionInfo Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 12/26] api-types: add "removable" to DataStoreListItem Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 13/26] ui: utils: remove parseMaintenanceMode function Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 14/26] ui: maintenance edit: disable description in maintenance edit if no type is selected Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 15/26] ui: config: add RemovableDeviceView Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 16/26] ui: add "no removable device present" mask to summary Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 17/26] ui: add removable datastore specific icons to list Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 18/26] ui: window: add RemovableDeviceEdit Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 19/26] ui: form: add PartitionSelector Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 20/26] ui: add "removable" checkbox to datastore edit Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 21/26] ui: disable maintenance update while removable datastore is unplugged Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 22/26] ui: datstore selector: add icon for removable datastores Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 23/26] api2: admin: add mount-device endpoint that just takes an UUID Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 24/26] cli: manager: add removable-device commands Hannes Laimer
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 25/26] debian/etc: add udev rules and simple service for automounting Hannes Laimer
2022-07-06 11:49   ` Wolfgang Bumiller
2022-07-05 13:08 ` [pbs-devel] [PATCH proxmox-backup 26/26] api: mark all removable datastores as 'unplugged' after restart Hannes Laimer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220705130834.14285-13-h.laimer@proxmox.com \
    --to=h.laimer@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal