From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 1E2BC1FF1C2 for ; Fri, 22 Nov 2024 15:47:50 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 10B2C15A08; Fri, 22 Nov 2024 15:47:54 +0100 (CET) From: Hannes Laimer To: pbs-devel@lists.proxmox.com Date: Fri, 22 Nov 2024 15:47:12 +0100 Message-Id: <20241122144713.299130-26-h.laimer@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241122144713.299130-1-h.laimer@proxmox.com> References: <20241122144713.299130-1-h.laimer@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.025 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 v14 25/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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" ... to get information about (removable) datastores a device contains Signed-off-by: Hannes Laimer --- 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 28a472b0f..17df09be2 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(¶m); + 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