From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup v2 2/4] api: datastore admin: add 'snapshot-information' api call
Date: Tue, 5 Dec 2023 11:53:43 +0100 [thread overview]
Message-ID: <20231205105345.1656325-3-d.csapak@proxmox.com> (raw)
In-Reply-To: <20231205105345.1656325-1-d.csapak@proxmox.com>
get information about a snapshot. This is intended to contain all
relevant information about a snapshot, so that we don't fill the
'list_snapshots' api call with further info
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
pbs-api-types/src/datastore.rs | 24 +++++++
src/api2/admin/datastore.rs | 117 +++++++++++++++++++++++++++++++--
2 files changed, 135 insertions(+), 6 deletions(-)
diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 52f83f48..76d1811f 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -421,6 +421,30 @@ pub struct SnapshotVerifyState {
pub state: VerifyState,
}
+#[api(
+ properties: {
+ info : {
+ type: SnapshotListItem,
+ },
+ "upload-statistics": {
+ type: UploadStatistic,
+ optional: true,
+ },
+ },
+)]
+#[derive(Serialize, Deserialize, Clone, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+/// Information about a Snapshot
+pub struct SnapshotInformation {
+ #[serde(flatten)]
+ pub info: SnapshotListItem,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub upload_statistics: Option<UploadStatistic>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ /// "notes" for the whole backup group
+ pub group_comment: Option<String>,
+}
+
#[api()]
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index a95031e7..f6fcb8ff 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -34,12 +34,12 @@ use pxar::EntryKind;
use pbs_api_types::{
print_ns_and_snapshot, print_store_and_ns, Authid, BackupContent, BackupNamespace, BackupType,
Counts, CryptMode, DataStoreListItem, DataStoreStatus, GarbageCollectionStatus, GroupListItem,
- KeepOptions, Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotListItem,
- SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA,
- BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA,
- MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
- PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY,
- UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
+ KeepOptions, Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotInformation,
+ SnapshotListItem, SnapshotVerifyState, UploadStatistic, BACKUP_ARCHIVE_NAME_SCHEMA,
+ BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA,
+ DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA,
+ PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE,
+ PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
};
use pbs_client::pxar::{create_tar, create_zip};
use pbs_config::CachedUserInfo;
@@ -2239,6 +2239,107 @@ pub async fn set_backup_owner(
.await?
}
+#[api(
+ input: {
+ properties: {
+ store: { schema: DATASTORE_SCHEMA },
+ ns: {
+ type: BackupNamespace,
+ optional: true,
+ },
+ backup_dir: {
+ type: pbs_api_types::BackupDir,
+ flatten: true,
+ },
+ },
+ },
+ access: {
+ permission: &Permission::Anybody,
+ description: "Requires on /datastore/{store}[/{namespace}] either DATASTORE_AUDIT for any \
+ or DATASTORE_BACKUP and being the owner of the group",
+ },
+)]
+/// Get Information about a snapshot
+pub fn get_snapshot_info(
+ store: String,
+ ns: Option<BackupNamespace>,
+ backup_dir: pbs_api_types::BackupDir,
+ rpcenv: &mut dyn RpcEnvironment,
+) -> Result<SnapshotInformation, Error> {
+ let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+ let ns = ns.unwrap_or_default();
+
+ let datastore = check_privs_and_load_store(
+ &store,
+ &ns,
+ &auth_id,
+ PRIV_DATASTORE_AUDIT,
+ PRIV_DATASTORE_BACKUP,
+ Some(Operation::Read),
+ &backup_dir.group,
+ )?;
+
+ let group = datastore.backup_group(ns.clone(), backup_dir.group.clone());
+ let owner = group.get_owner()?;
+ let backup = datastore.backup_dir(ns.clone(), backup_dir.clone())?;
+
+ let protected = backup.is_protected();
+ let backup_info = BackupInfo::new(backup)?;
+
+ let (manifest, files) = get_all_snapshot_files(&backup_info)?;
+ let comment: Option<String> = manifest.unprotected["notes"].as_str().map(String::from);
+
+ let fingerprint = match manifest.fingerprint() {
+ Ok(fp) => fp,
+ Err(err) => {
+ eprintln!("error parsing fingerprint: '{}'", err);
+ None
+ }
+ };
+
+ let verification = manifest.unprotected["verify_state"].clone();
+ let verification: Option<SnapshotVerifyState> = match serde_json::from_value(verification) {
+ Ok(verify) => verify,
+ Err(err) => {
+ eprintln!("error parsing verification state : '{}'", err);
+ None
+ }
+ };
+
+ let size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum());
+
+ let group = backup_dir.group.clone();
+
+ let info = SnapshotListItem {
+ backup: backup_dir,
+ comment,
+ verification,
+ fingerprint,
+ files,
+ size,
+ owner: Some(owner),
+ protected,
+ };
+
+ let upload_statistics =
+ match Option::<UploadStatistic>::deserialize(&manifest.unprotected["chunk_upload_stats"]) {
+ Ok(stats) => stats,
+ Err(err) => {
+ log::warn!("error parsing chunk_upload_stats: {err}");
+ None
+ }
+ };
+
+ let note_path = get_group_note_path(&datastore, &ns, &group);
+ let group_comment = file_read_optional_string(note_path)?;
+
+ Ok(SnapshotInformation {
+ info,
+ upload_statistics,
+ group_comment,
+ })
+}
+
#[sortable]
const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
(
@@ -2304,6 +2405,10 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
&Router::new().download(&API_METHOD_PXAR_FILE_DOWNLOAD),
),
("rrd", &Router::new().get(&API_METHOD_GET_RRD_STATS)),
+ (
+ "snapshot-information",
+ &Router::new().get(&API_METHOD_GET_SNAPSHOT_INFO),
+ ),
(
"snapshots",
&Router::new()
--
2.30.2
next prev parent reply other threads:[~2023-12-05 10:53 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-05 10:53 [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots Dominik Csapak
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 1/4] api-types: add an UploadStatistic api type Dominik Csapak
2023-12-05 10:53 ` Dominik Csapak [this message]
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 3/4] ui: datastore content: add snapshot information to context menu Dominik Csapak
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 4/4] ui: datastore content: add 'more actions' menu to actions 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=20231205105345.1656325-3-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pbs-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.