]>
Commit | Line | Data |
---|---|---|
c96a4a38 DM |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Stefan Reiter <s.reiter@proxmox.com> | |
3 | Date: Mon, 29 Jun 2020 11:06:03 +0200 | |
4 | Subject: [PATCH] PVE-Backup: Add dirty-bitmap tracking for incremental backups | |
5 | ||
6 | Uses QEMU's existing MIRROR_SYNC_MODE_BITMAP and a dirty-bitmap on top | |
7 | of all backed-up drives. This will only execute the data-write callback | |
8 | for any changed chunks, the PBS rust code will reuse chunks from the | |
9 | previous index for everything it doesn't receive if reuse_index is true. | |
10 | ||
11 | On error or cancellation, remove all dirty bitmaps to ensure | |
12 | consistency. | |
13 | ||
20be7fa0 TL |
14 | Add PBS/incremental specific information to query backup info QMP and |
15 | HMP commands. | |
16 | ||
c96a4a38 DM |
17 | Only supported for PBS backups. |
18 | ||
19 | Signed-off-by: Stefan Reiter <s.reiter@proxmox.com> | |
20 | Signed-off-by: Dietmar Maurer <dietmar@proxmox.com> | |
20be7fa0 | 21 | Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> |
c96a4a38 | 22 | --- |
0c893fd8 SR |
23 | block/monitor/block-hmp-cmds.c | 1 + |
24 | monitor/hmp-cmds.c | 45 ++++++++++---- | |
25 | proxmox-backup-client.c | 3 +- | |
26 | proxmox-backup-client.h | 1 + | |
bf251437 | 27 | pve-backup.c | 104 ++++++++++++++++++++++++++++++--- |
0c893fd8 | 28 | qapi/block-core.json | 12 +++- |
bf251437 | 29 | 6 files changed, 143 insertions(+), 23 deletions(-) |
c96a4a38 DM |
30 | |
31 | diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c | |
53b56ca7 | 32 | index d50e99df26..cda5de792b 100644 |
c96a4a38 DM |
33 | --- a/block/monitor/block-hmp-cmds.c |
34 | +++ b/block/monitor/block-hmp-cmds.c | |
53b56ca7 | 35 | @@ -1056,6 +1056,7 @@ void hmp_backup(Monitor *mon, const QDict *qdict) |
bf251437 FE |
36 | NULL, // PBS fingerprint |
37 | NULL, // PBS backup-id | |
c96a4a38 DM |
38 | false, 0, // PBS backup-time |
39 | + false, false, // PBS incremental | |
40 | true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, | |
bf251437 | 41 | NULL, NULL, |
c96a4a38 | 42 | devlist, qdict_haskey(qdict, "speed"), speed, &error); |
20be7fa0 | 43 | diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c |
bf251437 | 44 | index 9e1bd57aeb..087161a967 100644 |
20be7fa0 TL |
45 | --- a/monitor/hmp-cmds.c |
46 | +++ b/monitor/hmp-cmds.c | |
bf251437 | 47 | @@ -171,19 +171,42 @@ void hmp_info_backup(Monitor *mon, const QDict *qdict) |
20be7fa0 TL |
48 | monitor_printf(mon, "End time: %s", ctime(&info->end_time)); |
49 | } | |
50 | ||
51 | - int per = (info->has_total && info->total && | |
52 | - info->has_transferred && info->transferred) ? | |
53 | - (info->transferred * 100)/info->total : 0; | |
54 | - int zero_per = (info->has_total && info->total && | |
55 | - info->has_zero_bytes && info->zero_bytes) ? | |
56 | - (info->zero_bytes * 100)/info->total : 0; | |
57 | monitor_printf(mon, "Backup file: %s\n", info->backup_file); | |
58 | monitor_printf(mon, "Backup uuid: %s\n", info->uuid); | |
59 | - monitor_printf(mon, "Total size: %zd\n", info->total); | |
60 | - monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n", | |
61 | - info->transferred, per); | |
62 | - monitor_printf(mon, "Zero bytes: %zd (%d%%)\n", | |
63 | - info->zero_bytes, zero_per); | |
64 | + | |
65 | + if (!(info->has_total && info->total)) { | |
66 | + // this should not happen normally | |
67 | + monitor_printf(mon, "Total size: %d\n", 0); | |
68 | + } else { | |
69 | + bool incremental = false; | |
70 | + size_t total_or_dirty = info->total; | |
71 | + if (info->has_transferred) { | |
72 | + if (info->has_dirty && info->dirty) { | |
73 | + if (info->dirty < info->total) { | |
74 | + total_or_dirty = info->dirty; | |
75 | + incremental = true; | |
76 | + } | |
77 | + } | |
78 | + } | |
79 | + | |
80 | + int per = (info->transferred * 100)/total_or_dirty; | |
81 | + | |
82 | + monitor_printf(mon, "Backup mode: %s\n", incremental ? "incremental" : "full"); | |
83 | + | |
84 | + int zero_per = (info->has_zero_bytes && info->zero_bytes) ? | |
85 | + (info->zero_bytes * 100)/info->total : 0; | |
86 | + monitor_printf(mon, "Total size: %zd\n", info->total); | |
87 | + monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n", | |
88 | + info->transferred, per); | |
89 | + monitor_printf(mon, "Zero bytes: %zd (%d%%)\n", | |
90 | + info->zero_bytes, zero_per); | |
91 | + | |
92 | + if (info->has_reused) { | |
93 | + int reused_per = (info->reused * 100)/total_or_dirty; | |
94 | + monitor_printf(mon, "Reused bytes: %zd (%d%%)\n", | |
95 | + info->reused, reused_per); | |
96 | + } | |
97 | + } | |
98 | } | |
99 | ||
100 | qapi_free_BackupStatus(info); | |
c96a4a38 | 101 | diff --git a/proxmox-backup-client.c b/proxmox-backup-client.c |
72ae34ec | 102 | index a8f6653a81..4ce7bc0b5e 100644 |
c96a4a38 DM |
103 | --- a/proxmox-backup-client.c |
104 | +++ b/proxmox-backup-client.c | |
72ae34ec | 105 | @@ -89,6 +89,7 @@ proxmox_backup_co_register_image( |
c96a4a38 DM |
106 | ProxmoxBackupHandle *pbs, |
107 | const char *device_name, | |
108 | uint64_t size, | |
109 | + bool incremental, | |
110 | Error **errp) | |
111 | { | |
112 | Coroutine *co = qemu_coroutine_self(); | |
72ae34ec | 113 | @@ -98,7 +99,7 @@ proxmox_backup_co_register_image( |
c96a4a38 DM |
114 | int pbs_res = -1; |
115 | ||
116 | proxmox_backup_register_image_async( | |
117 | - pbs, device_name, size ,proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); | |
118 | + pbs, device_name, size, incremental, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); | |
119 | qemu_coroutine_yield(); | |
120 | if (pbs_res < 0) { | |
121 | if (errp) error_setg(errp, "backup register image failed: %s", pbs_err ? pbs_err : "unknown error"); | |
122 | diff --git a/proxmox-backup-client.h b/proxmox-backup-client.h | |
72ae34ec | 123 | index 1dda8b7d8f..8cbf645b2c 100644 |
c96a4a38 DM |
124 | --- a/proxmox-backup-client.h |
125 | +++ b/proxmox-backup-client.h | |
72ae34ec | 126 | @@ -32,6 +32,7 @@ proxmox_backup_co_register_image( |
c96a4a38 DM |
127 | ProxmoxBackupHandle *pbs, |
128 | const char *device_name, | |
129 | uint64_t size, | |
130 | + bool incremental, | |
131 | Error **errp); | |
132 | ||
133 | ||
134 | diff --git a/pve-backup.c b/pve-backup.c | |
53b56ca7 | 135 | index 389d6c84a0..c4cbff7fb1 100644 |
c96a4a38 DM |
136 | --- a/pve-backup.c |
137 | +++ b/pve-backup.c | |
bf251437 FE |
138 | @@ -7,6 +7,7 @@ |
139 | #include "sysemu/blockdev.h" | |
140 | #include "block/block_int-global-state.h" | |
141 | #include "block/blockjob.h" | |
142 | +#include "block/dirty-bitmap.h" | |
143 | #include "qapi/qapi-commands-block.h" | |
144 | #include "qapi/qmp/qerror.h" | |
145 | ||
146 | @@ -29,6 +30,8 @@ | |
c96a4a38 DM |
147 | * |
148 | */ | |
149 | ||
150 | +const char *PBS_BITMAP_NAME = "pbs-incremental-dirty-bitmap"; | |
151 | + | |
152 | static struct PVEBackupState { | |
153 | struct { | |
154 | // Everithing accessed from qmp_backup_query command is protected using lock | |
bf251437 | 155 | @@ -40,7 +43,9 @@ static struct PVEBackupState { |
20be7fa0 TL |
156 | uuid_t uuid; |
157 | char uuid_str[37]; | |
158 | size_t total; | |
159 | + size_t dirty; | |
160 | size_t transferred; | |
161 | + size_t reused; | |
162 | size_t zero_bytes; | |
163 | } stat; | |
164 | int64_t speed; | |
bf251437 | 165 | @@ -67,6 +72,7 @@ typedef struct PVEBackupDevInfo { |
c96a4a38 DM |
166 | uint8_t dev_id; |
167 | bool completed; | |
168 | char targetfile[PATH_MAX]; | |
169 | + BdrvDirtyBitmap *bitmap; | |
170 | BlockDriverState *target; | |
171 | } PVEBackupDevInfo; | |
172 | ||
bf251437 | 173 | @@ -108,11 +114,12 @@ static bool pvebackup_error_or_canceled(void) |
20be7fa0 TL |
174 | return error_or_canceled; |
175 | } | |
176 | ||
177 | -static void pvebackup_add_transfered_bytes(size_t transferred, size_t zero_bytes) | |
178 | +static void pvebackup_add_transfered_bytes(size_t transferred, size_t zero_bytes, size_t reused) | |
179 | { | |
180 | qemu_mutex_lock(&backup_state.stat.lock); | |
181 | backup_state.stat.zero_bytes += zero_bytes; | |
182 | backup_state.stat.transferred += transferred; | |
183 | + backup_state.stat.reused += reused; | |
184 | qemu_mutex_unlock(&backup_state.stat.lock); | |
185 | } | |
186 | ||
bf251437 | 187 | @@ -151,7 +158,8 @@ pvebackup_co_dump_pbs_cb( |
20be7fa0 TL |
188 | pvebackup_propagate_error(local_err); |
189 | return pbs_res; | |
190 | } else { | |
191 | - pvebackup_add_transfered_bytes(size, !buf ? size : 0); | |
192 | + size_t reused = (pbs_res == 0) ? size : 0; | |
193 | + pvebackup_add_transfered_bytes(size, !buf ? size : 0, reused); | |
194 | } | |
195 | ||
196 | return size; | |
bf251437 | 197 | @@ -211,11 +219,11 @@ pvebackup_co_dump_vma_cb( |
20be7fa0 TL |
198 | } else { |
199 | if (remaining >= VMA_CLUSTER_SIZE) { | |
200 | assert(ret == VMA_CLUSTER_SIZE); | |
201 | - pvebackup_add_transfered_bytes(VMA_CLUSTER_SIZE, zero_bytes); | |
202 | + pvebackup_add_transfered_bytes(VMA_CLUSTER_SIZE, zero_bytes, 0); | |
203 | remaining -= VMA_CLUSTER_SIZE; | |
204 | } else { | |
205 | assert(ret == remaining); | |
206 | - pvebackup_add_transfered_bytes(remaining, zero_bytes); | |
207 | + pvebackup_add_transfered_bytes(remaining, zero_bytes, 0); | |
208 | remaining = 0; | |
209 | } | |
210 | } | |
bf251437 | 211 | @@ -251,6 +259,18 @@ static void coroutine_fn pvebackup_co_cleanup(void *unused) |
c96a4a38 DM |
212 | if (local_err != NULL) { |
213 | pvebackup_propagate_error(local_err); | |
214 | } | |
215 | + } else { | |
216 | + // on error or cancel we cannot ensure synchronization of dirty | |
217 | + // bitmaps with backup server, so remove all and do full backup next | |
218 | + GList *l = backup_state.di_list; | |
219 | + while (l) { | |
220 | + PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; | |
221 | + l = g_list_next(l); | |
222 | + | |
223 | + if (di->bitmap) { | |
224 | + bdrv_release_dirty_bitmap(di->bitmap); | |
225 | + } | |
226 | + } | |
227 | } | |
228 | ||
229 | proxmox_backup_disconnect(backup_state.pbs); | |
bf251437 | 230 | @@ -306,6 +326,12 @@ static void pvebackup_complete_cb(void *opaque, int ret) |
20be7fa0 TL |
231 | // remove self from job queue |
232 | backup_state.di_list = g_list_remove(backup_state.di_list, di); | |
233 | ||
234 | + if (di->bitmap && ret < 0) { | |
235 | + // on error or cancel we cannot ensure synchronization of dirty | |
236 | + // bitmaps with backup server, so remove all and do full backup next | |
237 | + bdrv_release_dirty_bitmap(di->bitmap); | |
238 | + } | |
239 | + | |
240 | g_free(di); | |
241 | ||
242 | qemu_mutex_unlock(&backup_state.backup_mutex); | |
bf251437 | 243 | @@ -470,12 +496,18 @@ static bool create_backup_jobs(void) { |
c96a4a38 DM |
244 | |
245 | assert(di->target != NULL); | |
246 | ||
247 | + MirrorSyncMode sync_mode = MIRROR_SYNC_MODE_FULL; | |
248 | + BitmapSyncMode bitmap_mode = BITMAP_SYNC_MODE_NEVER; | |
249 | + if (di->bitmap) { | |
250 | + sync_mode = MIRROR_SYNC_MODE_BITMAP; | |
251 | + bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS; | |
252 | + } | |
253 | AioContext *aio_context = bdrv_get_aio_context(di->bs); | |
254 | aio_context_acquire(aio_context); | |
255 | ||
256 | BlockJob *job = backup_job_create( | |
257 | - NULL, di->bs, di->target, backup_state.speed, MIRROR_SYNC_MODE_FULL, NULL, | |
8dca018b | 258 | - BITMAP_SYNC_MODE_NEVER, false, NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, |
c96a4a38 | 259 | + NULL, di->bs, di->target, backup_state.speed, sync_mode, di->bitmap, |
8dca018b SR |
260 | + bitmap_mode, false, NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, |
261 | JOB_DEFAULT, pvebackup_complete_cb, di, NULL, &local_err); | |
c96a4a38 DM |
262 | |
263 | aio_context_release(aio_context); | |
bf251437 FE |
264 | @@ -521,6 +553,8 @@ typedef struct QmpBackupTask { |
265 | bool has_backup_time; | |
c96a4a38 | 266 | const char *fingerprint; |
c96a4a38 | 267 | int64_t backup_time; |
0c893fd8 SR |
268 | + bool has_use_dirty_bitmap; |
269 | + bool use_dirty_bitmap; | |
c96a4a38 DM |
270 | bool has_format; |
271 | BackupFormat format; | |
bf251437 FE |
272 | const char *config_file; |
273 | @@ -609,6 +643,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) | |
20be7fa0 TL |
274 | } |
275 | ||
276 | size_t total = 0; | |
277 | + size_t dirty = 0; | |
278 | ||
279 | l = di_list; | |
280 | while (l) { | |
bf251437 | 281 | @@ -646,6 +681,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) |
c96a4a38 DM |
282 | int dump_cb_block_size = PROXMOX_BACKUP_DEFAULT_CHUNK_SIZE; // Hardcoded (4M) |
283 | firewall_name = "fw.conf"; | |
284 | ||
0c893fd8 | 285 | + bool use_dirty_bitmap = task->has_use_dirty_bitmap && task->use_dirty_bitmap; |
c96a4a38 DM |
286 | + |
287 | char *pbs_err = NULL; | |
288 | pbs = proxmox_backup_new( | |
289 | task->backup_file, | |
bf251437 | 290 | @@ -665,7 +702,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) |
c96a4a38 DM |
291 | goto err; |
292 | } | |
293 | ||
294 | - if (proxmox_backup_co_connect(pbs, task->errp) < 0) | |
295 | + int connect_result = proxmox_backup_co_connect(pbs, task->errp); | |
296 | + if (connect_result < 0) | |
297 | goto err; | |
298 | ||
299 | /* register all devices */ | |
bf251437 | 300 | @@ -676,9 +714,40 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) |
c96a4a38 DM |
301 | |
302 | const char *devname = bdrv_get_device_name(di->bs); | |
303 | ||
304 | - int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, task->errp); | |
305 | - if (dev_id < 0) | |
306 | + BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(di->bs, PBS_BITMAP_NAME); | |
0c893fd8 | 307 | + bool expect_only_dirty = false; |
c96a4a38 | 308 | + |
0c893fd8 | 309 | + if (use_dirty_bitmap) { |
c96a4a38 DM |
310 | + if (bitmap == NULL) { |
311 | + bitmap = bdrv_create_dirty_bitmap(di->bs, dump_cb_block_size, PBS_BITMAP_NAME, task->errp); | |
312 | + if (!bitmap) { | |
313 | + goto err; | |
314 | + } | |
c96a4a38 | 315 | + } else { |
0c893fd8 SR |
316 | + expect_only_dirty = proxmox_backup_check_incremental(pbs, devname, di->size) != 0; |
317 | + } | |
318 | + | |
319 | + if (expect_only_dirty) { | |
20be7fa0 | 320 | + dirty += bdrv_get_dirty_count(bitmap); |
0c893fd8 SR |
321 | + } else { |
322 | + /* mark entire bitmap as dirty to make full backup */ | |
323 | + bdrv_set_dirty_bitmap(bitmap, 0, di->size); | |
324 | + dirty += di->size; | |
c96a4a38 DM |
325 | + } |
326 | + di->bitmap = bitmap; | |
0c893fd8 | 327 | + } else { |
20be7fa0 | 328 | + dirty += di->size; |
0c893fd8 SR |
329 | + |
330 | + /* after a full backup the old dirty bitmap is invalid anyway */ | |
331 | + if (bitmap != NULL) { | |
332 | + bdrv_release_dirty_bitmap(bitmap); | |
333 | + } | |
c96a4a38 DM |
334 | + } |
335 | + | |
0c893fd8 | 336 | + int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, expect_only_dirty, task->errp); |
c96a4a38 DM |
337 | + if (dev_id < 0) { |
338 | goto err; | |
339 | + } | |
340 | ||
341 | if (!(di->target = bdrv_backup_dump_create(dump_cb_block_size, di->size, pvebackup_co_dump_pbs_cb, di, task->errp))) { | |
342 | goto err; | |
bf251437 | 343 | @@ -687,6 +756,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) |
20be7fa0 TL |
344 | di->dev_id = dev_id; |
345 | } | |
346 | } else if (format == BACKUP_FORMAT_VMA) { | |
347 | + dirty = total; | |
348 | + | |
349 | vmaw = vma_writer_create(task->backup_file, uuid, &local_err); | |
350 | if (!vmaw) { | |
351 | if (local_err) { | |
bf251437 | 352 | @@ -714,6 +785,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) |
20be7fa0 TL |
353 | } |
354 | } | |
355 | } else if (format == BACKUP_FORMAT_DIR) { | |
356 | + dirty = total; | |
357 | + | |
358 | if (mkdir(task->backup_file, 0640) != 0) { | |
359 | error_setg_errno(task->errp, errno, "can't create directory '%s'\n", | |
360 | task->backup_file); | |
bf251437 | 361 | @@ -786,8 +859,10 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque) |
20be7fa0 TL |
362 | char *uuid_str = g_strdup(backup_state.stat.uuid_str); |
363 | ||
364 | backup_state.stat.total = total; | |
365 | + backup_state.stat.dirty = dirty; | |
366 | backup_state.stat.transferred = 0; | |
367 | backup_state.stat.zero_bytes = 0; | |
0c893fd8 | 368 | + backup_state.stat.reused = format == BACKUP_FORMAT_PBS && dirty >= total ? 0 : total - dirty; |
20be7fa0 TL |
369 | |
370 | qemu_mutex_unlock(&backup_state.stat.lock); | |
371 | ||
bf251437 | 372 | @@ -811,6 +886,10 @@ err: |
c96a4a38 DM |
373 | PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; |
374 | l = g_list_next(l); | |
375 | ||
376 | + if (di->bitmap) { | |
377 | + bdrv_release_dirty_bitmap(di->bitmap); | |
378 | + } | |
379 | + | |
380 | if (di->target) { | |
53b56ca7 | 381 | bdrv_co_unref(di->target); |
c96a4a38 | 382 | } |
bf251437 FE |
383 | @@ -852,6 +931,7 @@ UuidInfo *qmp_backup( |
384 | const char *fingerprint, | |
385 | const char *backup_id, | |
c96a4a38 | 386 | bool has_backup_time, int64_t backup_time, |
0c893fd8 | 387 | + bool has_use_dirty_bitmap, bool use_dirty_bitmap, |
c96a4a38 | 388 | bool has_format, BackupFormat format, |
bf251437 FE |
389 | const char *config_file, |
390 | const char *firewall_file, | |
391 | @@ -866,6 +946,8 @@ UuidInfo *qmp_backup( | |
c96a4a38 DM |
392 | .backup_id = backup_id, |
393 | .has_backup_time = has_backup_time, | |
394 | .backup_time = backup_time, | |
0c893fd8 SR |
395 | + .has_use_dirty_bitmap = has_use_dirty_bitmap, |
396 | + .use_dirty_bitmap = use_dirty_bitmap, | |
c96a4a38 DM |
397 | .has_format = has_format, |
398 | .format = format, | |
bf251437 FE |
399 | .config_file = config_file, |
400 | @@ -927,10 +1009,14 @@ BackupStatus *qmp_query_backup(Error **errp) | |
20be7fa0 TL |
401 | |
402 | info->has_total = true; | |
403 | info->total = backup_state.stat.total; | |
404 | + info->has_dirty = true; | |
405 | + info->dirty = backup_state.stat.dirty; | |
406 | info->has_zero_bytes = true; | |
407 | info->zero_bytes = backup_state.stat.zero_bytes; | |
408 | info->has_transferred = true; | |
409 | info->transferred = backup_state.stat.transferred; | |
410 | + info->has_reused = true; | |
411 | + info->reused = backup_state.stat.reused; | |
412 | ||
413 | qemu_mutex_unlock(&backup_state.stat.lock); | |
414 | ||
c96a4a38 | 415 | diff --git a/qapi/block-core.json b/qapi/block-core.json |
bf251437 | 416 | index 16fb4c5ea0..92f90a898a 100644 |
c96a4a38 DM |
417 | --- a/qapi/block-core.json |
418 | +++ b/qapi/block-core.json | |
bf251437 | 419 | @@ -848,8 +848,13 @@ |
20be7fa0 TL |
420 | # |
421 | # @total: total amount of bytes involved in the backup process | |
422 | # | |
0c893fd8 | 423 | +# @dirty: with incremental mode (PBS) this is the amount of bytes involved |
20be7fa0 TL |
424 | +# in the backup process which are marked dirty. |
425 | +# | |
426 | # @transferred: amount of bytes already backed up. | |
427 | # | |
428 | +# @reused: amount of bytes reused due to deduplication. | |
429 | +# | |
430 | # @zero-bytes: amount of 'zero' bytes detected. | |
431 | # | |
432 | # @start-time: time (epoch) when backup job started. | |
bf251437 | 433 | @@ -862,8 +867,8 @@ |
20be7fa0 TL |
434 | # |
435 | ## | |
436 | { 'struct': 'BackupStatus', | |
437 | - 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', | |
438 | - '*transferred': 'int', '*zero-bytes': 'int', | |
439 | + 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', '*dirty': 'int', | |
440 | + '*transferred': 'int', '*zero-bytes': 'int', '*reused': 'int', | |
441 | '*start-time': 'int', '*end-time': 'int', | |
442 | '*backup-file': 'str', '*uuid': 'str' } } | |
443 | ||
bf251437 | 444 | @@ -906,6 +911,8 @@ |
c96a4a38 DM |
445 | # |
446 | # @backup-time: backup timestamp (Unix epoch, required for format 'pbs') | |
447 | # | |
0c893fd8 | 448 | +# @use-dirty-bitmap: use dirty bitmap to detect incremental changes since last job (optional for format 'pbs') |
c96a4a38 DM |
449 | +# |
450 | # Returns: the uuid of the backup job | |
451 | # | |
452 | ## | |
bf251437 | 453 | @@ -916,6 +923,7 @@ |
c96a4a38 DM |
454 | '*fingerprint': 'str', |
455 | '*backup-id': 'str', | |
456 | '*backup-time': 'int', | |
0c893fd8 | 457 | + '*use-dirty-bitmap': 'bool', |
c96a4a38 DM |
458 | '*format': 'BackupFormat', |
459 | '*config-file': 'str', | |
460 | '*firewall-file': 'str', |