From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <pbs-devel-bounces@lists.proxmox.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9])
	by lore.proxmox.com (Postfix) with ESMTPS id D9A421FF16E
	for <inbox@lore.proxmox.com>; Wed, 13 Nov 2024 16:01:28 +0100 (CET)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id 50F9918919;
	Wed, 13 Nov 2024 16:01:20 +0100 (CET)
From: Hannes Laimer <h.laimer@proxmox.com>
To: pbs-devel@lists.proxmox.com
Date: Wed, 13 Nov 2024 16:01:02 +0100
Message-Id: <20241113150102.164820-27-h.laimer@proxmox.com>
X-Mailer: git-send-email 2.39.5
In-Reply-To: <20241113150102.164820-1-h.laimer@proxmox.com>
References: <20241113150102.164820-1-h.laimer@proxmox.com>
MIME-Version: 1.0
X-SPAM-LEVEL: Spam detection results:  0
 AWL 0.022 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DMARC_MISSING             0.1 Missing DMARC policy
 KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment
 SPF_HELO_NONE           0.001 SPF: HELO does not publish an SPF Record
 SPF_PASS               -0.001 SPF: sender matches SPF record
Subject: [pbs-devel] [PATCH proxmox-backup v13 26/26] bin: debug: add
 inspect device command
X-BeenThere: pbs-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox Backup Server development discussion
 <pbs-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/>
List-Post: <mailto:pbs-devel@lists.proxmox.com>
List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe>
Reply-To: Proxmox Backup Server development discussion
 <pbs-devel@lists.proxmox.com>
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: pbs-devel-bounces@lists.proxmox.com
Sender: "pbs-devel" <pbs-devel-bounces@lists.proxmox.com>

... to get information about (removable) datastores a device contains

Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
new since v12.

Something like this would also make sense to somehow integrate into the
UI, making it easier to select and add existsing datastores on the
device. But since the device has to be mounted, I was not sure where it
makes sense, except maybe after selecting a device, so the `path` field
could be some sort of selection. But we'd need a new endpoint and this
can definitely also be added later, so I did not include anything in this
series.

 src/bin/proxmox_backup_debug/inspect.rs | 149 ++++++++++++++++++++++++
 1 file changed, 149 insertions(+)

diff --git a/src/bin/proxmox_backup_debug/inspect.rs b/src/bin/proxmox_backup_debug/inspect.rs
index 28a472b0..17df09be 100644
--- a/src/bin/proxmox_backup_debug/inspect.rs
+++ b/src/bin/proxmox_backup_debug/inspect.rs
@@ -331,6 +331,151 @@ fn inspect_file(
     Ok(())
 }
 
+/// Return the count of VM, CT and host backup groups and the count of namespaces
+/// as this tuple (vm, ct, host, ns)
+fn get_basic_ds_info(path: String) -> Result<(i64, i64, i64, i64), Error> {
+    let mut vms = 0;
+    let mut cts = 0;
+    let mut hosts = 0;
+    let mut ns = 0;
+    let mut walker = WalkDir::new(path).into_iter();
+
+    while let Some(entry_result) = walker.next() {
+        let entry = entry_result?;
+        if !entry.file_type().is_dir() {
+            continue;
+        }
+
+        let Some(name) = entry.path().file_name().and_then(|a| a.to_str()) else {
+            continue;
+        };
+
+        if name == ".chunks" {
+            walker.skip_current_dir();
+            continue;
+        }
+
+        let dir_count = std::fs::read_dir(entry.path())?
+            .filter_map(Result::ok)
+            .filter(|entry| entry.path().is_dir())
+            .count() as i64;
+
+        match name {
+            "ns" => ns += dir_count,
+            "vm" => {
+                vms += dir_count;
+                walker.skip_current_dir();
+            }
+            "ct" => {
+                cts += dir_count;
+                walker.skip_current_dir();
+            }
+            "host" => {
+                hosts += dir_count;
+                walker.skip_current_dir();
+            }
+            _ => {
+                // root or ns dir
+            }
+        }
+    }
+
+    Ok((vms, cts, hosts, ns))
+}
+
+#[api(
+    input: {
+        properties: {
+            device: {
+                description: "Device path, usually /dev/...",
+                type: String,
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// Inspect a device for possible datastores on it
+fn inspect_device(device: String, param: Value) -> Result<(), Error> {
+    let output_format = get_output_format(&param);
+    let tmp_mount_path = format!(
+        "{}/{:x}",
+        pbs_buildcfg::rundir!("/mount"),
+        proxmox_uuid::Uuid::generate()
+    );
+
+    let default_options = proxmox_sys::fs::CreateOptions::new();
+    proxmox_sys::fs::create_path(
+        &tmp_mount_path,
+        Some(default_options.clone()),
+        Some(default_options.clone()),
+    )?;
+    let mut mount_cmd = std::process::Command::new("mount");
+    mount_cmd.arg(device.clone());
+    mount_cmd.arg(tmp_mount_path.clone());
+    proxmox_sys::command::run_command(mount_cmd, None)?;
+
+    let mut walker = WalkDir::new(tmp_mount_path.clone()).into_iter();
+
+    let mut stores = Vec::new();
+
+    let mut ds_count = 0;
+    while let Some(entry_result) = walker.next() {
+        let entry = entry_result?;
+
+        if entry.file_type().is_dir()
+            && entry
+                .file_name()
+                .to_str()
+                .map_or(false, |name| name == ".chunks")
+        {
+            let store_path = entry
+                .path()
+                .to_str()
+                .and_then(|n| n.strip_suffix("/.chunks"));
+
+            if let Some(store_path) = store_path {
+                ds_count += 1;
+                let (vm, ct, host, ns) = get_basic_ds_info(store_path.to_string())?;
+                stores.push(json!({
+                    "path": store_path.strip_prefix(&tmp_mount_path).unwrap_or("???"),
+                    "vm-count": vm,
+                    "ct-count": ct,
+                    "host-count": host,
+                    "ns-count": ns,
+                }));
+            };
+
+            walker.skip_current_dir();
+        }
+    }
+
+    let mut umount_cmd = std::process::Command::new("umount");
+    umount_cmd.arg(tmp_mount_path.clone());
+    proxmox_sys::command::run_command(umount_cmd, None)?;
+    std::fs::remove_dir(std::path::Path::new(&tmp_mount_path))?;
+
+    if output_format == "text" {
+        println!("Device containes {} stores", ds_count);
+        println!("---------------");
+        for s in stores {
+            println!(
+                "Datastore at {} | VM: {}, CT: {}, HOST: {}, NS: {}",
+                s["path"], s["vm-count"], s["ct-count"], s["host-count"], s["ns-count"]
+            );
+        }
+    } else {
+        format_and_print_result(
+            &json!({"store_count": stores.len(), "stores": stores}),
+            &output_format,
+        );
+    }
+
+    Ok(())
+}
+
 pub fn inspect_commands() -> CommandLineInterface {
     let cmd_def = CliCommandMap::new()
         .insert(
@@ -340,6 +485,10 @@ pub fn inspect_commands() -> CommandLineInterface {
         .insert(
             "file",
             CliCommand::new(&API_METHOD_INSPECT_FILE).arg_param(&["file"]),
+        )
+        .insert(
+            "device",
+            CliCommand::new(&API_METHOD_INSPECT_DEVICE).arg_param(&["device"]),
         );
 
     cmd_def.into()
-- 
2.39.5



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel