From: Dominik Csapak <d.csapak@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [pdm-devel] [RFC PATCH datacenter-manager 1/2] server: add new streaming 'content' api call for pbs
Date: Wed, 8 Oct 2025 15:54:04 +0200 [thread overview]
Message-ID: <20251008135558.3586369-2-d.csapak@proxmox.com> (raw)
In-Reply-To: <20251008135558.3586369-1-d.csapak@proxmox.com>
that makes use of 'application/json-seq' mime type to stream data from
pbs to pdm, removing the need to buffer the data on pdm in the api call.
The pbs-client code is more or less a copy of the current snapshot call
list, but if needed this method can be refactored.
This requires the new 'content' api call for pbs [0]
0: https://lore.proxmox.com/pbs-devel/20251008134344.3512958-1-d.csapak@proxmox.com/
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
server/src/api/pbs/mod.rs | 40 +++++++++++++++++++++++++++++-
server/src/pbs_client.rs | 51 +++++++++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/server/src/api/pbs/mod.rs b/server/src/api/pbs/mod.rs
index dc31f620..6e53ce6f 100644
--- a/server/src/api/pbs/mod.rs
+++ b/server/src/api/pbs/mod.rs
@@ -1,7 +1,7 @@
use anyhow::{format_err, Error};
use futures::StreamExt;
-use proxmox_router::{list_subdirs_api_method, Permission, Router, SubdirMap};
+use proxmox_router::{list_subdirs_api_method, Permission, Record, Router, SubdirMap};
use proxmox_schema::api;
use proxmox_schema::property_string::PropertyString;
use proxmox_sortable_macro::sortable;
@@ -63,6 +63,7 @@ const DATASTORE_ITEM_SUBDIRS: SubdirMap = &sorted!([
"snapshots",
&Router::new().get(&API_METHOD_LIST_SNAPSHOTS_2)
),
+ ("content", &Router::new().get(&API_METHOD_LIST_CONTENT)),
]);
#[api(
@@ -175,6 +176,43 @@ async fn list_snapshots_2(
.into())
}
+#[api(
+ stream: true,
+ input: {
+ properties: {
+ remote: { schema: REMOTE_ID_SCHEMA },
+ datastore: { schema: pbs_api_types::DATASTORE_SCHEMA },
+ ns: {
+ schema: pbs_api_types::BACKUP_NAMESPACE_SCHEMA,
+ optional: true,
+ },
+ "max-depth": {
+ schema: pbs_api_types::NS_MAX_DEPTH_SCHEMA,
+ optional: true,
+ }
+ },
+ },
+ returns: pbs_api_types::ADMIN_DATASTORE_LIST_CONTENT_RETURN_TYPE,
+ access: {
+ permission: &Permission::Privilege(&["resource", "{remote}", "datastore", "{datastore}"], PRIV_RESOURCE_AUDIT, false),
+ },
+)]
+/// List the PBS remote's datastores.
+async fn list_content(
+ remote: String,
+ datastore: String,
+ ns: Option<String>,
+ max_depth: Option<usize>,
+) -> Result<proxmox_router::Stream, Error> {
+ let (remotes, _) = pdm_config::remotes::config()?;
+ let remote = get_remote(&remotes, &remote)?;
+ let snapshots = connection::make_pbs_client(remote)?
+ .list_content(&datastore, ns.as_deref(), max_depth)
+ .await?
+ .map(Record::from_result);
+ Ok(snapshots.into())
+}
+
#[api(
input: {
properties: {
diff --git a/server/src/pbs_client.rs b/server/src/pbs_client.rs
index d8278c8a..76041090 100644
--- a/server/src/pbs_client.rs
+++ b/server/src/pbs_client.rs
@@ -190,6 +190,57 @@ impl PbsClient {
}
}
+ /// List a datastore's content.
+ pub async fn list_content(
+ &self,
+ datastore: &str,
+ namespace: Option<&str>,
+ max_depth: Option<usize>,
+ ) -> Result<JsonRecords<pbs_api_types::DatastoreContent>, anyhow::Error> {
+ let path = ApiPathBuilder::new(format!("/api2/json/admin/datastore/{datastore}/content"))
+ .maybe_arg("ns", &namespace)
+ .maybe_arg("max-depth", &max_depth)
+ .build();
+ let response = self
+ .0
+ .streaming_request(http::Method::GET, &path, None::<()>)
+ .await?;
+
+ let body = response
+ .body
+ .ok_or_else(|| Error::Other("missing response body"))?;
+
+ if response.status == 200 {
+ if response
+ .content_type
+ .is_some_and(|c| c.starts_with("application/json-seq"))
+ {
+ Ok(JsonRecords::from_body(body))
+ } else {
+ let response: JsonData<_> = serde_json::from_slice(
+ &body
+ .collect()
+ .await
+ .map_err(|err| {
+ Error::Anyhow(Box::new(err).context("failed to retrieve response body"))
+ })?
+ .to_bytes(),
+ )?;
+ Ok(JsonRecords::from_vec(response.data))
+ }
+ } else {
+ let data = body
+ .collect()
+ .await
+ .map_err(|err| {
+ Error::Anyhow(Box::new(err).context("failed to retrieve response body"))
+ })?
+ .to_bytes();
+ let error = String::from_utf8_lossy(&data).into_owned();
+ Err(anyhow::Error::msg(error))
+ }
+ }
+
/// create an API-Token on the PBS remote and give the token admin ACL on everything.
pub async fn create_admin_token(
&self,
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
next prev parent reply other threads:[~2025-10-08 13:55 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-08 13:54 [pdm-devel] [RFC PATCH datacenter-manager 0/2] use streaming content api Dominik Csapak
2025-10-08 13:54 ` Dominik Csapak [this message]
2025-10-08 13:54 ` [pdm-devel] [RFC PATCH datacenter-manager 2/2] ui: pbs: snapshot list: change to streaming 'content' api call Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251008135558.3586369-2-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pdm-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.