[pbs-devel] [PATCH proxmox-backup 3/5] api: filter snapshot counts

Fabian Grünbichler f.gruenbichler at proxmox.com
Thu Nov 12 11:30:32 CET 2020


unprivileged users should only see the counts related to their part of
the datastore.

while we're at it, switch to a list groups, filter groups, count
snapshots approach (like list_snapshots) to speedup calls to this
endpoint when many unprivileged users share a datastore.

Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
 src/api2/admin/datastore.rs | 93 +++++++++++++++++++------------------
 src/api2/types/mod.rs       |  2 +-
 2 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 857fb903..6d9de4e4 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -472,51 +472,40 @@ pub fn list_snapshots (
         })
 }
 
-fn get_snapshots_count(store: &DataStore) -> Result<Counts, Error> {
+fn get_snapshots_count(store: &DataStore, filter_owner: Option<&Authid>) -> Result<Counts, Error> {
     let base_path = store.base_path();
-    let backup_list = BackupInfo::list_backups(&base_path)?;
-    let mut groups = HashSet::new();
-
-    let mut result = Counts {
-        ct: None,
-        host: None,
-        vm: None,
-        other: None,
-    };
-
-    for info in backup_list {
-        let group = info.backup_dir.group();
-
-        let id = group.backup_id();
-        let backup_type = group.backup_type();
-
-        let mut new_id = false;
-
-        if groups.insert(format!("{}-{}", &backup_type, &id)) {
-            new_id = true;
-        }
+    let groups = BackupInfo::list_backup_groups(&base_path)?;
 
-        let mut counts = match backup_type {
-            "ct" => result.ct.take().unwrap_or(Default::default()),
-            "host" => result.host.take().unwrap_or(Default::default()),
-            "vm" => result.vm.take().unwrap_or(Default::default()),
-            _ => result.other.take().unwrap_or(Default::default()),
-        };
+    groups.iter()
+        .filter(|group| {
+            let owner = match store.get_owner(&group) {
+                Ok(owner) => owner,
+                Err(err) => {
+                    eprintln!("Failed to get owner of group '{}' - {}", group, err);
+                    return false;
+                },
+            };
 
-        counts.snapshots += 1;
-        if new_id {
-            counts.groups +=1;
-        }
+            match filter_owner {
+                Some(filter) => check_backup_owner(&owner, filter).is_ok(),
+                None => true,
+            }
+        })
+        .try_fold(Counts::default(), |mut counts, group| {
+            let snapshot_count = group.list_backups(&base_path)?.len() as u64;
+
+            let type_count = match group.backup_type() {
+                "ct" => counts.ct.get_or_insert(Default::default()),
+                "vm" => counts.vm.get_or_insert(Default::default()),
+                "host" => counts.host.get_or_insert(Default::default()),
+                _ => counts.other.get_or_insert(Default::default()),
+            };
 
-        match backup_type {
-            "ct" => result.ct = Some(counts),
-            "host" => result.host = Some(counts),
-            "vm" => result.vm = Some(counts),
-            _ => result.other = Some(counts),
-        }
-    }
+            type_count.groups += 1;
+            type_count.snapshots += snapshot_count;
 
-    Ok(result)
+            Ok(counts)
+        })
 }
 
 #[api(
@@ -546,15 +535,27 @@ pub fn status(
     store: String,
     verbose: bool,
     _info: &ApiMethod,
-    _rpcenv: &mut dyn RpcEnvironment,
+    rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<DataStoreStatus, Error> {
     let datastore = DataStore::lookup_datastore(&store)?;
     let storage = crate::tools::disks::disk_usage(&datastore.base_path())?;
-    let (counts, gc_status) = match verbose {
-        true => {
-            (Some(get_snapshots_count(&datastore)?), Some(datastore.last_gc_status()))
-        },
-        false => (None, None),
+    let (counts, gc_status) = if verbose {
+        let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+        let user_info = CachedUserInfo::new()?;
+
+        let store_privs = user_info.lookup_privs(&auth_id, &["datastore", &store]);
+        let filter_owner = if store_privs & PRIV_DATASTORE_AUDIT != 0 {
+            None
+        } else {
+            Some(&auth_id)
+        };
+
+        let counts = Some(get_snapshots_count(&datastore, filter_owner)?);
+        let gc_status = Some(datastore.last_gc_status());
+
+        (counts, gc_status)
+    } else {
+        (None, None)
     };
 
     Ok(DataStoreStatus {
diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs
index 44cfef94..d7d5a8af 100644
--- a/src/api2/types/mod.rs
+++ b/src/api2/types/mod.rs
@@ -692,7 +692,7 @@ pub struct TypeCounts {
         },
     },
 )]
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, Default)]
 /// Counts of groups/snapshots per BackupType.
 pub struct Counts {
     /// The counts for CT backups
-- 
2.20.1






More information about the pbs-devel mailing list