From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id E63CF1FF0DF for ; Fri, 28 Aug 2026 15:31:13 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 444DA21604; Fri, 28 Aug 2026 15:30:54 +0200 (CEST) From: Shannon Sterz To: pve-devel@lists.proxmox.com Subject: [PATCH cluster 04/21] pmxcfs: add ability to query backup progress Date: Fri, 28 Aug 2026 15:30:13 +0200 Message-ID: <20260828133030.351140-5-s.sterz@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260828133030.351140-1-s.sterz@proxmox.com> References: <20260828133030.351140-1-s.sterz@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1787923824276 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.918 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: MXORUJ7XI66OIQI6Q7LUE3K6YLHGUZFI X-Message-ID-Hash: MXORUJ7XI66OIQI6Q7LUE3K6YLHGUZFI X-MailFrom: s.sterz@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: cfs_live_backup_database() will now also return the progress of the backup instead of just the filename. Signed-off-by: Shannon Sterz --- src/PVE/Cluster.pm | 43 +++++++++++++++++++++++++++++++++++----- src/pmxcfs/cfs-ipc-ops.h | 2 ++ src/pmxcfs/database.c | 18 +++++++++++++++++ src/pmxcfs/memdb.c | 12 +++++++++++ src/pmxcfs/memdb.h | 28 ++++++++++++++++++++++++++ src/pmxcfs/server.c | 11 ++++++++++ src/pmxcfs/status.c | 7 +++++++ src/pmxcfs/status.h | 2 ++ 8 files changed, 118 insertions(+), 5 deletions(-) diff --git a/src/PVE/Cluster.pm b/src/PVE/Cluster.pm index 31c06e8..b907f2e 100644 --- a/src/PVE/Cluster.pm +++ b/src/PVE/Cluster.pm @@ -243,8 +243,14 @@ my $ipcc_init_backup = sub { my ($filename) = @_; my $bindata = pack "Z*", $filename; - PVE::IPCC::ipcc_send_rec(CFS_IPC_INIT_BACKUP, $bindata); - warn $! if $! != 0; + my $result = &$ipcc_send_rec_json(CFS_IPC_INIT_BACKUP, $bindata); + die $! if $! != 0; + + return $result; +}; + +my $ipcc_backup_progress = sub { + return &$ipcc_send_rec_json(CFS_IPC_BACKUP_PROGRESS); }; my $ccache = {}; @@ -943,7 +949,11 @@ sub cfs_rename_db_unsafe { Creates a new live backup of the database backing the cluster file system. -Returns a file path to the created backup. +Returns a hash with two members: C is the filename of the new backups and +C which is also a hash. C has three members: C, +a boolean indicating whether the backup is finished or still on-going, +C, an integer showing the pages that are not yet backed up, and +C showing the total page count. =cut @@ -964,12 +974,35 @@ sub cfs_live_backup_database { my $ctime = time(); my $backup_file = "config-backup-$ctime.db"; print "starting backup of current database to \"$dbbackupdir/$backup_file\"\n"; + my $result = {}; - eval { &$ipcc_init_backup($backup_file); }; + eval { $result = &$ipcc_init_backup($backup_file); }; + die $@ if $@; + return { + 'file' => $backup_file, + 'progress' => $result, + }; +} + +=head3 cfs_live_backup_progress() + +Returns a hash that describes the current live backup status of the database +backing the cluster file system. + +It contains three members: C, a boolean indicating whether the +backup is finished or still on-going, C, an integer showing the pages +that are not yet backed up, and C showing the total page count. + +=cut + +sub cfs_live_backup_progress { + my $result = {}; + + eval { $result = &$ipcc_backup_progress(); }; warn $@ if $@; - return $backup_file; + return $result; } 1; diff --git a/src/pmxcfs/cfs-ipc-ops.h b/src/pmxcfs/cfs-ipc-ops.h index 054ebb3..e8f7541 100644 --- a/src/pmxcfs/cfs-ipc-ops.h +++ b/src/pmxcfs/cfs-ipc-ops.h @@ -47,4 +47,6 @@ #define CFS_IPC_INIT_BACKUP 14 +#define CFS_IPC_BACKUP_PROGRESS 15 + #endif diff --git a/src/pmxcfs/database.c b/src/pmxcfs/database.c index 07b3664..39bdf61 100644 --- a/src/pmxcfs/database.c +++ b/src/pmxcfs/database.c @@ -866,3 +866,21 @@ int bdb_handle_backup(db_backend_t *bdb) { return to_return_code; } + +memdb_backup_progress_t bdb_backup_progress(db_backend_t *bdb) { + memdb_backup_progress_t to_return = { + .in_progress = FALSE, + .remaining = 0, + .total = 0, + }; + + if (bdb == NULL || bdb->current_backup == NULL) { + return to_return; + } + + to_return.in_progress = TRUE; + to_return.remaining = sqlite3_backup_remaining(bdb->current_backup); + to_return.total = sqlite3_backup_pagecount(bdb->current_backup); + + return to_return; +} diff --git a/src/pmxcfs/memdb.c b/src/pmxcfs/memdb.c index b7372f9..255c03f 100644 --- a/src/pmxcfs/memdb.c +++ b/src/pmxcfs/memdb.c @@ -1550,3 +1550,15 @@ int memdb_handle_backup(memdb_t *memdb) { return to_return; } + +memdb_backup_progress_t memdb_backup_progress(memdb_t *memdb) { + memdb_backup_progress_t to_return = { + .in_progress = FALSE, + .remaining = 0, + .total = 0, + }; + + g_return_val_if_fail(memdb != NULL, to_return); + + return bdb_backup_progress(memdb->bdb); +} diff --git a/src/pmxcfs/memdb.h b/src/pmxcfs/memdb.h index 2c27959..3943328 100644 --- a/src/pmxcfs/memdb.h +++ b/src/pmxcfs/memdb.h @@ -88,6 +88,12 @@ typedef struct { db_backend_t *bdb; } memdb_t; +typedef struct { + gboolean in_progress; // whether a backup is currently in progress + int remaining; // how many pages are still remaining to back up + int total; // how many pages there are to back up in total after the last backup step +} memdb_backup_progress_t; + memdb_t *memdb_open(const char *dbfilename); void memdb_close(memdb_t *memdb); @@ -200,6 +206,17 @@ int memdb_finish_or_abort_backup(memdb_t *memdb, gboolean abort); */ int memdb_handle_backup(memdb_t *memdb); +/** + * memdb_backup_progress: + * @memdb: a memdb object that has an initialized db to back up. + * + * Queries whether a backup is in progress and if so how far it has progressed. + * + * Return: the returned `memdb_backup_progress_t` struct provides information on whether a back up + * is in progress and if so how far it has progressed. + */ +memdb_backup_progress_t memdb_backup_progress(memdb_t *memdb); + db_backend_t *bdb_backend_open(const char *filename, memdb_tree_entry_t *root, GHashTable *index); void bdb_backend_close(db_backend_t *bdb); @@ -267,4 +284,15 @@ int bdb_finish_or_abort_backup(db_backend_t *bdb, gboolean abort); */ int bdb_handle_backup(db_backend_t *bdb); +/** + * bdb_backup_progress: + * @bdb: a db backend that has an initialized db to back up. + * + * Queries whether a backup is in progress and if so how far it has progressed. + * + * Return: the returned `memdb_backup_progress_t` struct provides information on whether a back up + * is in progress and if so how far it has progressed. + */ +memdb_backup_progress_t bdb_backup_progress(db_backend_t *bdb); + #endif /* _PVE_MEMDB_H_ */ diff --git a/src/pmxcfs/server.c b/src/pmxcfs/server.c index 8149fd3..5f9ab0a 100644 --- a/src/pmxcfs/server.c +++ b/src/pmxcfs/server.c @@ -511,6 +511,9 @@ static int32_t s1_msg_process_fn(qb_ipcs_connection_t *c, void *data, size_t siz if (result != 0) { cfs_critical("couldn't add first backup step (error %d), abort...", result); (void)memdb_finish_or_abort_backup(memdb, TRUE); + } else { + memdb_backup_progress_t progress = memdb_backup_progress(memdb); + cfs_create_backup_progress_msg(outbuf, &progress); } } @@ -522,6 +525,14 @@ static int32_t s1_msg_process_fn(qb_ipcs_connection_t *c, void *data, size_t siz result = -EINVAL; } } + } else if (request_id == CFS_IPC_BACKUP_PROGRESS) { + if (request_size != sizeof(struct qb_ipc_request_header)) { + result = -EINVAL; + } else { + memdb_backup_progress_t progress = memdb_backup_progress(memdb); + cfs_create_backup_progress_msg(outbuf, &progress); + result = 0; + } } cfs_debug("process result %d", result); diff --git a/src/pmxcfs/status.c b/src/pmxcfs/status.c index 9581e2a..41591d5 100644 --- a/src/pmxcfs/status.c +++ b/src/pmxcfs/status.c @@ -1974,3 +1974,10 @@ void cfs_set_quorate(uint32_t quorate, gboolean quiet) { g_mutex_unlock(&mutex); } + +void cfs_create_backup_progress_msg(GString *str, memdb_backup_progress_t *progress) { + g_string_append_printf( + str, "{\n\t\"in-progress\": %s,\n\t\"remaining\": %d,\n\t\"total\": %d\n}", + progress->in_progress ? "true" : "false", progress->remaining, progress->total + ); +} diff --git a/src/pmxcfs/status.h b/src/pmxcfs/status.h index 6a6b0a7..0790c88 100644 --- a/src/pmxcfs/status.h +++ b/src/pmxcfs/status.h @@ -103,4 +103,6 @@ int cfs_create_guest_conf_properties_msg( GString *str, memdb_t *memdb, const char **props, uint8_t num_props, uint32_t vmid ); +void cfs_create_backup_progress_msg(GString *str, memdb_backup_progress_t *progress); + #endif /* _PVE_STATUS_H_ */ -- 2.47.3