* [pbs-devel] [PATCH proxmox-backup v7 01/20] tools: add disks utility functions
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 9:54 ` Christian Ebner
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 02/20] pbs-api-types: add backing-device to DataStoreConfig Hannes Laimer
` (19 subsequent siblings)
20 siblings, 1 reply; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
... for mounting and unmounting
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
src/tools/disks/mod.rs | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/src/tools/disks/mod.rs b/src/tools/disks/mod.rs
index 94f89e0a..68854918 100644
--- a/src/tools/disks/mod.rs
+++ b/src/tools/disks/mod.rs
@@ -1323,3 +1323,22 @@ pub fn get_fs_uuid(disk: &Disk) -> Result<String, Error> {
bail!("get_fs_uuid failed - missing UUID");
}
+
+/// Mount a disk by its UUID and the mount point.
+pub fn mount_by_uuid(uuid: &str, mount_point: &Path) -> Result<(), Error> {
+ let mut command = std::process::Command::new("mount");
+ command.args([&format!("UUID={uuid}")]);
+ command.arg(mount_point);
+
+ proxmox_sys::command::run_command(command, None)?;
+ Ok(())
+}
+
+/// Unmount a disk by its mount point.
+pub fn unmount_by_mountpoint(path: &str) -> Result<(), Error> {
+ let mut command = std::process::Command::new("umount");
+ command.arg(path);
+
+ proxmox_sys::command::run_command(command, None)?;
+ Ok(())
+}
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v7 01/20] tools: add disks utility functions
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 01/20] tools: add disks utility functions Hannes Laimer
@ 2024-04-19 9:54 ` Christian Ebner
0 siblings, 0 replies; 27+ messages in thread
From: Christian Ebner @ 2024-04-19 9:54 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Hannes Laimer
nit inline
On 4/19/24 10:58, Hannes Laimer wrote:
> ... for mounting and unmounting
>
> Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
> ---
> src/tools/disks/mod.rs | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/src/tools/disks/mod.rs b/src/tools/disks/mod.rs
> index 94f89e0a..68854918 100644
> --- a/src/tools/disks/mod.rs
> +++ b/src/tools/disks/mod.rs
> @@ -1323,3 +1323,22 @@ pub fn get_fs_uuid(disk: &Disk) -> Result<String, Error> {
>
> bail!("get_fs_uuid failed - missing UUID");
> }
> +
> +/// Mount a disk by its UUID and the mount point.
> +pub fn mount_by_uuid(uuid: &str, mount_point: &Path) -> Result<(), Error> {
> + let mut command = std::process::Command::new("mount");
> + command.args([&format!("UUID={uuid}")]);
this should be: `command.arg(&format!("UUID={uuid}"));`, or pass already
both arguments when using args.
> + command.arg(mount_point);
> +
> + proxmox_sys::command::run_command(command, None)?;
> + Ok(())
> +}
> +
> +/// Unmount a disk by its mount point.
> +pub fn unmount_by_mountpoint(path: &str) -> Result<(), Error> {
> + let mut command = std::process::Command::new("umount");
> + command.arg(path);
> +
> + proxmox_sys::command::run_command(command, None)?;
> + Ok(())
> +}
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 02/20] pbs-api-types: add backing-device to DataStoreConfig
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 01/20] tools: add disks utility functions Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 03/20] disks: add UUID to partition info Hannes Laimer
` (18 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pbs-api-types/src/datastore.rs | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 9fe45bc0..8cb51708 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -273,6 +273,12 @@ pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new("Datastore
format: &ApiStringFormat::PropertyString(&MaintenanceMode::API_SCHEMA),
type: String,
},
+ "backing-device": {
+ description: "The UUID of the filesystem partition for removable datastores.",
+ optional: true,
+ format: &proxmox_schema::api_types::UUID_FORMAT,
+ type: String,
+ }
}
)]
#[derive(Serialize, Deserialize, Updater, Clone, PartialEq)]
@@ -316,6 +322,11 @@ pub struct DataStoreConfig {
/// Maintenance mode, type is either 'offline' or 'read-only', message should be enclosed in "
#[serde(skip_serializing_if = "Option::is_none")]
pub maintenance_mode: Option<String>,
+
+ /// The UUID of the device(for removable datastores)
+ #[updater(skip)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub backing_device: Option<String>,
}
impl DataStoreConfig {
@@ -332,6 +343,7 @@ impl DataStoreConfig {
notify: None,
tuning: None,
maintenance_mode: None,
+ backing_device: None,
}
}
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 03/20] disks: add UUID to partition info
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 01/20] tools: add disks utility functions Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 02/20] pbs-api-types: add backing-device to DataStoreConfig Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available Hannes Laimer
` (17 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
src/tools/disks/mod.rs | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/tools/disks/mod.rs b/src/tools/disks/mod.rs
index 68854918..eaf016df 100644
--- a/src/tools/disks/mod.rs
+++ b/src/tools/disks/mod.rs
@@ -59,6 +59,8 @@ pub struct LsblkInfo {
/// File system label.
#[serde(rename = "fstype")]
file_system_type: Option<String>,
+ /// File system UUID.
+ uuid: Option<String>,
}
impl DiskManage {
@@ -617,7 +619,7 @@ pub struct BlockDevStat {
/// Use lsblk to read partition type uuids and file system types.
pub fn get_lsblk_info() -> Result<Vec<LsblkInfo>, Error> {
let mut command = std::process::Command::new("lsblk");
- command.args(["--json", "-o", "path,parttype,fstype"]);
+ command.args(["--json", "-o", "path,parttype,fstype,uuid"]);
let output = proxmox_sys::command::run_command(command, None)?;
@@ -701,6 +703,8 @@ pub struct PartitionInfo {
pub size: Option<u64>,
/// GPT partition
pub gpt: bool,
+ /// UUID
+ pub uuid: Option<String>,
}
#[api(
@@ -891,8 +895,10 @@ fn get_partitions_info(
let mounted = disk.is_mounted().unwrap_or(false);
let mut filesystem = None;
+ let mut uuid = None;
if let (Some(devpath), Some(infos)) = (devpath.as_ref(), lsblk_infos.as_ref()) {
for info in infos.iter().filter(|i| i.path.eq(devpath)) {
+ uuid = info.uuid.clone();
used = match info.partition_type.as_deref() {
Some("21686148-6449-6e6f-744e-656564454649") => PartitionUsageType::BIOS,
Some("c12a7328-f81f-11d2-ba4b-00a0c93ec93b") => PartitionUsageType::EFI,
@@ -915,6 +921,7 @@ fn get_partitions_info(
filesystem,
size: disk.size().ok(),
gpt: disk.has_gpt(),
+ uuid,
}
})
.collect()
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (2 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 03/20] disks: add UUID to partition info Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 9:14 ` Christian Ebner
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 05/20] api: admin: add (un)mount endpoint for removable datastores Hannes Laimer
` (16 subsequent siblings)
20 siblings, 1 reply; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pbs-api-types/src/maintenance.rs | 2 +-
pbs-datastore/src/datastore.rs | 44 ++++++++++++++++++++++++++++++++
pbs-datastore/src/lib.rs | 2 +-
3 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
index fd4d3416..4f653ec7 100644
--- a/pbs-api-types/src/maintenance.rs
+++ b/pbs-api-types/src/maintenance.rs
@@ -81,7 +81,7 @@ impl MaintenanceMode {
/// Used for deciding whether the datastore is cleared from the internal cache after the last
/// task finishes, so all open files are closed.
pub fn is_offline(&self) -> bool {
- self.ty == MaintenanceType::Offline
+ self.ty == MaintenanceType::Offline || self.ty == MaintenanceType::Unmount
}
pub fn check(&self, operation: Option<Operation>) -> Result<(), Error> {
diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index f95da761..274f1506 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -14,6 +14,7 @@ use proxmox_schema::ApiType;
use proxmox_sys::error::SysError;
use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
use proxmox_sys::fs::{lock_dir_noblock, DirLockGuard};
+use proxmox_sys::linux::procfs::MountInfo;
use proxmox_sys::process_locker::ProcessLockSharedGuard;
use proxmox_sys::WorkerTaskContext;
use proxmox_sys::{task_log, task_warn};
@@ -49,6 +50,39 @@ pub fn check_backup_owner(owner: &Authid, auth_id: &Authid) -> Result<(), Error>
Ok(())
}
+/// check if a removable datastore is currently available/mounted by using the UUID
+/// to find the `/dev/sdX` path and checking in /proc/self/mountinfo for entries containing it.
+/// If it should mounted, but not in the expected path, so config.path, it is considered not
+/// available.
+pub fn is_datastore_available(config: &DataStoreConfig) -> bool {
+ config.backing_device.as_ref().map_or(true, |uuid| {
+ if let Ok(store_dev_path) = std::fs::read_link(Path::new("/dev/disk/by-uuid").join(uuid))
+ .map(|p| p.to_string_lossy().into_owned())
+ .map(|p| p.replace("../..", "/dev"))
+ .map(PathBuf::from)
+ {
+ let store_mount_point = PathBuf::from(&config.path);
+
+ MountInfo::read().map_or(false, |mount_info| {
+ mount_info
+ .iter()
+ .filter_map(|(_, entry)| {
+ entry
+ .mount_source
+ .as_ref()
+ .map(|source| (&entry.mount_point, source))
+ })
+ .any(|(mount_point, mount_source)| {
+ PathBuf::from(&mount_point) == store_mount_point
+ && PathBuf::from(&mount_source) == store_dev_path
+ })
+ })
+ } else {
+ false
+ }
+ })
+}
+
/// Datastore Management
///
/// A Datastore can store severals backups, and provides the
@@ -158,6 +192,12 @@ impl DataStore {
}
}
+ if config.backing_device.is_some() && !is_datastore_available(&config) {
+ let mut datastore_cache = DATASTORE_MAP.lock().unwrap();
+ datastore_cache.remove(&config.name);
+ bail!("Removable Datastore is not mounted");
+ }
+
if let Some(operation) = operation {
update_active_operations(name, operation, 1)?;
}
@@ -261,6 +301,10 @@ impl DataStore {
) -> Result<Arc<Self>, Error> {
let name = config.name.clone();
+ if !is_datastore_available(&config) {
+ bail!("Datastore is not available")
+ }
+
let tuning: DatastoreTuning = serde_json::from_value(
DatastoreTuning::API_SCHEMA
.parse_property_string(config.tuning.as_deref().unwrap_or(""))?,
diff --git a/pbs-datastore/src/lib.rs b/pbs-datastore/src/lib.rs
index 43050162..458f93d9 100644
--- a/pbs-datastore/src/lib.rs
+++ b/pbs-datastore/src/lib.rs
@@ -206,7 +206,7 @@ pub use manifest::BackupManifest;
pub use store_progress::StoreProgress;
mod datastore;
-pub use datastore::{check_backup_owner, DataStore};
+pub use datastore::{check_backup_owner, is_datastore_available, DataStore};
mod hierarchy;
pub use hierarchy::{
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available Hannes Laimer
@ 2024-04-19 9:14 ` Christian Ebner
2024-04-19 15:35 ` Hannes Laimer
0 siblings, 1 reply; 27+ messages in thread
From: Christian Ebner @ 2024-04-19 9:14 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Hannes Laimer
On 4/19/24 10:58, Hannes Laimer wrote:
> Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
> ---
> pbs-api-types/src/maintenance.rs | 2 +-
> pbs-datastore/src/datastore.rs | 44 ++++++++++++++++++++++++++++++++
> pbs-datastore/src/lib.rs | 2 +-
> 3 files changed, 46 insertions(+), 2 deletions(-)
>
> diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
> index fd4d3416..4f653ec7 100644
> --- a/pbs-api-types/src/maintenance.rs
> +++ b/pbs-api-types/src/maintenance.rs
> @@ -81,7 +81,7 @@ impl MaintenanceMode {
> /// Used for deciding whether the datastore is cleared from the internal cache after the last
> /// task finishes, so all open files are closed.
> pub fn is_offline(&self) -> bool {
> - self.ty == MaintenanceType::Offline
> + self.ty == MaintenanceType::Offline || self.ty == MaintenanceType::Unmount
> }
>
> pub fn check(&self, operation: Option<Operation>) -> Result<(), Error> {
> diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
> index f95da761..274f1506 100644
> --- a/pbs-datastore/src/datastore.rs
> +++ b/pbs-datastore/src/datastore.rs
> @@ -14,6 +14,7 @@ use proxmox_schema::ApiType;
> use proxmox_sys::error::SysError;
> use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
> use proxmox_sys::fs::{lock_dir_noblock, DirLockGuard};
> +use proxmox_sys::linux::procfs::MountInfo;
> use proxmox_sys::process_locker::ProcessLockSharedGuard;
> use proxmox_sys::WorkerTaskContext;
> use proxmox_sys::{task_log, task_warn};
> @@ -49,6 +50,39 @@ pub fn check_backup_owner(owner: &Authid, auth_id: &Authid) -> Result<(), Error>
> Ok(())
> }
>
> +/// check if a removable datastore is currently available/mounted by using the UUID
> +/// to find the `/dev/sdX` path and checking in /proc/self/mountinfo for entries containing it.
> +/// If it should mounted, but not in the expected path, so config.path, it is considered not
> +/// available.
> +pub fn is_datastore_available(config: &DataStoreConfig) -> bool {
> + config.backing_device.as_ref().map_or(true, |uuid| {
> + if let Ok(store_dev_path) = std::fs::read_link(Path::new("/dev/disk/by-uuid").join(uuid))
> + .map(|p| p.to_string_lossy().into_owned())
while this probably never happens for this case, the `to_string_lossy`
might replace non UTF-8 characters here.
So I think this and the next two map calls should be replaced with a
`canonicalize` of `PathBuf`, which also resolves the symlink?
https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.canonicalize
> + .map(|p| p.replace("../..", "/dev"))
> + .map(PathBuf::from)
> + {
> + let store_mount_point = PathBuf::from(&config.path);
> +
> + MountInfo::read().map_or(false, |mount_info| {
> + mount_info
> + .iter()
> + .filter_map(|(_, entry)| {
> + entry
> + .mount_source
> + .as_ref()
> + .map(|source| (&entry.mount_point, source))
> + })
> + .any(|(mount_point, mount_source)| {
> + PathBuf::from(&mount_point) == store_mount_point
> + && PathBuf::from(&mount_source) == store_dev_path
> + })
> + })
> + } else {
> + false
> + }
> + })
> +}
> +
> /// Datastore Management
> ///
> /// A Datastore can store severals backups, and provides the
> @@ -158,6 +192,12 @@ impl DataStore {
> }
> }
>
> + if config.backing_device.is_some() && !is_datastore_available(&config) {
> + let mut datastore_cache = DATASTORE_MAP.lock().unwrap();
> + datastore_cache.remove(&config.name);
> + bail!("Removable Datastore is not mounted");
> + }
> +
> if let Some(operation) = operation {
> update_active_operations(name, operation, 1)?;
> }
> @@ -261,6 +301,10 @@ impl DataStore {
> ) -> Result<Arc<Self>, Error> {
> let name = config.name.clone();
>
> + if !is_datastore_available(&config) {
> + bail!("Datastore is not available")
> + }
> +
> let tuning: DatastoreTuning = serde_json::from_value(
> DatastoreTuning::API_SCHEMA
> .parse_property_string(config.tuning.as_deref().unwrap_or(""))?,
> diff --git a/pbs-datastore/src/lib.rs b/pbs-datastore/src/lib.rs
> index 43050162..458f93d9 100644
> --- a/pbs-datastore/src/lib.rs
> +++ b/pbs-datastore/src/lib.rs
> @@ -206,7 +206,7 @@ pub use manifest::BackupManifest;
> pub use store_progress::StoreProgress;
>
> mod datastore;
> -pub use datastore::{check_backup_owner, DataStore};
> +pub use datastore::{check_backup_owner, is_datastore_available, DataStore};
>
> mod hierarchy;
> pub use hierarchy::{
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available
2024-04-19 9:14 ` Christian Ebner
@ 2024-04-19 15:35 ` Hannes Laimer
2024-04-22 8:23 ` Christian Ebner
0 siblings, 1 reply; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 15:35 UTC (permalink / raw)
To: Christian Ebner, Proxmox Backup Server development discussion
On Fri Apr 19, 2024 at 11:14 AM CEST, Christian Ebner wrote:
> On 4/19/24 10:58, Hannes Laimer wrote:
> > Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
> > ---
> > pbs-api-types/src/maintenance.rs | 2 +-
> > pbs-datastore/src/datastore.rs | 44 ++++++++++++++++++++++++++++++++
> > pbs-datastore/src/lib.rs | 2 +-
> > 3 files changed, 46 insertions(+), 2 deletions(-)
> >
> > diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
> > index fd4d3416..4f653ec7 100644
> > --- a/pbs-api-types/src/maintenance.rs
> > +++ b/pbs-api-types/src/maintenance.rs
> > @@ -81,7 +81,7 @@ impl MaintenanceMode {
> > /// Used for deciding whether the datastore is cleared from the internal cache after the last
> > /// task finishes, so all open files are closed.
> > pub fn is_offline(&self) -> bool {
> > - self.ty == MaintenanceType::Offline
> > + self.ty == MaintenanceType::Offline || self.ty == MaintenanceType::Unmount
> > }
> >
> > pub fn check(&self, operation: Option<Operation>) -> Result<(), Error> {
> > diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
> > index f95da761..274f1506 100644
> > --- a/pbs-datastore/src/datastore.rs
> > +++ b/pbs-datastore/src/datastore.rs
> > @@ -14,6 +14,7 @@ use proxmox_schema::ApiType;
> > use proxmox_sys::error::SysError;
> > use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
> > use proxmox_sys::fs::{lock_dir_noblock, DirLockGuard};
> > +use proxmox_sys::linux::procfs::MountInfo;
> > use proxmox_sys::process_locker::ProcessLockSharedGuard;
> > use proxmox_sys::WorkerTaskContext;
> > use proxmox_sys::{task_log, task_warn};
> > @@ -49,6 +50,39 @@ pub fn check_backup_owner(owner: &Authid, auth_id: &Authid) -> Result<(), Error>
> > Ok(())
> > }
> >
> > +/// check if a removable datastore is currently available/mounted by using the UUID
> > +/// to find the `/dev/sdX` path and checking in /proc/self/mountinfo for entries containing it.
> > +/// If it should mounted, but not in the expected path, so config.path, it is considered not
> > +/// available.
> > +pub fn is_datastore_available(config: &DataStoreConfig) -> bool {
> > + config.backing_device.as_ref().map_or(true, |uuid| {
> > + if let Ok(store_dev_path) = std::fs::read_link(Path::new("/dev/disk/by-uuid").join(uuid))
> > + .map(|p| p.to_string_lossy().into_owned())
>
> while this probably never happens for this case, the `to_string_lossy`
> might replace non UTF-8 characters here.
>
> So I think this and the next two map calls should be replaced with a
> `canonicalize` of `PathBuf`, which also resolves the symlink?
>
> https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.canonicalize
>
doesn't work, I'm not 100% sure, but I think it's beacuse it uses the
running dir of the binary as a base
> > + .map(|p| p.replace("../..", "/dev"))
> > + .map(PathBuf::from)
> > + {
> > + let store_mount_point = PathBuf::from(&config.path);
> > +
> > + MountInfo::read().map_or(false, |mount_info| {
> > + mount_info
> > + .iter()
> > + .filter_map(|(_, entry)| {
> > + entry
> > + .mount_source
> > + .as_ref()
> > + .map(|source| (&entry.mount_point, source))
> > + })
> > + .any(|(mount_point, mount_source)| {
> > + PathBuf::from(&mount_point) == store_mount_point
> > + && PathBuf::from(&mount_source) == store_dev_path
> > + })
> > + })
> > + } else {
> > + false
> > + }
> > + })
> > +}
> > +
> > /// Datastore Management
> > ///
> > /// A Datastore can store severals backups, and provides the
> > @@ -158,6 +192,12 @@ impl DataStore {
> > }
> > }
> >
> > + if config.backing_device.is_some() && !is_datastore_available(&config) {
> > + let mut datastore_cache = DATASTORE_MAP.lock().unwrap();
> > + datastore_cache.remove(&config.name);
> > + bail!("Removable Datastore is not mounted");
> > + }
> > +
> > if let Some(operation) = operation {
> > update_active_operations(name, operation, 1)?;
> > }
> > @@ -261,6 +301,10 @@ impl DataStore {
> > ) -> Result<Arc<Self>, Error> {
> > let name = config.name.clone();
> >
> > + if !is_datastore_available(&config) {
> > + bail!("Datastore is not available")
> > + }
> > +
> > let tuning: DatastoreTuning = serde_json::from_value(
> > DatastoreTuning::API_SCHEMA
> > .parse_property_string(config.tuning.as_deref().unwrap_or(""))?,
> > diff --git a/pbs-datastore/src/lib.rs b/pbs-datastore/src/lib.rs
> > index 43050162..458f93d9 100644
> > --- a/pbs-datastore/src/lib.rs
> > +++ b/pbs-datastore/src/lib.rs
> > @@ -206,7 +206,7 @@ pub use manifest::BackupManifest;
> > pub use store_progress::StoreProgress;
> >
> > mod datastore;
> > -pub use datastore::{check_backup_owner, DataStore};
> > +pub use datastore::{check_backup_owner, is_datastore_available, DataStore};
> >
> > mod hierarchy;
> > pub use hierarchy::{
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available
2024-04-19 15:35 ` Hannes Laimer
@ 2024-04-22 8:23 ` Christian Ebner
0 siblings, 0 replies; 27+ messages in thread
From: Christian Ebner @ 2024-04-22 8:23 UTC (permalink / raw)
To: Hannes Laimer, Proxmox Backup Server development discussion
On 4/19/24 17:35, Hannes Laimer wrote:
> On Fri Apr 19, 2024 at 11:14 AM CEST, Christian Ebner wrote:
>> On 4/19/24 10:58, Hannes Laimer wrote:
>>> Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
>>> ---
>>> pbs-api-types/src/maintenance.rs | 2 +-
>>> pbs-datastore/src/datastore.rs | 44 ++++++++++++++++++++++++++++++++
>>> pbs-datastore/src/lib.rs | 2 +-
>>> 3 files changed, 46 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
>>> index fd4d3416..4f653ec7 100644
>>> --- a/pbs-api-types/src/maintenance.rs
>>> +++ b/pbs-api-types/src/maintenance.rs
>>> @@ -81,7 +81,7 @@ impl MaintenanceMode {
>>> /// Used for deciding whether the datastore is cleared from the internal cache after the last
>>> /// task finishes, so all open files are closed.
>>> pub fn is_offline(&self) -> bool {
>>> - self.ty == MaintenanceType::Offline
>>> + self.ty == MaintenanceType::Offline || self.ty == MaintenanceType::Unmount
>>> }
>>>
>>> pub fn check(&self, operation: Option<Operation>) -> Result<(), Error> {
>>> diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
>>> index f95da761..274f1506 100644
>>> --- a/pbs-datastore/src/datastore.rs
>>> +++ b/pbs-datastore/src/datastore.rs
>>> @@ -14,6 +14,7 @@ use proxmox_schema::ApiType;
>>> use proxmox_sys::error::SysError;
>>> use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
>>> use proxmox_sys::fs::{lock_dir_noblock, DirLockGuard};
>>> +use proxmox_sys::linux::procfs::MountInfo;
>>> use proxmox_sys::process_locker::ProcessLockSharedGuard;
>>> use proxmox_sys::WorkerTaskContext;
>>> use proxmox_sys::{task_log, task_warn};
>>> @@ -49,6 +50,39 @@ pub fn check_backup_owner(owner: &Authid, auth_id: &Authid) -> Result<(), Error>
>>> Ok(())
>>> }
>>>
>>> +/// check if a removable datastore is currently available/mounted by using the UUID
>>> +/// to find the `/dev/sdX` path and checking in /proc/self/mountinfo for entries containing it.
>>> +/// If it should mounted, but not in the expected path, so config.path, it is considered not
>>> +/// available.
>>> +pub fn is_datastore_available(config: &DataStoreConfig) -> bool {
>>> + config.backing_device.as_ref().map_or(true, |uuid| {
>>> + if let Ok(store_dev_path) = std::fs::read_link(Path::new("/dev/disk/by-uuid").join(uuid))
>>> + .map(|p| p.to_string_lossy().into_owned())
>>
>> while this probably never happens for this case, the `to_string_lossy`
>> might replace non UTF-8 characters here.
>>
>> So I think this and the next two map calls should be replaced with a
>> `canonicalize` of `PathBuf`, which also resolves the symlink?
>>
>> https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.canonicalize
>>
>
> doesn't work, I'm not 100% sure, but I think it's beacuse it uses the
> running dir of the binary as a base
True, but we can work around that by adding the base if the symlink is a
relative path. This should do the trick:
```
config.backing_device.as_ref().map_or(true, |uuid| {
let mut target = PathBuf::from("/dev/disk/by-uuid");
if let Ok(store_dev_path) = std::fs::read_link(target.join(uuid))
.map(|link_target| {
// Replaces current path buffer content if link_target
is absolute path
//
https://docs.rs/rustc-std-workspace-std/latest/std/path/struct.PathBuf.html#method.push
target.push(link_target);
target
})
.and_then(|target| target.canonicalize())
{
```
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 05/20] api: admin: add (un)mount endpoint for removable datastores
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (3 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 04/20] datastore: add helper for checking if a removable datastore is available Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 10:21 ` Christian Ebner
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 06/20] api: removable datastore creation Hannes Laimer
` (15 subsequent siblings)
20 siblings, 1 reply; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pbs-api-types/src/maintenance.rs | 4 +
src/api2/admin/datastore.rs | 183 +++++++++++++++++++++++++++++--
2 files changed, 177 insertions(+), 10 deletions(-)
diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
index 4f653ec7..469dc25d 100644
--- a/pbs-api-types/src/maintenance.rs
+++ b/pbs-api-types/src/maintenance.rs
@@ -78,6 +78,10 @@ pub struct MaintenanceMode {
}
impl MaintenanceMode {
+ pub fn new(ty: MaintenanceType, message: Option<String>) -> Self {
+ Self { ty, message }
+ }
+
/// Used for deciding whether the datastore is cleared from the internal cache after the last
/// task finishes, so all open files are closed.
pub fn is_offline(&self) -> bool {
diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 35628c59..07676a27 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -26,20 +26,21 @@ use proxmox_sortable_macro::sortable;
use proxmox_sys::fs::{
file_read_firstline, file_read_optional_string, replace_file, CreateOptions,
};
-use proxmox_sys::{task_log, task_warn};
+use proxmox_sys::{task_log, task_warn, WorkerTaskContext};
use pxar::accessor::aio::Accessor;
use pxar::EntryKind;
use pbs_api_types::{
print_ns_and_snapshot, print_store_and_ns, Authid, BackupContent, BackupNamespace, BackupType,
- Counts, CryptMode, DataStoreListItem, DataStoreStatus, GarbageCollectionStatus, GroupListItem,
- KeepOptions, Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotListItem,
- SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA,
- BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA,
- MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
- PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY,
- UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
+ Counts, CryptMode, DataStoreConfig, DataStoreListItem, DataStoreStatus,
+ GarbageCollectionStatus, GroupListItem, KeepOptions, MaintenanceMode, MaintenanceType,
+ Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotListItem, SnapshotVerifyState,
+ BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA,
+ BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, MAX_NAMESPACE_DEPTH,
+ NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY,
+ PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, UPID_SCHEMA,
+ VERIFICATION_OUTDATED_AFTER_SCHEMA,
};
use pbs_client::pxar::{create_tar, create_zip};
use pbs_config::CachedUserInfo;
@@ -54,8 +55,8 @@ use pbs_datastore::index::IndexFile;
use pbs_datastore::manifest::{BackupManifest, CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME};
use pbs_datastore::prune::compute_prune_info;
use pbs_datastore::{
- check_backup_owner, task_tracking, BackupDir, BackupGroup, DataStore, LocalChunkReader,
- StoreProgress, CATALOG_NAME,
+ check_backup_owner, is_datastore_available, task_tracking, BackupDir, BackupGroup, DataStore,
+ LocalChunkReader, StoreProgress, CATALOG_NAME,
};
use pbs_tools::json::required_string_param;
use proxmox_rest_server::{formatter, WorkerTask};
@@ -2278,6 +2279,166 @@ pub async fn set_backup_owner(
.await?
}
+pub fn do_mount_device(
+ datastore: DataStoreConfig,
+ worker: Option<&dyn WorkerTaskContext>,
+) -> Result<(), Error> {
+ if let Some(uuid) = datastore.backing_device.as_ref() {
+ if pbs_datastore::is_datastore_available(&datastore) {
+ bail!("device '{uuid}' is already mounted");
+ }
+ let mount_point_path = std::path::Path::new(&datastore.path);
+ if let Some(worker) = worker {
+ task_log!(
+ worker,
+ "mounting '{uuid}' for store {} to '{}'",
+ datastore.name,
+ datastore.path
+ );
+ }
+ crate::tools::disks::mount_by_uuid(uuid, mount_point_path)?;
+
+ Ok(())
+ } else {
+ Err(format_err!(
+ "Datastore '{}' cannot be mounted because it is not removable.",
+ datastore.name
+ ))
+ }
+}
+
+#[api(
+ protected: true,
+ input: {
+ properties: {
+ store: {
+ schema: DATASTORE_SCHEMA,
+ },
+ }
+ },
+ returns: {
+ schema: UPID_SCHEMA,
+ },
+ access: {
+ permission: &Permission::Privilege(&["datastore", "{store}"], PRIV_DATASTORE_AUDIT, false),
+ },
+)]
+/// Mount removable datastore.
+pub fn mount(store: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+ let (section_config, _digest) = pbs_config::datastore::config()?;
+ let datastore: DataStoreConfig = section_config.lookup("datastore", &store)?;
+
+ if datastore.backing_device.is_none() {
+ bail!("datastore '{store}' is not removable");
+ }
+
+ let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+ let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
+ let upid = WorkerTask::new_thread(
+ "mount-device",
+ Some(store),
+ auth_id.to_string(),
+ to_stdout,
+ move |worker| do_mount_device(datastore, Some(&worker)),
+ )?;
+
+ Ok(json!(upid))
+}
+
+fn do_unmount_device(
+ datastore: DataStoreConfig,
+ worker: Option<&dyn WorkerTaskContext>,
+) -> Result<(), Error> {
+ let mut active_operations = task_tracking::get_active_operations(&datastore.name)?;
+ let mut old_status = String::new();
+ while active_operations.read + active_operations.write > 0 {
+ if let Some(worker) = worker {
+ if worker.abort_requested() {
+ bail!("aborted, due to user request");
+ }
+ let status = format!(
+ "cannot unmount yet, still {} read and {} write operations active",
+ active_operations.read, active_operations.write
+ );
+ if status != old_status {
+ task_log!(worker, "{}", status);
+ old_status = status;
+ }
+ }
+ std::thread::sleep(std::time::Duration::from_millis(250));
+ active_operations = task_tracking::get_active_operations(&datastore.name)?;
+ }
+ crate::tools::disks::unmount_by_mountpoint(&datastore.path)?;
+
+ let _lock = pbs_config::datastore::lock_config()?;
+ let (mut section_config, _digest) = pbs_config::datastore::config()?;
+ let mut store_config: DataStoreConfig = section_config.lookup("datastore", &datastore.name)?;
+ store_config.maintenance_mode = None;
+ section_config.set_data(&datastore.name, "datastore", &store_config)?;
+ pbs_config::datastore::save_config(§ion_config)?;
+
+ Ok(())
+}
+
+#[api(
+ protected: true,
+ input: {
+ properties: {
+ store: { schema: DATASTORE_SCHEMA },
+ },
+ },
+ returns: {
+ schema: UPID_SCHEMA,
+ },
+ access: {
+ permission: &Permission::Privilege(&["datastore", "{store}"], PRIV_DATASTORE_MODIFY, true),
+ }
+)]
+/// Unmount a removable device that is associated with the datastore
+pub async fn unmount(store: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+ let _lock = pbs_config::datastore::lock_config()?;
+ let (mut section_config, _digest) = pbs_config::datastore::config()?;
+ let mut datastore: DataStoreConfig = section_config.lookup("datastore", &store)?;
+
+ if datastore.backing_device.is_none() {
+ bail!("datastore '{}' is not removable", &store);
+ }
+
+ if !is_datastore_available(&datastore) {
+ bail!("datastore '{}' is not mounted", &store);
+ }
+
+ datastore.set_maintenance_mode(Some(MaintenanceMode::new(MaintenanceType::Unmount, None)))?;
+ section_config.set_data(&store, "datastore", &datastore)?;
+ pbs_config::datastore::save_config(§ion_config)?;
+
+ drop(_lock);
+
+ let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+ let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
+ if let Ok(proxy_pid) = proxmox_rest_server::read_pid(pbs_buildcfg::PROXMOX_BACKUP_PROXY_PID_FN)
+ {
+ let sock = proxmox_rest_server::ctrl_sock_from_pid(proxy_pid);
+ let _ = proxmox_rest_server::send_raw_command(
+ sock,
+ &format!("{{\"command\":\"update-datastore-cache\",\"args\":\"{store}\"}}\n"),
+ )
+ .await;
+ }
+
+ let upid = WorkerTask::new_thread(
+ "unmount-device",
+ Some(store),
+ auth_id.to_string(),
+ to_stdout,
+ move |worker| do_unmount_device(datastore, Some(&worker)),
+ )?;
+
+ Ok(json!(upid))
+}
+
#[sortable]
const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
(
@@ -2316,6 +2477,7 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
.get(&API_METHOD_LIST_GROUPS)
.delete(&API_METHOD_DELETE_GROUP),
),
+ ("mount", &Router::new().post(&API_METHOD_MOUNT)),
(
"namespace",
// FIXME: move into datastore:: sub-module?!
@@ -2350,6 +2512,7 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
.delete(&API_METHOD_DELETE_SNAPSHOT),
),
("status", &Router::new().get(&API_METHOD_STATUS)),
+ ("unmount", &Router::new().post(&API_METHOD_UNMOUNT)),
(
"upload-backup-log",
&Router::new().upload(&API_METHOD_UPLOAD_BACKUP_LOG),
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v7 05/20] api: admin: add (un)mount endpoint for removable datastores
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 05/20] api: admin: add (un)mount endpoint for removable datastores Hannes Laimer
@ 2024-04-19 10:21 ` Christian Ebner
0 siblings, 0 replies; 27+ messages in thread
From: Christian Ebner @ 2024-04-19 10:21 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Hannes Laimer
nits inline
On 4/19/24 10:58, Hannes Laimer wrote:
> Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
> ---
> pbs-api-types/src/maintenance.rs | 4 +
> src/api2/admin/datastore.rs | 183 +++++++++++++++++++++++++++++--
> 2 files changed, 177 insertions(+), 10 deletions(-)
>
> diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
> index 4f653ec7..469dc25d 100644
> --- a/pbs-api-types/src/maintenance.rs
> +++ b/pbs-api-types/src/maintenance.rs
> @@ -78,6 +78,10 @@ pub struct MaintenanceMode {
> }
>
> impl MaintenanceMode {
> + pub fn new(ty: MaintenanceType, message: Option<String>) -> Self {
> + Self { ty, message }
> + }
> +
> /// Used for deciding whether the datastore is cleared from the internal cache after the last
> /// task finishes, so all open files are closed.
> pub fn is_offline(&self) -> bool {
> diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
> index 35628c59..07676a27 100644
> --- a/src/api2/admin/datastore.rs
> +++ b/src/api2/admin/datastore.rs
> @@ -26,20 +26,21 @@ use proxmox_sortable_macro::sortable;
> use proxmox_sys::fs::{
> file_read_firstline, file_read_optional_string, replace_file, CreateOptions,
> };
> -use proxmox_sys::{task_log, task_warn};
> +use proxmox_sys::{task_log, task_warn, WorkerTaskContext};
>
> use pxar::accessor::aio::Accessor;
> use pxar::EntryKind;
>
> use pbs_api_types::{
> print_ns_and_snapshot, print_store_and_ns, Authid, BackupContent, BackupNamespace, BackupType,
> - Counts, CryptMode, DataStoreListItem, DataStoreStatus, GarbageCollectionStatus, GroupListItem,
> - KeepOptions, Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotListItem,
> - SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA,
> - BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA,
> - MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
> - PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY,
> - UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
> + Counts, CryptMode, DataStoreConfig, DataStoreListItem, DataStoreStatus,
> + GarbageCollectionStatus, GroupListItem, KeepOptions, MaintenanceMode, MaintenanceType,
> + Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotListItem, SnapshotVerifyState,
> + BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA,
> + BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, MAX_NAMESPACE_DEPTH,
> + NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY,
> + PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, UPID_SCHEMA,
> + VERIFICATION_OUTDATED_AFTER_SCHEMA,
> };
> use pbs_client::pxar::{create_tar, create_zip};
> use pbs_config::CachedUserInfo;
> @@ -54,8 +55,8 @@ use pbs_datastore::index::IndexFile;
> use pbs_datastore::manifest::{BackupManifest, CLIENT_LOG_BLOB_NAME, MANIFEST_BLOB_NAME};
> use pbs_datastore::prune::compute_prune_info;
> use pbs_datastore::{
> - check_backup_owner, task_tracking, BackupDir, BackupGroup, DataStore, LocalChunkReader,
> - StoreProgress, CATALOG_NAME,
> + check_backup_owner, is_datastore_available, task_tracking, BackupDir, BackupGroup, DataStore,
> + LocalChunkReader, StoreProgress, CATALOG_NAME,
> };
> use pbs_tools::json::required_string_param;
> use proxmox_rest_server::{formatter, WorkerTask};
> @@ -2278,6 +2279,166 @@ pub async fn set_backup_owner(
> .await?
> }
>
> +pub fn do_mount_device(
> + datastore: DataStoreConfig,
> + worker: Option<&dyn WorkerTaskContext>,
> +) -> Result<(), Error> {
> + if let Some(uuid) = datastore.backing_device.as_ref() {
> + if pbs_datastore::is_datastore_available(&datastore) {
> + bail!("device '{uuid}' is already mounted");
> + }
> + let mount_point_path = std::path::Path::new(&datastore.path);
nit: bring `Path` into scope above
> + if let Some(worker) = worker {
> + task_log!(
> + worker,
> + "mounting '{uuid}' for store {} to '{}'",
> + datastore.name,
> + datastore.path
> + );
> + }
> + crate::tools::disks::mount_by_uuid(uuid, mount_point_path)?;
> +
> + Ok(())
> + } else {
> + Err(format_err!(
> + "Datastore '{}' cannot be mounted because it is not removable.",
> + datastore.name
> + ))
> + }
> +}
> +
> +#[api(
> + protected: true,
> + input: {
> + properties: {
> + store: {
> + schema: DATASTORE_SCHEMA,
> + },
> + }
> + },
> + returns: {
> + schema: UPID_SCHEMA,
> + },
> + access: {
> + permission: &Permission::Privilege(&["datastore", "{store}"], PRIV_DATASTORE_AUDIT, false),
> + },
> +)]
> +/// Mount removable datastore.
> +pub fn mount(store: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
> + let (section_config, _digest) = pbs_config::datastore::config()?;
> + let datastore: DataStoreConfig = section_config.lookup("datastore", &store)?;
> +
> + if datastore.backing_device.is_none() {
> + bail!("datastore '{store}' is not removable");
> + }
> +
> + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
> + let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
> +
> + let upid = WorkerTask::new_thread(
> + "mount-device",
> + Some(store),
> + auth_id.to_string(),
> + to_stdout,
> + move |worker| do_mount_device(datastore, Some(&worker)),
> + )?;
> +
> + Ok(json!(upid))
> +}
> +
> +fn do_unmount_device(
> + datastore: DataStoreConfig,
> + worker: Option<&dyn WorkerTaskContext>,
> +) -> Result<(), Error> {
> + let mut active_operations = task_tracking::get_active_operations(&datastore.name)?;
> + let mut old_status = String::new();
> + while active_operations.read + active_operations.write > 0 {
> + if let Some(worker) = worker {
> + if worker.abort_requested() {
> + bail!("aborted, due to user request");
> + }
> + let status = format!(
> + "cannot unmount yet, still {} read and {} write operations active",
> + active_operations.read, active_operations.write
> + );
> + if status != old_status {
> + task_log!(worker, "{}", status);
nit: inline `status`
> + old_status = status;
> + }
> + }
> + std::thread::sleep(std::time::Duration::from_millis(250));
> + active_operations = task_tracking::get_active_operations(&datastore.name)?;
> + }
> + crate::tools::disks::unmount_by_mountpoint(&datastore.path)?;
> +
> + let _lock = pbs_config::datastore::lock_config()?;
> + let (mut section_config, _digest) = pbs_config::datastore::config()?;
> + let mut store_config: DataStoreConfig = section_config.lookup("datastore", &datastore.name)?;
> + store_config.maintenance_mode = None;
> + section_config.set_data(&datastore.name, "datastore", &store_config)?;
> + pbs_config::datastore::save_config(§ion_config)?;
> +
> + Ok(())
> +}
> +
> +#[api(
> + protected: true,
> + input: {
> + properties: {
> + store: { schema: DATASTORE_SCHEMA },
> + },
> + },
> + returns: {
> + schema: UPID_SCHEMA,
> + },
> + access: {
> + permission: &Permission::Privilege(&["datastore", "{store}"], PRIV_DATASTORE_MODIFY, true),
> + }
> +)]
> +/// Unmount a removable device that is associated with the datastore
> +pub async fn unmount(store: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
> + let _lock = pbs_config::datastore::lock_config()?;
> + let (mut section_config, _digest) = pbs_config::datastore::config()?;
> + let mut datastore: DataStoreConfig = section_config.lookup("datastore", &store)?;
> +
> + if datastore.backing_device.is_none() {
> + bail!("datastore '{}' is not removable", &store);
nit: inline `store`
> + }
> +
> + if !is_datastore_available(&datastore) {
> + bail!("datastore '{}' is not mounted", &store);
nit: inline `store`
> + }
> +
> + datastore.set_maintenance_mode(Some(MaintenanceMode::new(MaintenanceType::Unmount, None)))?;
> + section_config.set_data(&store, "datastore", &datastore)?;
> + pbs_config::datastore::save_config(§ion_config)?;
> +
> + drop(_lock);
> +
> + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
> + let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
> +
> + if let Ok(proxy_pid) = proxmox_rest_server::read_pid(pbs_buildcfg::PROXMOX_BACKUP_PROXY_PID_FN)
> + {
> + let sock = proxmox_rest_server::ctrl_sock_from_pid(proxy_pid);
> + let _ = proxmox_rest_server::send_raw_command(
> + sock,
> + &format!("{{\"command\":\"update-datastore-cache\",\"args\":\"{store}\"}}\n"),
> + )
> + .await;
> + }
> +
> + let upid = WorkerTask::new_thread(
> + "unmount-device",
> + Some(store),
> + auth_id.to_string(),
> + to_stdout,
> + move |worker| do_unmount_device(datastore, Some(&worker)),
> + )?;
> +
> + Ok(json!(upid))
> +}
> +
> #[sortable]
> const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
> (
> @@ -2316,6 +2477,7 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
> .get(&API_METHOD_LIST_GROUPS)
> .delete(&API_METHOD_DELETE_GROUP),
> ),
> + ("mount", &Router::new().post(&API_METHOD_MOUNT)),
> (
> "namespace",
> // FIXME: move into datastore:: sub-module?!
> @@ -2350,6 +2512,7 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
> .delete(&API_METHOD_DELETE_SNAPSHOT),
> ),
> ("status", &Router::new().get(&API_METHOD_STATUS)),
> + ("unmount", &Router::new().post(&API_METHOD_UNMOUNT)),
> (
> "upload-backup-log",
> &Router::new().upload(&API_METHOD_UPLOAD_BACKUP_LOG),
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 06/20] api: removable datastore creation
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (4 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 05/20] api: admin: add (un)mount endpoint for removable datastores Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 07/20] api: disks list: add exclude-used flag Hannes Laimer
` (14 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
src/api2/config/datastore.rs | 53 ++++++++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)
diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
index 87425ff5..953a73c3 100644
--- a/src/api2/config/datastore.rs
+++ b/src/api2/config/datastore.rs
@@ -8,7 +8,7 @@ use serde_json::Value;
use proxmox_router::{http_bail, Permission, Router, RpcEnvironment, RpcEnvironmentType};
use proxmox_schema::{api, param_bail, ApiType};
use proxmox_section_config::SectionConfigData;
-use proxmox_sys::{task_warn, WorkerTaskContext};
+use proxmox_sys::{task_log, task_warn, WorkerTaskContext};
use proxmox_uuid::Uuid;
use pbs_api_types::{
@@ -20,7 +20,8 @@ use pbs_config::BackupLockGuard;
use pbs_datastore::chunk_store::ChunkStore;
use crate::api2::admin::{
- prune::list_prune_jobs, sync::list_sync_jobs, verify::list_verification_jobs,
+ datastore::do_mount_device, prune::list_prune_jobs, sync::list_sync_jobs,
+ verify::list_verification_jobs,
};
use crate::api2::config::prune::{delete_prune_job, do_create_prune_job};
use crate::api2::config::sync::delete_sync_job;
@@ -72,6 +73,32 @@ pub(crate) fn do_create_datastore(
datastore: DataStoreConfig,
worker: Option<&dyn WorkerTaskContext>,
) -> Result<(), Error> {
+ if datastore.backing_device.is_some() {
+ let mut mount_point = PathBuf::from(&datastore.path);
+
+ let default_options = proxmox_sys::fs::CreateOptions::new();
+ proxmox_sys::fs::create_path(
+ &mount_point,
+ Some(default_options.clone()),
+ Some(default_options),
+ )?;
+ do_mount_device(datastore.clone(), worker)?;
+
+ mount_point.push(".chunks");
+ if mount_point.is_dir() {
+ config.set_data(&datastore.name, "datastore", &datastore)?;
+ pbs_config::datastore::save_config(&config)?;
+ jobstate::create_state_file("garbage_collection", &datastore.name)?;
+ if let Some(worker) = worker {
+ task_log!(
+ worker,
+ "created removable datastore, chunkstore already exists"
+ );
+ }
+ return Ok(());
+ }
+ }
+
let path: PathBuf = datastore.path.clone().into();
let tuning: DatastoreTuning = serde_json::from_value(
@@ -122,6 +149,28 @@ pub fn create_datastore(
param_bail!("name", "datastore '{}' already exists.", config.name);
}
+ if let Some(uuid) = &config.backing_device {
+ let already_used_by = section_config
+ .sections
+ .iter()
+ .flat_map(|(datastore_name, (_, config))| {
+ config
+ .as_object()
+ .and_then(|cfg| cfg.get("backing-device"))
+ .and_then(|backing_device| backing_device.as_str())
+ .filter(|&device_uuid| device_uuid == uuid)
+ .map(|_| datastore_name)
+ })
+ .next();
+
+ if let Some(datastore_name) = already_used_by {
+ param_bail!(
+ "backing-device",
+ "device already in use by datastore '{datastore_name}'",
+ );
+ }
+ }
+
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 07/20] api: disks list: add exclude-used flag
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (5 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 06/20] api: removable datastore creation Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 08/20] pbs-api-types: add removable/is-available flag to DataStoreListItem Hannes Laimer
` (13 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
... used by the partition selector for removable datastore creation.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
src/api2/node/disks/mod.rs | 8 +++++
src/tools/disks/mod.rs | 62 ++++++++++++++++++++++++++++++++------
2 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/src/api2/node/disks/mod.rs b/src/api2/node/disks/mod.rs
index 711dae7b..f63bbd9b 100644
--- a/src/api2/node/disks/mod.rs
+++ b/src/api2/node/disks/mod.rs
@@ -41,6 +41,12 @@ pub mod zfs;
optional: true,
default: false,
},
+ "exclude-used": {
+ description: "Exclude partitions already used for removable datastores or mounted directories.",
+ type: bool,
+ optional: true,
+ default: false,
+ },
"usage-type": {
type: DiskUsageType,
optional: true,
@@ -62,6 +68,7 @@ pub mod zfs;
pub fn list_disks(
skipsmart: bool,
include_partitions: bool,
+ exclude_used: bool,
usage_type: Option<DiskUsageType>,
) -> Result<Vec<DiskUsageInfo>, Error> {
let mut list = Vec::new();
@@ -69,6 +76,7 @@ pub fn list_disks(
for (_, info) in DiskUsageQuery::new()
.smart(!skipsmart)
.partitions(include_partitions)
+ .exclude_used(exclude_used)
.query()?
{
if let Some(ref usage_type) = usage_type {
diff --git a/src/tools/disks/mod.rs b/src/tools/disks/mod.rs
index eaf016df..d27f5086 100644
--- a/src/tools/disks/mod.rs
+++ b/src/tools/disks/mod.rs
@@ -20,6 +20,7 @@ use proxmox_rest_server::WorkerTask;
use proxmox_schema::api;
use proxmox_sys::linux::procfs::{mountinfo::Device, MountInfo};
use proxmox_sys::task_log;
+use serde_json::Value;
use pbs_api_types::{BLOCKDEVICE_DISK_AND_PARTITION_NAME_REGEX, BLOCKDEVICE_NAME_REGEX};
@@ -32,6 +33,7 @@ pub use zpool_list::*;
mod lvm;
pub use lvm::*;
mod smart;
+use crate::api2::node::disks::directory::list_datastore_mounts;
pub use smart::*;
lazy_static::lazy_static! {
@@ -828,6 +830,7 @@ fn scan_partitions(
pub struct DiskUsageQuery {
smart: bool,
partitions: bool,
+ exclude_used: bool,
}
impl DiskUsageQuery {
@@ -835,6 +838,7 @@ impl DiskUsageQuery {
Self {
smart: true,
partitions: false,
+ exclude_used: false,
}
}
@@ -848,12 +852,22 @@ impl DiskUsageQuery {
self
}
+ pub fn exclude_used(&mut self, exclude_used: bool) -> &mut Self {
+ self.exclude_used = exclude_used;
+ self
+ }
+
pub fn query(&self) -> Result<HashMap<String, DiskUsageInfo>, Error> {
- get_disks(None, !self.smart, self.partitions)
+ get_disks(None, !self.smart, self.partitions, self.exclude_used)
}
pub fn find(&self, disk: &str) -> Result<DiskUsageInfo, Error> {
- let mut map = get_disks(Some(vec![disk.to_string()]), !self.smart, self.partitions)?;
+ let mut map = get_disks(
+ Some(vec![disk.to_string()]),
+ !self.smart,
+ self.partitions,
+ self.exclude_used,
+ )?;
if let Some(info) = map.remove(disk) {
Ok(info)
} else {
@@ -862,7 +876,7 @@ impl DiskUsageQuery {
}
pub fn find_all(&self, disks: Vec<String>) -> Result<HashMap<String, DiskUsageInfo>, Error> {
- get_disks(Some(disks), !self.smart, self.partitions)
+ get_disks(Some(disks), !self.smart, self.partitions, self.exclude_used)
}
}
@@ -935,6 +949,8 @@ fn get_disks(
no_smart: bool,
// include partitions
include_partitions: bool,
+ // skip partitions which are in use
+ exclude_used: bool,
) -> Result<HashMap<String, DiskUsageInfo>, Error> {
let disk_manager = DiskManage::new();
@@ -952,6 +968,30 @@ fn get_disks(
// fixme: ceph journals/volumes
+ let uuids_in_use = if exclude_used && include_partitions {
+ let (config, _digest) = pbs_config::datastore::config()?;
+
+ let uuids_from_datastores: Vec<String> = config
+ .sections
+ .iter()
+ .filter_map(|(_, (_, data))| {
+ data.as_object()
+ .and_then(|cfg| cfg.get("backing-device"))
+ .and_then(Value::as_str)
+ .map(String::from)
+ })
+ .collect();
+
+ let uuids_from_mounts: Vec<String> = list_datastore_mounts()?
+ .into_iter()
+ .filter_map(|mount| mount.device.split('/').last().map(String::from))
+ .collect();
+
+ [&uuids_from_datastores[..], &uuids_from_mounts[..]].concat()
+ } else {
+ Vec::new()
+ };
+
let mut result = HashMap::new();
for item in proxmox_sys::fs::scan_subdir(libc::AT_FDCWD, "/sys/block", &BLOCKDEVICE_NAME_REGEX)?
@@ -1024,12 +1064,16 @@ fn get_disks(
let partitions: Option<Vec<PartitionInfo>> = if include_partitions {
disk.partitions().map_or(None, |parts| {
- Some(get_partitions_info(
- parts,
- &lvm_devices,
- &zfs_devices,
- &file_system_devices,
- ))
+ let infos =
+ get_partitions_info(parts, &lvm_devices, &zfs_devices, &file_system_devices)
+ .into_iter()
+ .filter(|part| {
+ part.uuid
+ .as_ref()
+ .map_or(true, |u| !uuids_in_use.contains(u))
+ })
+ .collect();
+ Some(infos)
})
} else {
None
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 08/20] pbs-api-types: add removable/is-available flag to DataStoreListItem
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (6 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 07/20] api: disks list: add exclude-used flag Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 09/20] bin: manager: add (un)mount command Hannes Laimer
` (12 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pbs-api-types/src/datastore.rs | 9 ++++++++-
src/api2/admin/datastore.rs | 17 +++++++++--------
src/api2/status.rs | 18 +++++++++++++++---
3 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 8cb51708..b8326991 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -414,6 +414,10 @@ impl DataStoreConfig {
pub struct DataStoreListItem {
pub store: String,
pub comment: Option<String>,
+ /// Datastore is removable
+ pub removable: bool,
+ /// Datastore is available
+ pub available: bool,
/// If the datastore is in maintenance mode, information about it
#[serde(skip_serializing_if = "Option::is_none")]
pub maintenance: Option<String>,
@@ -1381,6 +1385,8 @@ pub struct DataStoreStatusListItem {
/// The available bytes of the underlying storage. (-1 on error)
#[serde(skip_serializing_if = "Option::is_none")]
pub avail: Option<u64>,
+ /// The datastore is available, relevant if removable
+ pub is_available: bool,
/// A list of usages of the past (last Month).
#[serde(skip_serializing_if = "Option::is_none")]
pub history: Option<Vec<Option<f64>>>,
@@ -1405,12 +1411,13 @@ pub struct DataStoreStatusListItem {
}
impl DataStoreStatusListItem {
- pub fn empty(store: &str, err: Option<String>) -> Self {
+ pub fn empty(store: &str, err: Option<String>, is_available: bool) -> Self {
DataStoreStatusListItem {
store: store.to_owned(),
total: None,
used: None,
avail: None,
+ is_available,
history: None,
history_start: None,
history_delta: None,
diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 07676a27..dc3cdb66 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -1262,8 +1262,8 @@ pub fn get_datastore_list(
let mut list = Vec::new();
- for (store, (_, data)) in &config.sections {
- let acl_path = &["datastore", store];
+ for (store, (_, data)) in config.sections {
+ let acl_path = &["datastore", &store];
let user_privs = user_info.lookup_privs(&auth_id, acl_path);
let allowed = (user_privs & (PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP)) != 0;
@@ -1274,15 +1274,16 @@ pub fn get_datastore_list(
}
}
+ let store_config: DataStoreConfig = serde_json::from_value(data)?;
+ let is_available = pbs_datastore::is_datastore_available(&store_config);
+
if allowed || allow_id {
list.push(DataStoreListItem {
store: store.clone(),
- comment: if !allowed {
- None
- } else {
- data["comment"].as_str().map(String::from)
- },
- maintenance: data["maintenance-mode"].as_str().map(String::from),
+ comment: if !allowed { None } else { store_config.comment },
+ removable: store_config.backing_device.is_some(),
+ available: is_available,
+ maintenance: store_config.maintenance_mode,
});
}
}
diff --git a/src/api2/status.rs b/src/api2/status.rs
index 78bc06b5..e1d33ccf 100644
--- a/src/api2/status.rs
+++ b/src/api2/status.rs
@@ -13,7 +13,7 @@ use pbs_api_types::{
};
use pbs_config::CachedUserInfo;
-use pbs_datastore::DataStore;
+use pbs_datastore::{is_datastore_available, DataStore};
use crate::rrd_cache::extract_rrd_data;
use crate::tools::statistics::linear_regression;
@@ -48,10 +48,17 @@ pub async fn datastore_status(
for (store, (_, _)) in &config.sections {
let user_privs = user_info.lookup_privs(&auth_id, &["datastore", store]);
let allowed = (user_privs & (PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP)) != 0;
+
+ let store_config = config.lookup("datastore", store)?;
+ if !is_datastore_available(&store_config) {
+ list.push(DataStoreStatusListItem::empty(store, None, false));
+ continue;
+ }
+
if !allowed {
if let Ok(datastore) = DataStore::lookup_datastore(store, Some(Operation::Lookup)) {
if can_access_any_namespace(datastore, &auth_id, &user_info) {
- list.push(DataStoreStatusListItem::empty(store, None));
+ list.push(DataStoreStatusListItem::empty(store, None, true));
}
}
continue;
@@ -60,7 +67,11 @@ pub async fn datastore_status(
let datastore = match DataStore::lookup_datastore(store, Some(Operation::Read)) {
Ok(datastore) => datastore,
Err(err) => {
- list.push(DataStoreStatusListItem::empty(store, Some(err.to_string())));
+ list.push(DataStoreStatusListItem::empty(
+ store,
+ Some(err.to_string()),
+ true,
+ ));
continue;
}
};
@@ -71,6 +82,7 @@ pub async fn datastore_status(
total: Some(status.total),
used: Some(status.used),
avail: Some(status.available),
+ is_available: true,
history: None,
history_start: None,
history_delta: None,
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 09/20] bin: manager: add (un)mount command
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (7 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 08/20] pbs-api-types: add removable/is-available flag to DataStoreListItem Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 10/20] add auto-mounting for removable datastores Hannes Laimer
` (11 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pbs-config/src/datastore.rs | 14 ++++
src/bin/proxmox_backup_manager/datastore.rs | 76 ++++++++++++++++++++-
2 files changed, 89 insertions(+), 1 deletion(-)
diff --git a/pbs-config/src/datastore.rs b/pbs-config/src/datastore.rs
index 5844a174..a540788f 100644
--- a/pbs-config/src/datastore.rs
+++ b/pbs-config/src/datastore.rs
@@ -63,6 +63,20 @@ pub fn complete_datastore_name(_arg: &str, _param: &HashMap<String, String>) ->
}
}
+pub fn complete_removable_datastore_name(
+ _arg: &str,
+ _param: &HashMap<String, String>,
+) -> Vec<String> {
+ match config() {
+ Ok((data, _digest)) => data
+ .sections
+ .into_iter()
+ .filter_map(|(name, (_, c))| c.get("backing-device").map(|_| name))
+ .collect(),
+ Err(_) => Vec::new(),
+ }
+}
+
pub fn complete_acl_path(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
let mut list = vec![
String::from("/"),
diff --git a/src/bin/proxmox_backup_manager/datastore.rs b/src/bin/proxmox_backup_manager/datastore.rs
index 383bcd24..f8462459 100644
--- a/src/bin/proxmox_backup_manager/datastore.rs
+++ b/src/bin/proxmox_backup_manager/datastore.rs
@@ -1,4 +1,4 @@
-use anyhow::Error;
+use anyhow::{format_err, Error};
use serde_json::Value;
use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
@@ -40,6 +40,34 @@ fn list_datastores(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Valu
Ok(Value::Null)
}
+#[api(
+ protected: true,
+ input: {
+ properties: {
+ store: {
+ schema: DATASTORE_SCHEMA,
+ },
+ digest: {
+ optional: true,
+ schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+ },
+ },
+ },
+)]
+/// Mount a removable a datastore configuration.
+async fn mount_datastore(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ param["node"] = "localhost".into();
+
+ let info = &api2::admin::datastore::API_METHOD_MOUNT;
+ let result = match info.handler {
+ ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+ _ => unreachable!(),
+ };
+
+ crate::wait_for_local_worker(result.as_str().unwrap()).await?;
+ Ok(())
+}
+
#[api(
input: {
properties: {
@@ -99,6 +127,34 @@ async fn create_datastore(mut param: Value) -> Result<Value, Error> {
Ok(Value::Null)
}
+#[api(
+ protected: true,
+ input: {
+ properties: {
+ store: {
+ schema: DATASTORE_SCHEMA,
+ },
+ digest: {
+ optional: true,
+ schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+ },
+ },
+ },
+)]
+/// Unmunt a removable a datastore configuration.
+async fn unmount_datastore(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ param["node"] = "localhost".into();
+
+ let info = &api2::admin::datastore::API_METHOD_UNMOUNT;
+ let result = match info.handler {
+ ApiHandler::Async(handler) => (handler)(param, info, rpcenv).await?,
+ _ => unreachable!(),
+ };
+
+ crate::wait_for_local_worker(result.as_str().unwrap()).await?;
+ Ok(())
+}
+
#[api(
protected: true,
input: {
@@ -142,6 +198,15 @@ async fn delete_datastore(mut param: Value, rpcenv: &mut dyn RpcEnvironment) ->
pub fn datastore_commands() -> CommandLineInterface {
let cmd_def = CliCommandMap::new()
.insert("list", CliCommand::new(&API_METHOD_LIST_DATASTORES))
+ .insert(
+ "mount",
+ CliCommand::new(&API_METHOD_MOUNT_DATASTORE)
+ .arg_param(&["store"])
+ .completion_cb(
+ "store",
+ pbs_config::datastore::complete_removable_datastore_name,
+ ),
+ )
.insert(
"show",
CliCommand::new(&API_METHOD_SHOW_DATASTORE)
@@ -152,6 +217,15 @@ pub fn datastore_commands() -> CommandLineInterface {
"create",
CliCommand::new(&API_METHOD_CREATE_DATASTORE).arg_param(&["name", "path"]),
)
+ .insert(
+ "unmount",
+ CliCommand::new(&API_METHOD_UNMOUNT_DATASTORE)
+ .arg_param(&["store"])
+ .completion_cb(
+ "store",
+ pbs_config::datastore::complete_removable_datastore_name,
+ ),
+ )
.insert(
"update",
CliCommand::new(&api2::config::datastore::API_METHOD_UPDATE_DATASTORE)
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 10/20] add auto-mounting for removable datastores
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (8 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 09/20] bin: manager: add (un)mount command Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 11/20] datastore: handle deletion of removable datastore properly Hannes Laimer
` (10 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
debian/proxmox-backup-server.install | 1 +
debian/proxmox-backup-server.udev | 3 ++
etc/Makefile | 3 +-
etc/removable-device-attach@.service.in | 8 +++
src/bin/proxmox_backup_manager/datastore.rs | 56 ++++++++++++++++++++-
5 files changed, 69 insertions(+), 2 deletions(-)
create mode 100644 etc/removable-device-attach@.service.in
diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index ee114ea3..cda01069 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -4,6 +4,7 @@ etc/proxmox-backup-daily-update.service /lib/systemd/system/
etc/proxmox-backup-daily-update.timer /lib/systemd/system/
etc/proxmox-backup-proxy.service /lib/systemd/system/
etc/proxmox-backup.service /lib/systemd/system/
+etc/removable-device-attach@.service /lib/systemd/system/
usr/bin/pmt
usr/bin/pmtx
usr/bin/proxmox-tape
diff --git a/debian/proxmox-backup-server.udev b/debian/proxmox-backup-server.udev
index afdfb2bc..e21b8bc7 100644
--- a/debian/proxmox-backup-server.udev
+++ b/debian/proxmox-backup-server.udev
@@ -16,3 +16,6 @@ SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="1", ENV{ID_SCSI_SER
SYMLINK+="tape/by-id/scsi-$env{ID_SCSI_SERIAL}-sg"
LABEL="persistent_storage_tape_end"
+
+# triggers the mounting of a removable device
+ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_UUID}!="", TAG+="systemd", ENV{SYSTEMD_WANTS}="removable-device-attach@$env{ID_FS_UUID}"
\ No newline at end of file
diff --git a/etc/Makefile b/etc/Makefile
index 42f639f6..730de4f8 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -7,7 +7,8 @@ DYNAMIC_UNITS := \
proxmox-backup-banner.service \
proxmox-backup-daily-update.service \
proxmox-backup.service \
- proxmox-backup-proxy.service
+ proxmox-backup-proxy.service \
+ removable-device-attach@.service
all: $(UNITS) $(DYNAMIC_UNITS) pbs-enterprise.list
diff --git a/etc/removable-device-attach@.service.in b/etc/removable-device-attach@.service.in
new file mode 100644
index 00000000..e10d1ea3
--- /dev/null
+++ b/etc/removable-device-attach@.service.in
@@ -0,0 +1,8 @@
+[Unit]
+Description=Try to mount the removable device of a datastore with uuid '%i'.
+After=proxmox-backup-proxy.service
+Requires=proxmox-backup-proxy.service
+
+[Service]
+Type=simple
+ExecStart=/usr/sbin/proxmox-backup-manager datastore uuid-mount %i
diff --git a/src/bin/proxmox_backup_manager/datastore.rs b/src/bin/proxmox_backup_manager/datastore.rs
index f8462459..7575d0c5 100644
--- a/src/bin/proxmox_backup_manager/datastore.rs
+++ b/src/bin/proxmox_backup_manager/datastore.rs
@@ -1,4 +1,4 @@
-use anyhow::{format_err, Error};
+use anyhow::{bail, format_err, Error};
use serde_json::Value;
use proxmox_router::{cli::*, ApiHandler, RpcEnvironment};
@@ -195,6 +195,56 @@ async fn delete_datastore(mut param: Value, rpcenv: &mut dyn RpcEnvironment) ->
Ok(())
}
+#[api(
+ protected: true,
+ input: {
+ properties: {
+ uuid: {
+ type: String,
+ format: &proxmox_schema::api_types::UUID_FORMAT,
+ description: "The UUID of the device that should be mounted",
+ },
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ },
+ },
+)]
+/// Try mounting a removable datastore given the UUID.
+async fn uuid_mount(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+ let uuid = param["uuid"]
+ .as_str()
+ .ok_or_else(|| format_err!("uuid has to be specified"))?;
+
+ let info = &api2::config::datastore::API_METHOD_LIST_DATASTORES;
+ let data: Value = match info.handler {
+ ApiHandler::Sync(handler) => (handler)(serde_json::json!({}), info, rpcenv)?,
+ _ => unreachable!(),
+ };
+
+ let store_name = data.as_array().and_then(|list| {
+ list.iter()
+ .filter_map(Value::as_object)
+ .find(|store| store.get("backing-device").map_or(false, |d| d.eq(&uuid)))
+ .and_then(|s| s.get("name").and_then(Value::as_str))
+ });
+
+ if let Some(store_name) = store_name {
+ let info = &api2::admin::datastore::API_METHOD_MOUNT;
+ let mount_param = serde_json::json!({
+ "store": store_name,
+ });
+ let result = match info.handler {
+ ApiHandler::Sync(handler) => (handler)(mount_param, info, rpcenv)?,
+ _ => unreachable!(),
+ };
+ crate::wait_for_local_worker(result.as_str().unwrap()).await?;
+ return Ok(Value::Null);
+ }
+ bail!("'{uuid}' is not associated with any datastore")
+}
+
pub fn datastore_commands() -> CommandLineInterface {
let cmd_def = CliCommandMap::new()
.insert("list", CliCommand::new(&API_METHOD_LIST_DATASTORES))
@@ -240,6 +290,10 @@ pub fn datastore_commands() -> CommandLineInterface {
pbs_config::datastore::complete_calendar_event,
),
)
+ .insert(
+ "uuid-mount",
+ CliCommand::new(&API_METHOD_UUID_MOUNT).arg_param(&["uuid"]),
+ )
.insert(
"remove",
CliCommand::new(&API_METHOD_DELETE_DATASTORE)
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 11/20] datastore: handle deletion of removable datastore properly
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (9 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 10/20] add auto-mounting for removable datastores Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 12/20] docs: add removable datastores section Hannes Laimer
` (9 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pbs-datastore/src/datastore.rs | 10 ++++++----
src/api2/config/datastore.rs | 14 ++++++++++++++
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs
index 274f1506..5ce20096 100644
--- a/pbs-datastore/src/datastore.rs
+++ b/pbs-datastore/src/datastore.rs
@@ -1511,10 +1511,12 @@ impl DataStore {
// weird, but ok
}
Err(err) if err.is_errno(nix::errno::Errno::EBUSY) => {
- task_warn!(
- worker,
- "Cannot delete datastore directory (is it a mount point?)."
- )
+ if datastore_config.backing_device.is_none() {
+ task_warn!(
+ worker,
+ "Cannot delete datastore directory (is it a mount point?)."
+ )
+ }
}
Err(err) if err.is_errno(nix::errno::Errno::ENOTEMPTY) => {
task_warn!(worker, "Datastore directory not empty, not deleting.")
diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
index 953a73c3..54156b7b 100644
--- a/src/api2/config/datastore.rs
+++ b/src/api2/config/datastore.rs
@@ -29,9 +29,11 @@ use crate::api2::config::tape_backup_job::{delete_tape_backup_job, list_tape_bac
use crate::api2::config::verify::delete_verification_job;
use pbs_config::CachedUserInfo;
+use pbs_datastore::is_datastore_available;
use proxmox_rest_server::WorkerTask;
use crate::server::jobstate;
+use crate::tools::disks::unmount_by_mountpoint;
#[api(
input: {
@@ -536,6 +538,14 @@ pub async fn delete_datastore(
http_bail!(NOT_FOUND, "datastore '{}' does not exist.", name);
}
+ let store_config: DataStoreConfig = config.lookup("datastore", &name)?;
+ if destroy_data && is_datastore_available(&store_config) {
+ http_bail!(
+ BAD_REQUEST,
+ "cannot destroy data on '{name}' unless the datastore is mounted"
+ );
+ }
+
if !keep_job_configs {
for job in list_verification_jobs(Some(name.clone()), Value::Null, rpcenv)? {
delete_verification_job(job.config.id, None, rpcenv)?
@@ -574,6 +584,10 @@ pub async fn delete_datastore(
// ignore errors
let _ = jobstate::remove_state_file("prune", &name);
let _ = jobstate::remove_state_file("garbage_collection", &name);
+ if destroy_data && store_config.backing_device.is_some() {
+ let _ = unmount_by_mountpoint(&store_config.path);
+ let _ = std::fs::remove_dir(&store_config.path);
+ }
if let Err(err) =
proxmox_async::runtime::block_on(crate::server::notify_datastore_removed())
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 12/20] docs: add removable datastores section
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (10 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 11/20] datastore: handle deletion of removable datastore properly Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 13/20] ui: add partition selector form Hannes Laimer
` (8 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
docs/storage.rst | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/docs/storage.rst b/docs/storage.rst
index 4444c423..ed02dbd4 100644
--- a/docs/storage.rst
+++ b/docs/storage.rst
@@ -165,6 +165,29 @@ following command creates a new datastore called ``store1`` on
# proxmox-backup-manager datastore create store1 /backup/disk1/store1
+Removable Datastores
+^^^^^^^^^^^^^^^^^^^^
+Removable datastores have a ``backing-device`` associadted with them, they can be
+mounted and unmounted. Other than that they behave the same way a normal Datastore
+would.
+
+They can be created on already correctly formatted partitions, as with normal Datastores
+the partitions should be either ``ext4``, ``xfs`` or ``zfs``.
+
+They are mounted automatically when plugged in. It is possible to create a
+removable Datastore on one PBS and use it on mutiple, the device just has to be added
+on all as a removable Datastore. This can be done using the normal `Create Datastore`
+form, by checking **Removable datastore** and selecting the device. If the devcice
+already contains a datastore it'll just be added as a new Datastore to the PBS
+instance and will be mounted whenever plugged in. Unmounting has to be done through
+the UI by clicking "Unmount" on the summary page or using the CLI
+
+.. code-block:: console
+
+ # proxmox-backup-manager datastore unmount store1
+
+both will wait for any running tasks and unmount the device once the last finishes.
+
Managing Datastores
^^^^^^^^^^^^^^^^^^^
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 13/20] ui: add partition selector form
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (11 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 12/20] docs: add removable datastores section Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 14/20] ui: add removable datastore creation support Hannes Laimer
` (7 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/Makefile | 1 +
www/form/PartitionSelector.js | 74 +++++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 www/form/PartitionSelector.js
diff --git a/www/Makefile b/www/Makefile
index 79cb4c04..762d90c5 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -49,6 +49,7 @@ JSSRC= \
form/NamespaceMaxDepth.js \
form/CalendarEvent.js \
form/PermissionPathSelector.js \
+ form/PartitionSelector.js \
form/GroupSelector.js \
form/GroupFilter.js \
form/VerifyOutdatedAfter.js \
diff --git a/www/form/PartitionSelector.js b/www/form/PartitionSelector.js
new file mode 100644
index 00000000..604975a6
--- /dev/null
+++ b/www/form/PartitionSelector.js
@@ -0,0 +1,74 @@
+Ext.define('pbs-partition-list', {
+ extend: 'Ext.data.Model',
+ fields: ['name', 'uuid', 'filesystem', 'devpath', 'size'],
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/nodes/localhost/disks/list?skipsmart=1&include-partitions=1&exclude-used=1",
+ reader: {
+ transform: (rawData) => rawData.data
+ .flatMap(disk => (disk.partitions ?? [])
+ .filter(partition => partition.used === 'filesystem')),
+ },
+ },
+ idProperty: 'devpath',
+
+});
+
+Ext.define('PBS.form.PartitionSelector', {
+ extend: 'Proxmox.form.ComboGrid',
+ alias: 'widget.pbsPartitionSelector',
+
+ allowBlank: false,
+ autoSelect: false,
+ submitEmpty: false,
+ valueField: 'uuid',
+ displayField: 'devpath',
+
+ store: {
+ model: 'pbs-partition-list',
+ autoLoad: true,
+ sorters: 'devpath',
+ },
+ getSubmitData: function() {
+ let me = this;
+ let data = null;
+ if (!me.disabled && me.submitValue && !me.isFileUpload()) {
+ let val = me.getSubmitValue();
+ if (val !== undefined && val !== null && val !== '') {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.getDeleteEmpty()) {
+ data = {};
+ data.delete = me.getName();
+ }
+ }
+ return data;
+ },
+ listConfig: {
+ columns: [
+ {
+ header: gettext('Path'),
+ sortable: true,
+ dataIndex: 'devpath',
+ renderer: (v, metaData, rec) => Ext.String.htmlEncode(v),
+ flex: 1,
+ },
+ {
+ header: gettext('Filesystem'),
+ sortable: true,
+ dataIndex: 'filesystem',
+ flex: 1,
+ },
+ {
+ header: gettext('Size'),
+ sortable: true,
+ dataIndex: 'size',
+ renderer: Proxmox.Utils.format_size,
+ flex: 1,
+ },
+ ],
+ viewConfig: {
+ emptyText: 'No partitions present',
+ },
+ },
+});
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 14/20] ui: add removable datastore creation support
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (12 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 13/20] ui: add partition selector form Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 15/20] ui: add (un)mount button to summary Hannes Laimer
` (6 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/window/DataStoreEdit.js | 51 +++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/www/window/DataStoreEdit.js b/www/window/DataStoreEdit.js
index aecf6b8d..9114cf4f 100644
--- a/www/window/DataStoreEdit.js
+++ b/www/window/DataStoreEdit.js
@@ -59,6 +59,31 @@ Ext.define('PBS.DataStoreEdit', {
fieldLabel: gettext('Backing Path'),
emptyText: gettext('An absolute path'),
},
+ {
+ xtype: 'pmxDisplayEditField',
+ fieldLabel: gettext('Device'),
+ name: 'backing-device',
+ hidden: true,
+ cbind: {
+ editable: '{isCreate}',
+ }, editConfig: {
+ xtype: 'pbsPartitionSelector',
+ allowBlank: true,
+ },
+ emptyText: gettext('Device path'),
+ listeners: {
+ change(field, newValue, oldValue) {
+ const form = field.up('form');
+ const pathField = form.down('[name=path]');
+
+ if (newValue) {
+ pathField.setValue(`/mnt/removable_datastore/${newValue}`);
+ } else if (oldValue) {
+ pathField.setValue('');
+ }
+ },
+ },
+ },
],
column2: [
{
@@ -84,6 +109,32 @@ Ext.define('PBS.DataStoreEdit', {
},
],
columnB: [
+ {
+ xtype: 'checkbox',
+ boxLabel: gettext('Removable datastore'),
+ submitValue: false,
+ listeners: {
+ change: function(checkbox, isRemovable) {
+ let inputPanel = checkbox.up('inputpanel');
+ let pathField = inputPanel.down('[name=path]');
+ let uuidField = inputPanel.down('[name=backing-device]');
+
+ pathField.setValue('');
+ uuidField.setValue('');
+
+ pathField.allowBlank = isRemovable;
+ uuidField.allowBlank = !isRemovable;
+
+ if (isRemovable) {
+ pathField.hide();
+ uuidField.show();
+ } else {
+ pathField.show();
+ uuidField.hide();
+ }
+ },
+ },
+ },
{
xtype: 'textfield',
name: 'comment',
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 15/20] ui: add (un)mount button to summary
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (13 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 14/20] ui: add removable datastore creation support Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 16/20] ui: tree: render unmounted datastores correctly Hannes Laimer
` (5 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
And only try to load datastore information if the datastore is
available.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/datastore/Summary.js | 92 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 90 insertions(+), 2 deletions(-)
diff --git a/www/datastore/Summary.js b/www/datastore/Summary.js
index a932b4e0..49aa3b3c 100644
--- a/www/datastore/Summary.js
+++ b/www/datastore/Summary.js
@@ -309,7 +309,82 @@ Ext.define('PBS.DataStoreSummary', {
model: 'pve-rrd-datastore',
});
- me.callParent();
+ me.statusStore = Ext.create('Proxmox.data.ObjectStore', {
+ url: `/api2/json/admin/datastore/${me.datastore}/status`,
+ interval: 1000,
+ });
+
+ let unmountBtn = Ext.create('Ext.Button', {
+ text: gettext('Unmount'),
+ hidden: true,
+ handler: () => {
+ Proxmox.Utils.API2Request({
+ url: `/admin/datastore/${me.datastore}/unmount`,
+ method: 'POST',
+ failure: function(response) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ Ext.create('Proxmox.window.TaskViewer', {
+ upid: response.result.data,
+ }).show();
+ },
+ });
+ },
+ });
+
+ let mountBtn = Ext.create('Ext.Button', {
+ text: gettext('Mount'),
+ hidden: true,
+ handler: () => {
+ Proxmox.Utils.API2Request({
+ url: `/admin/datastore/${me.datastore}/mount`,
+ method: 'POST',
+ failure: function(response) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ Ext.create('Proxmox.window.TaskViewer', {
+ upid: response.result.data,
+ }).show();
+ },
+ });
+ },
+ });
+
+ Ext.apply(me, {
+ tbar: [unmountBtn, mountBtn, '->', { xtype: 'proxmoxRRDTypeSelector' }],
+ });
+
+ me.mon(me.statusStore, 'load', (s, records, success) => {
+ if (!success) {
+ me.down('pbsDataStoreInfo').fireEvent('deactivate');
+ Proxmox.Utils.API2Request({
+ url: `/config/datastore/${me.datastore}`,
+ success: response => {
+ let mode = response.result.data['maintenance-mode'];
+ let [type, _message] = PBS.Utils.parseMaintenanceMode(mode);
+ if (!response.result.data['backing-device']) {
+ return;
+ }
+ if (!type || type === 'read-only') {
+ unmountBtn.setDisabled(true);
+ mountBtn.setDisabled(false);
+ } else if (type === 'unmount') {
+ unmountBtn.setDisabled(true);
+ mountBtn.setDisabled(true);
+ } else {
+ unmountBtn.setDisabled(false);
+ mountBtn.setDisabled(false);
+ }
+ },
+ });
+ } else {
+ me.down('pbsDataStoreInfo').fireEvent('activate');
+ unmountBtn.setDisabled(false);
+ mountBtn.setDisabled(true);
+ }
+ });
let sp = Ext.state.Manager.getProvider();
me.mon(sp, 'statechange', function(provider, key, value) {
@@ -322,11 +397,17 @@ Ext.define('PBS.DataStoreSummary', {
Proxmox.Utils.updateColumns(me);
});
+ me.callParent();
+
Proxmox.Utils.API2Request({
url: `/config/datastore/${me.datastore}`,
waitMsgTarget: me.down('pbsDataStoreInfo'),
success: function(response) {
- let path = Ext.htmlEncode(response.result.data.path);
+ let data = response.result.data;
+ let path = Ext.htmlEncode(data.path);
+ const removable = Object.prototype.hasOwnProperty.call(data, "backing-device");
+ unmountBtn.setHidden(!removable);
+ mountBtn.setHidden(!removable);
me.down('pbsDataStoreInfo').setTitle(`${me.datastore} (${path})`);
me.down('pbsDataStoreNotes').setNotes(response.result.data.comment);
},
@@ -344,6 +425,13 @@ Ext.define('PBS.DataStoreSummary', {
let hasIoTicks = records?.some((rec) => rec?.data?.io_ticks !== undefined);
me.down('#ioDelayChart').setVisible(!success || hasIoTicks);
}, undefined, { single: true });
+ me.on('afterrender', () => {
+ me.statusStore.startUpdate();
+ });
+
+ me.on('destroy', () => {
+ me.statusStore.stopUpdate();
+ });
me.query('proxmoxRRDChart').forEach((chart) => {
chart.setStore(me.rrdstore);
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 16/20] ui: tree: render unmounted datastores correctly
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (14 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 15/20] ui: add (un)mount button to summary Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 17/20] ui: utils: make parseMaintenanceMode more robust Hannes Laimer
` (4 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/NavigationTree.js | 15 ++++++++++++---
www/css/ext6-pbs.css | 8 ++++++++
www/datastore/DataStoreListSummary.js | 1 +
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/www/NavigationTree.js b/www/NavigationTree.js
index 3e0040f7..dc4a3dc7 100644
--- a/www/NavigationTree.js
+++ b/www/NavigationTree.js
@@ -261,13 +261,22 @@ Ext.define('PBS.view.main.NavigationTree', {
j++;
}
- let [qtip, iconCls] = ['', 'fa fa-database'];
+ let mainIcon = `fa fa-${records[i].data.removable ? 'plug' : 'database'}`;
+ let [qtip, iconCls] = ['', mainIcon];
const maintenance = records[i].data.maintenance;
+ const removable_not_available = records[i].data.removable && !records[i].data.available;
+ if (removable_not_available) {
+ iconCls = `${mainIcon} pmx-tree-icon-custom unplugged`;
+ qtip = gettext('Removable datastore not mounted');
+ }
if (maintenance) {
const [type, message] = PBS.Utils.parseMaintenanceMode(maintenance);
qtip = `${type}${message ? ': ' + message : ''}`;
- let mainenanceTypeCls = type === 'delete' ? 'destroying' : 'maintenance';
- iconCls = `fa fa-database pmx-tree-icon-custom ${mainenanceTypeCls}`;
+ let maintenanceTypeCls = 'maintenance';
+ if (type === 'delete') {
+ maintenanceTypeCls = 'destroying';
+ }
+ iconCls = `${mainIcon} pmx-tree-icon-custom ${maintenanceTypeCls}`;
}
if (getChildTextAt(j).localeCompare(name) !== 0) {
diff --git a/www/css/ext6-pbs.css b/www/css/ext6-pbs.css
index c33ce684..706e681e 100644
--- a/www/css/ext6-pbs.css
+++ b/www/css/ext6-pbs.css
@@ -271,6 +271,10 @@ span.snapshot-comment-column {
content: "\ ";
}
+.x-treelist-item-icon.fa-plug, .pmx-tree-icon-custom.fa-plug {
+ font-size: 12px;
+}
+
/* datastore maintenance */
.pmx-tree-icon-custom.maintenance:after {
content: "\f0ad";
@@ -290,6 +294,10 @@ span.snapshot-comment-column {
color: #888;
}
+.pmx-tree-icon-custom.unplugged:before {
+ color: #888;
+}
+
/*' PBS specific icons */
.pbs-icon-tape {
diff --git a/www/datastore/DataStoreListSummary.js b/www/datastore/DataStoreListSummary.js
index b908034d..f7ea83e7 100644
--- a/www/datastore/DataStoreListSummary.js
+++ b/www/datastore/DataStoreListSummary.js
@@ -22,6 +22,7 @@ Ext.define('PBS.datastore.DataStoreListSummary', {
stillbad: 0,
deduplication: 1.0,
error: "",
+ removable: false,
maintenance: '',
},
},
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 17/20] ui: utils: make parseMaintenanceMode more robust
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (15 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 16/20] ui: tree: render unmounted datastores correctly Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 18/20] ui: add datastore status mask for unmounted removable datastores Hannes Laimer
` (3 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/Utils.js | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/www/Utils.js b/www/Utils.js
index 5357949b..ad2b2777 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -663,14 +663,29 @@ Ext.define('PBS.Utils', {
return `${icon} ${value}`;
},
- // FIXME: this "parser" is brittle and relies on the order the arguments will appear in
+ /**
+ * Parses maintenance mode property string.
+ * Examples:
+ * "offline,message=foo" -> ["offline", "foo"]
+ * "offline" -> ["offline", null]
+ * "message=foo,offline" -> ["offline", "foo"]
+ * null/undefined -> [null, null]
+ *
+ * @param {string|null} mode - Maintenance mode string to parse.
+ * @return {Array<string|null>} - Parsed maintenance mode values.
+ */
parseMaintenanceMode: function(mode) {
- let [type, message] = mode.split(/,(.+)/);
- type = type.split("=").pop();
- message = message ? message.split("=")[1]
- .replace(/^"(.*)"$/, '$1')
- .replaceAll('\\"', '"') : null;
- return [type, message];
+ if (!mode) {
+ return [null, null];
+ }
+ return mode.split(',').reduce(([m, msg], pair) => {
+ const [key, value] = pair.split('=');
+ if (key === 'message') {
+ return [m, value.replace(/^"(.*)"$/, '$1').replace(/\\"/g, '"')];
+ } else {
+ return [value ?? key, msg];
+ }
+ }, [null, null]);
},
renderMaintenance: function(mode, activeTasks) {
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 18/20] ui: add datastore status mask for unmounted removable datastores
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (16 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 17/20] ui: utils: make parseMaintenanceMode more robust Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 19/20] ui: maintenance: fix disable msg field if no type is selected Hannes Laimer
` (2 subsequent siblings)
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/css/ext6-pbs.css | 12 ++++++++++++
www/datastore/Summary.js | 21 +++++++++++++--------
2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/www/css/ext6-pbs.css b/www/css/ext6-pbs.css
index 706e681e..891189ae 100644
--- a/www/css/ext6-pbs.css
+++ b/www/css/ext6-pbs.css
@@ -261,6 +261,18 @@ span.snapshot-comment-column {
content: "\f0ad";
}
+.pbs-unplugged-mask div.x-mask-msg-text {
+ background: None;
+ padding: 12px 0 0;
+}
+
+.pbs-unplugged-mask:before {
+ font-size: 3em;
+ display: flex;
+ justify-content: center;
+ content: "\f1e6";
+}
+
/* the small icons TODO move to proxmox-widget-toolkit */
.pmx-tree-icon-custom:after {
position: relative;
diff --git a/www/datastore/Summary.js b/www/datastore/Summary.js
index 49aa3b3c..33b4e18a 100644
--- a/www/datastore/Summary.js
+++ b/www/datastore/Summary.js
@@ -61,16 +61,21 @@ Ext.define('PBS.DataStoreInfo', {
Proxmox.Utils.API2Request({
url: `/config/datastore/${me.view.datastore}`,
success: function(response) {
- const config = response.result.data;
- if (config['maintenance-mode']) {
- const [_type, msg] = PBS.Utils.parseMaintenanceMode(config['maintenance-mode']);
- me.view.el.mask(
- `${gettext('Datastore is in maintenance mode')}${msg ? ': ' + msg : ''}`,
- 'fa pbs-maintenance-mask',
- );
- } else {
+ let maintenanceString = response.result.data['maintenance-mode'];
+ let removable = !!response.result.data['backing-device'];
+ if (!maintenanceString && !removable) {
me.view.el.mask(gettext('Datastore is not available'));
+ return;
}
+
+ let [_type, msg] = PBS.Utils.parseMaintenanceMode(maintenanceString);
+ let isUnplugged = !maintenanceString && removable;
+ let maskMessage = isUnplugged
+ ? gettext('Datastore is not mounted')
+ : `${gettext('Datastore is in maintenance mode')}${msg ? ': ' + msg : ''}`;
+
+ let maskIcon = isUnplugged ? 'fa pbs-unplugged-mask' : 'fa pbs-maintenance-mask';
+ me.view.el.mask(maskMessage, maskIcon);
},
});
return;
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 19/20] ui: maintenance: fix disable msg field if no type is selected
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (17 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 18/20] ui: add datastore status mask for unmounted removable datastores Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 20/20] ui: render 'unmount' maintenance mode correctly Hannes Laimer
2024-04-19 10:10 ` [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Dietmar Maurer
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/window/MaintenanceOptions.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/www/window/MaintenanceOptions.js b/www/window/MaintenanceOptions.js
index 1ee92542..527c3698 100644
--- a/www/window/MaintenanceOptions.js
+++ b/www/window/MaintenanceOptions.js
@@ -56,12 +56,17 @@ Ext.define('PBS.window.MaintenanceOptions', {
fieldLabel: gettext('Maintenance Type'),
value: '__default__',
deleteEmpty: true,
+ listeners: {
+ change: (field, newValue) => {
+ Ext.getCmp('message-field').setDisabled(newValue === '__default__');
+ },
+ },
},
{
xtype: 'proxmoxtextfield',
+ id: 'message-field',
name: 'maintenance-msg',
fieldLabel: gettext('Description'),
- // FIXME: disable if maintenance type is none
},
],
},
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v7 20/20] ui: render 'unmount' maintenance mode correctly
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (18 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 19/20] ui: maintenance: fix disable msg field if no type is selected Hannes Laimer
@ 2024-04-19 8:58 ` Hannes Laimer
2024-04-19 10:10 ` [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Dietmar Maurer
20 siblings, 0 replies; 27+ messages in thread
From: Hannes Laimer @ 2024-04-19 8:58 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/Utils.js | 4 +++-
www/window/MaintenanceOptions.js | 10 ++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/www/Utils.js b/www/Utils.js
index ad2b2777..5af2baca 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -698,7 +698,7 @@ Ext.define('PBS.Utils', {
let extra = '';
if (activeTasks !== undefined) {
- const conflictingTasks = activeTasks.write + (type === 'offline' ? activeTasks.read : 0);
+ const conflictingTasks = activeTasks.write + (type === 'offline' || type === 'unmount' ? activeTasks.read : 0);
if (conflictingTasks > 0) {
extra += '| <i class="fa fa-spinner fa-pulse fa-fw"></i> ';
@@ -718,6 +718,8 @@ Ext.define('PBS.Utils', {
break;
case 'offline': modeText = gettext("Offline");
break;
+ case 'unmount': modeText = gettext("Unmounting");
+ break;
}
return `${modeText} ${extra}`;
},
diff --git a/www/window/MaintenanceOptions.js b/www/window/MaintenanceOptions.js
index 527c3698..d7348cb4 100644
--- a/www/window/MaintenanceOptions.js
+++ b/www/window/MaintenanceOptions.js
@@ -52,6 +52,7 @@ Ext.define('PBS.window.MaintenanceOptions', {
items: [
{
xtype: 'pbsMaintenanceType',
+ id: 'type-field',
name: 'maintenance-type',
fieldLabel: gettext('Maintenance Type'),
value: '__default__',
@@ -85,6 +86,15 @@ Ext.define('PBS.window.MaintenanceOptions', {
};
}
+ let unmounting = options['maintenance-type'] === 'unmount';
+ let defaultType = options['maintenance-type'] === '__default__';
+ if (unmounting) {
+ options['maintenance-type'] = '';
+ }
+
me.callParent([options]);
+
+ Ext.ComponentManager.get('type-field').setDisabled(unmounting);
+ Ext.ComponentManager.get('message-field').setDisabled(unmounting || defaultType);
},
});
--
2.39.2
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores
2024-04-19 8:58 [pbs-devel] [PATCH proxmox-backup v7 00/20] add removable datastores Hannes Laimer
` (19 preceding siblings ...)
2024-04-19 8:58 ` [pbs-devel] [PATCH proxmox-backup v7 20/20] ui: render 'unmount' maintenance mode correctly Hannes Laimer
@ 2024-04-19 10:10 ` Dietmar Maurer
20 siblings, 0 replies; 27+ messages in thread
From: Dietmar Maurer @ 2024-04-19 10:10 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Hannes Laimer
Reviewed-by: dietmar@proxmox.com
I guess we can commit this. The UI still needs some improvements, but we can do that on top of this series.
> On 19.4.2024 10:58 CEST Hannes Laimer <h.laimer@proxmox.com> wrote:
>
>
> These patches add support for removable datastores. All removable
> datastores have a backing-device(a UUID) associated with them. Removable
> datastores work like normal ones, just that they can be unplugged. It is
> possible to create a removable datastore, sync backups onto it, unplug
> it and use it on a different PBS.
>
> The datastore path is also the mountpoint for the removable datastore.
> By default when creating one through the web UI it will be
> `/mnt/removable-datastores/<UUID>`, using the CLI it is possible to
> specify something else. Since a removable datastore is associated with
> the UUID of a partition, it is technically possible to have two
> removable datastores on the same device, but I don't think there is a
> use-case that couldn't also be done using namespaces.
>
> When a removable datastore is deleted and 'destroy-data' is set, the
> device has to be plugged in. If 'destroy-data' is not set the datastore
> can be deleted even if the device is not present. Removable datastores
> are automatically mounted when plugged in. At the API service start all
> removable datastores are marked as 'unplugged', unless they are already
> mounted.
>
>
> v7:
> * drop first two patches, now depends on [1]
> * improve logging when waiting for tasks
> * drop `update-datatore-cache` refactoring
> * fix some commit messages
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 27+ messages in thread