From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 373B472EA7 for ; Wed, 26 May 2021 15:48:15 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id EDF3D12F22 for ; Wed, 26 May 2021 15:48:14 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 68FE412E88 for ; Wed, 26 May 2021 15:48:12 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 3F0AE419D4 for ; Wed, 26 May 2021 15:48:12 +0200 (CEST) From: Dominik Csapak To: pbs-devel@lists.proxmox.com Date: Wed, 26 May 2021 15:48:04 +0200 Message-Id: <20210526134811.333-3-d.csapak@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210526134811.333-1-d.csapak@proxmox.com> References: <20210526134811.333-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.035 Adjusted score from AWL reputation of From: address 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 2/9] api2/tape: add api call to list media sets 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: , X-List-Received-Date: Wed, 26 May 2021 13:48:15 -0000 we want a 'media-set' selector in the gui, this makes it very easy to do and is not as costly as reusing the media list, since we do not need to iterate over all media (e.g. unassigned) Signed-off-by: Dominik Csapak --- src/api2/tape/media.rs | 75 ++++++++++++++++++++++++++++++++++++ src/api2/types/tape/media.rs | 20 ++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/api2/tape/media.rs b/src/api2/tape/media.rs index 683c74b8..8351b2be 100644 --- a/src/api2/tape/media.rs +++ b/src/api2/tape/media.rs @@ -1,4 +1,5 @@ use std::path::Path; +use std::collections::HashSet; use anyhow::{bail, format_err, Error}; use serde::{Serialize, Deserialize}; @@ -28,6 +29,7 @@ use crate::{ CHANGER_NAME_SCHEMA, MediaPoolConfig, MediaListEntry, + MediaSetListEntry, MediaStatus, MediaContentEntry, VAULT_NAME_SCHEMA, @@ -44,6 +46,74 @@ use crate::{ }, }; +#[api( + returns: { + description: "List of media sets.", + type: Array, + items: { + type: MediaSetListEntry, + }, + }, + access: { + description: "List of media sets filtered by Tape.Audit privileges on pool", + permission: &Permission::Anybody, + }, +)] +/// List Media sets +pub async fn list_media_sets( + rpcenv: &mut dyn RpcEnvironment, +) -> Result, Error> { + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; + + let (config, _digest) = config::media_pool::config()?; + + let status_path = Path::new(TAPE_STATUS_DIR); + + let mut media_sets: HashSet = HashSet::new(); + let mut list = Vec::new(); + + for (_section_type, data) in config.sections.values() { + let pool_name = match data["name"].as_str() { + None => continue, + Some(name) => name, + }; + + let privs = user_info.lookup_privs(&auth_id, &["tape", "pool", pool_name]); + if (privs & PRIV_TAPE_AUDIT) == 0 { + continue; + } + + let config: MediaPoolConfig = config.lookup("pool", pool_name)?; + + let changer_name = None; // assume standalone drive + let pool = MediaPool::with_config(status_path, &config, changer_name, true)?; + + for media in pool.list_media() { + if let Some(label) = media.media_set_label() { + if media_sets.contains(&label.uuid) { + continue; + } + + let media_set_uuid = label.uuid.clone(); + let media_set_ctime = label.ctime; + let media_set_name = pool + .generate_media_set_name(&media_set_uuid, config.template.clone()) + .unwrap_or_else(|_| media_set_uuid.to_string()); + + media_sets.insert(media_set_uuid.clone()); + list.push(MediaSetListEntry { + media_set_name, + media_set_uuid, + media_set_ctime, + pool: pool_name.to_string(), + }); + } + } + } + + Ok(list) +} #[api( input: { properties: { @@ -546,6 +616,11 @@ const SUBDIRS: SubdirMap = &[ .get(&API_METHOD_DESTROY_MEDIA) ), ( "list", &MEDIA_LIST_ROUTER ), + ( + "media-sets", + &Router::new() + .get(&API_METHOD_LIST_MEDIA_SETS) + ), ( "move", &Router::new() diff --git a/src/api2/types/tape/media.rs b/src/api2/types/tape/media.rs index 554efa7a..b53ce28b 100644 --- a/src/api2/types/tape/media.rs +++ b/src/api2/types/tape/media.rs @@ -12,6 +12,26 @@ use crate::api2::types::{ MediaLocation, }; +#[api( + properties: { + "media-set-uuid": { + schema: MEDIA_SET_UUID_SCHEMA, + }, + }, +)] +#[derive(Serialize,Deserialize)] +#[serde(rename_all = "kebab-case")] +/// Media Set list entry +pub struct MediaSetListEntry { + /// Media set name + pub media_set_name: String, + pub media_set_uuid: Uuid, + /// MediaSet creation time stamp + pub media_set_ctime: i64, + /// Media Pool + pub pool: String, +} + #[api( properties: { location: { -- 2.20.1