]> git.proxmox.com Git - pve-qemu-kvm.git/blob - debian/patches/old/0003-add-backup-related-monitor-commands.patch
bump version to 2.9.0-1~rc2+5
[pve-qemu-kvm.git] / debian / patches / old / 0003-add-backup-related-monitor-commands.patch
1 From cf9cc8878d3246069da3fdf7ec865b7b983487fe Mon Sep 17 00:00:00 2001
2 From: Dietmar Maurer <dietmar@proxmox.com>
3 Date: Tue, 13 Nov 2012 11:27:56 +0100
4 Subject: [PATCH v5 3/6] add backup related monitor commands
5
6 We use a generic BackupDriver struct to encapsulate all archive format
7 related function.
8
9 Another option would be to simply dump <devid,cluster_num,cluster_data> to
10 the output fh (pipe), and an external binary saves the data. That way we
11 could move the whole archive format related code out of qemu.
12
13 Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
14 ---
15 backup.h | 15 ++
16 blockdev.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 hmp-commands.hx | 31 ++++
18 hmp.c | 63 ++++++++
19 hmp.h | 3 +
20 monitor.c | 7 +
21 qapi-schema.json | 95 ++++++++++++
22 qmp-commands.hx | 27 ++++
23 8 files changed, 664 insertions(+), 0 deletions(-)
24
25 diff --git a/backup.h b/backup.h
26 index 9b1ea1c..22598a6 100644
27 --- a/backup.h
28 +++ b/backup.h
29 @@ -14,6 +14,9 @@
30 #ifndef QEMU_BACKUP_H
31 #define QEMU_BACKUP_H
32
33 +#include <uuid/uuid.h>
34 +#include "block/block.h"
35 +
36 #define BACKUP_CLUSTER_BITS 16
37 #define BACKUP_CLUSTER_SIZE (1<<BACKUP_CLUSTER_BITS)
38 #define BACKUP_BLOCKS_PER_CLUSTER (BACKUP_CLUSTER_SIZE/BDRV_SECTOR_SIZE)
39 @@ -27,4 +30,16 @@ int backup_job_create(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb,
40 BlockDriverCompletionFunc *backup_complete_cb,
41 void *opaque, int64_t speed);
42
43 +typedef struct BackupDriver {
44 + const char *format;
45 + void *(*open)(const char *filename, uuid_t uuid, Error **errp);
46 + int (*close)(void *opaque, Error **errp);
47 + int (*register_config)(void *opaque, const char *name, gpointer data,
48 + size_t data_len);
49 + int (*register_stream)(void *opaque, const char *devname, size_t size);
50 + int (*dump)(void *opaque, uint8_t dev_id, int64_t cluster_num,
51 + unsigned char *buf, size_t *zero_bytes);
52 + int (*complete)(void *opaque, uint8_t dev_id, int ret);
53 +} BackupDriver;
54 +
55 #endif /* QEMU_BACKUP_H */
56 diff --git a/blockdev.c b/blockdev.c
57 index 63e6f1e..84f598d 100644
58 --- a/blockdev.c
59 +++ b/blockdev.c
60 @@ -20,6 +20,7 @@
61 #include "qmp-commands.h"
62 #include "trace.h"
63 #include "sysemu/arch_init.h"
64 +#include "backup.h"
65
66 static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
67
68 @@ -1334,6 +1335,428 @@ void qmp_drive_mirror(const char *device, const char *target,
69 drive_get_ref(drive_get_by_blockdev(bs));
70 }
71
72 +/* Backup related function */
73 +
74 +static void backup_run_next_job(void);
75 +
76 +static struct GenericBackupState {
77 + Error *error;
78 + bool cancel;
79 + uuid_t uuid;
80 + char uuid_str[37];
81 + int64_t speed;
82 + time_t start_time;
83 + time_t end_time;
84 + char *backup_file;
85 + const BackupDriver *driver;
86 + void *writer;
87 + GList *bcb_list;
88 + size_t total;
89 + size_t transferred;
90 + size_t zero_bytes;
91 +} backup_state;
92 +
93 +typedef struct BackupCB {
94 + BlockDriverState *bs;
95 + uint8_t dev_id;
96 + bool started;
97 + bool completed;
98 + size_t size;
99 + size_t transferred;
100 + size_t zero_bytes;
101 +} BackupCB;
102 +
103 +static int backup_dump_cb(void *opaque, BlockDriverState *bs,
104 + int64_t cluster_num, unsigned char *buf)
105 +{
106 + BackupCB *bcb = opaque;
107 +
108 + assert(backup_state.driver);
109 + assert(backup_state.writer);
110 + assert(backup_state.driver->dump);
111 +
112 + size_t zero_bytes = 0;
113 + int bytes = backup_state.driver->dump(backup_state.writer,
114 + bcb->dev_id, cluster_num,
115 + buf, &zero_bytes);
116 +
117 + if (bytes > 0) {
118 + bcb->transferred += bytes;
119 + backup_state.transferred += bytes;
120 + if (zero_bytes) {
121 + bcb->zero_bytes += bytes;
122 + backup_state.zero_bytes += zero_bytes;
123 + }
124 + }
125 +
126 + return bytes;
127 +}
128 +
129 +static void backup_cleanup(void)
130 +{
131 + if (backup_state.writer && backup_state.driver) {
132 + backup_state.end_time = time(NULL);
133 + Error *local_err = NULL;
134 + backup_state.driver->close(backup_state.writer, &local_err);
135 + error_propagate(&backup_state.error, local_err);
136 + backup_state.writer = NULL;
137 + }
138 +
139 + if (backup_state.bcb_list) {
140 + GList *l = backup_state.bcb_list;
141 + while (l) {
142 + BackupCB *bcb = l->data;
143 + l = g_list_next(l);
144 + drive_put_ref_bh_schedule(drive_get_by_blockdev(bcb->bs));
145 + g_free(bcb);
146 + }
147 + g_list_free(backup_state.bcb_list);
148 + backup_state.bcb_list = NULL;
149 + }
150 +}
151 +
152 +static void backup_complete_cb(void *opaque, int ret)
153 +{
154 + BackupCB *bcb = opaque;
155 +
156 + assert(backup_state.driver);
157 + assert(backup_state.writer);
158 + assert(backup_state.driver->complete);
159 + assert(backup_state.driver->close);
160 +
161 + bcb->completed = true;
162 +
163 + backup_state.driver->complete(backup_state.writer, bcb->dev_id, ret);
164 +
165 + if (!backup_state.cancel) {
166 + backup_run_next_job();
167 + }
168 +}
169 +
170 +static void backup_cancel(void)
171 +{
172 + backup_state.cancel = true;
173 +
174 + if (!backup_state.error) {
175 + error_setg(&backup_state.error, "backup cancelled");
176 + }
177 +
178 + /* drain all i/o (awake jobs waiting for aio) */
179 + bdrv_drain_all();
180 +
181 + int job_count = 0;
182 + GList *l = backup_state.bcb_list;
183 + while (l) {
184 + BackupCB *bcb = l->data;
185 + l = g_list_next(l);
186 + BlockJob *job = bcb->bs->job;
187 + if (job) {
188 + job_count++;
189 + if (!bcb->started) {
190 + bcb->started = true;
191 + backup_job_start(bcb->bs, true);
192 + }
193 + if (!bcb->completed) {
194 + block_job_cancel_sync(job);
195 + }
196 + }
197 + }
198 +
199 + backup_cleanup();
200 +}
201 +
202 +void qmp_backup_cancel(Error **errp)
203 +{
204 + backup_cancel();
205 +}
206 +
207 +static void backup_run_next_job(void)
208 +{
209 + GList *l = backup_state.bcb_list;
210 + while (l) {
211 + BackupCB *bcb = l->data;
212 + l = g_list_next(l);
213 +
214 + if (bcb->bs && bcb->bs->job && !bcb->completed) {
215 + if (!bcb->started) {
216 + bcb->started = true;
217 + bool cancel = backup_state.error || backup_state.cancel;
218 + backup_job_start(bcb->bs, cancel);
219 + }
220 + return;
221 + }
222 + }
223 +
224 + backup_cleanup();
225 +}
226 +
227 +static void backup_start_jobs(void)
228 +{
229 + /* create all jobs (one for each device), start first one */
230 + GList *l = backup_state.bcb_list;
231 + while (l) {
232 + BackupCB *bcb = l->data;
233 + l = g_list_next(l);
234 +
235 + if (backup_job_create(bcb->bs, backup_dump_cb, backup_complete_cb,
236 + bcb, backup_state.speed) != 0) {
237 + error_setg(&backup_state.error, "backup_job_create failed");
238 + backup_cancel();
239 + return;
240 + }
241 + }
242 +
243 + backup_run_next_job();
244 +}
245 +
246 +char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
247 + bool has_config_file, const char *config_file,
248 + bool has_devlist, const char *devlist,
249 + bool has_speed, int64_t speed, Error **errp)
250 +{
251 + BlockDriverState *bs;
252 + Error *local_err = NULL;
253 + uuid_t uuid;
254 + void *writer = NULL;
255 + gchar **devs = NULL;
256 + GList *bcblist = NULL;
257 +
258 + if (backup_state.bcb_list) {
259 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
260 + "previous backup not finished");
261 + return NULL;
262 + }
263 +
264 + /* Todo: try to auto-detect format based on file name */
265 + format = has_format ? format : BACKUP_FORMAT_VMA;
266 +
267 + /* fixme: find driver for specifued format */
268 + const BackupDriver *driver = NULL;
269 +
270 + if (!driver) {
271 + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
272 + return NULL;
273 + }
274 +
275 + if (has_devlist) {
276 + devs = g_strsplit_set(devlist, ",;:", -1);
277 +
278 + gchar **d = devs;
279 + while (d && *d) {
280 + bs = bdrv_find(*d);
281 + if (bs) {
282 + if (bdrv_is_read_only(bs)) {
283 + error_set(errp, QERR_DEVICE_IS_READ_ONLY, *d);
284 + goto err;
285 + }
286 + if (!bdrv_is_inserted(bs)) {
287 + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
288 + goto err;
289 + }
290 + BackupCB *bcb = g_new0(BackupCB, 1);
291 + bcb->bs = bs;
292 + bcblist = g_list_append(bcblist, bcb);
293 + } else {
294 + error_set(errp, QERR_DEVICE_NOT_FOUND, *d);
295 + goto err;
296 + }
297 + d++;
298 + }
299 +
300 + } else {
301 +
302 + bs = NULL;
303 + while ((bs = bdrv_next(bs))) {
304 +
305 + if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
306 + continue;
307 + }
308 +
309 + BackupCB *bcb = g_new0(BackupCB, 1);
310 + bcb->bs = bs;
311 + bcblist = g_list_append(bcblist, bcb);
312 + }
313 + }
314 +
315 + if (!bcblist) {
316 + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
317 + goto err;
318 + }
319 +
320 + GList *l = bcblist;
321 + while (l) {
322 + BackupCB *bcb = l->data;
323 + l = g_list_next(l);
324 + if (bcb->bs->job) {
325 + error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bcb->bs));
326 + goto err;
327 + }
328 + }
329 +
330 + uuid_generate(uuid);
331 +
332 + writer = driver->open(backup_file, uuid, &local_err);
333 + if (!writer) {
334 + if (error_is_set(&local_err)) {
335 + error_propagate(errp, local_err);
336 + }
337 + goto err;
338 + }
339 +
340 + size_t total = 0;
341 +
342 + /* register all devices for vma writer */
343 + l = bcblist;
344 + while (l) {
345 + BackupCB *bcb = l->data;
346 + l = g_list_next(l);
347 +
348 + int64_t size = bdrv_getlength(bcb->bs);
349 + const char *devname = bdrv_get_device_name(bcb->bs);
350 + bcb->dev_id = driver->register_stream(writer, devname, size);
351 + if (bcb->dev_id <= 0) {
352 + error_set(errp, ERROR_CLASS_GENERIC_ERROR,
353 + "register_stream failed");
354 + goto err;
355 + }
356 + bcb->size = size;
357 + total += size;
358 + }
359 +
360 + /* add configuration file to archive */
361 + if (has_config_file) {
362 + char *cdata = NULL;
363 + gsize clen = 0;
364 + GError *err = NULL;
365 + if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
366 + error_setg(errp, "unable to read file '%s'", config_file);
367 + goto err;
368 + }
369 +
370 + const char *basename = g_path_get_basename(config_file);
371 + if (driver->register_config(writer, basename, cdata, clen) < 0) {
372 + error_setg(errp, "register_config failed");
373 + g_free(cdata);
374 + goto err;
375 + }
376 + g_free(cdata);
377 + }
378 +
379 + /* initialize global backup_state now */
380 +
381 + backup_state.cancel = false;
382 +
383 + if (backup_state.error) {
384 + error_free(backup_state.error);
385 + backup_state.error = NULL;
386 + }
387 +
388 + backup_state.driver = driver;
389 +
390 + backup_state.speed = (has_speed && speed > 0) ? speed : 0;
391 +
392 + backup_state.start_time = time(NULL);
393 + backup_state.end_time = 0;
394 +
395 + if (backup_state.backup_file) {
396 + g_free(backup_state.backup_file);
397 + }
398 + backup_state.backup_file = g_strdup(backup_file);
399 +
400 + backup_state.writer = writer;
401 +
402 + uuid_copy(backup_state.uuid, uuid);
403 + uuid_unparse_lower(uuid, backup_state.uuid_str);
404 +
405 + backup_state.bcb_list = bcblist;
406 +
407 + backup_state.total = total;
408 + backup_state.transferred = 0;
409 + backup_state.zero_bytes = 0;
410 +
411 + /* Grab a reference so hotplug does not delete the
412 + * BlockDriverState from underneath us.
413 + */
414 + l = bcblist;
415 + while (l) {
416 + BackupCB *bcb = l->data;
417 + l = g_list_next(l);
418 + drive_get_ref(drive_get_by_blockdev(bcb->bs));
419 + }
420 +
421 + backup_start_jobs();
422 +
423 + return g_strdup(backup_state.uuid_str);
424 +
425 +err:
426 +
427 + l = bcblist;
428 + while (l) {
429 + g_free(l->data);
430 + l = g_list_next(l);
431 + }
432 + g_list_free(bcblist);
433 +
434 + if (devs) {
435 + g_strfreev(devs);
436 + }
437 +
438 + if (writer) {
439 + unlink(backup_file);
440 + if (driver) {
441 + Error *err = NULL;
442 + driver->close(writer, &err);
443 + }
444 + }
445 +
446 + return NULL;
447 +}
448 +
449 +BackupStatus *qmp_query_backup(Error **errp)
450 +{
451 + BackupStatus *info = g_malloc0(sizeof(*info));
452 +
453 + if (!backup_state.start_time) {
454 + /* not started, return {} */
455 + return info;
456 + }
457 +
458 + info->has_status = true;
459 + info->has_start_time = true;
460 + info->start_time = backup_state.start_time;
461 +
462 + if (backup_state.backup_file) {
463 + info->has_backup_file = true;
464 + info->backup_file = g_strdup(backup_state.backup_file);
465 + }
466 +
467 + info->has_uuid = true;
468 + info->uuid = g_strdup(backup_state.uuid_str);
469 +
470 + if (backup_state.end_time) {
471 + if (backup_state.error) {
472 + info->status = g_strdup("error");
473 + info->has_errmsg = true;
474 + info->errmsg = g_strdup(error_get_pretty(backup_state.error));
475 + } else {
476 + info->status = g_strdup("done");
477 + }
478 + info->has_end_time = true;
479 + info->end_time = backup_state.end_time;
480 + } else {
481 + info->status = g_strdup("active");
482 + }
483 +
484 + info->has_total = true;
485 + info->total = backup_state.total;
486 + info->has_zero_bytes = true;
487 + info->zero_bytes = backup_state.zero_bytes;
488 + info->has_transferred = true;
489 + info->transferred = backup_state.transferred;
490 +
491 + return info;
492 +}
493 +
494 static BlockJob *find_block_job(const char *device)
495 {
496 BlockDriverState *bs;
497 diff --git a/hmp-commands.hx b/hmp-commands.hx
498 index 64008a9..0f178d8 100644
499 --- a/hmp-commands.hx
500 +++ b/hmp-commands.hx
501 @@ -83,6 +83,35 @@ STEXI
502 Copy data from a backing file into a block device.
503 ETEXI
504
505 + {
506 + .name = "backup",
507 + .args_type = "backupfile:s,speed:o?,devlist:s?",
508 + .params = "backupfile [speed [devlist]]",
509 + .help = "create a VM Backup.",
510 + .mhandler.cmd = hmp_backup,
511 + },
512 +
513 +STEXI
514 +@item backup
515 +@findex backup
516 +Create a VM backup.
517 +ETEXI
518 +
519 + {
520 + .name = "backup_cancel",
521 + .args_type = "",
522 + .params = "",
523 + .help = "cancel the current VM backup",
524 + .mhandler.cmd = hmp_backup_cancel,
525 + },
526 +
527 +STEXI
528 +@item backup_cancel
529 +@findex backup_cancel
530 +Cancel the current VM backup.
531 +
532 +ETEXI
533 +
534 {
535 .name = "block_job_set_speed",
536 .args_type = "device:B,speed:o",
537 @@ -1630,6 +1659,8 @@ show CPU statistics
538 show user network stack connection states
539 @item info migrate
540 show migration status
541 +@item info backup
542 +show backup status
543 @item info migrate_capabilities
544 show current migration capabilities
545 @item info migrate_cache_size
546 diff --git a/hmp.c b/hmp.c
547 index 2f47a8a..b2c1f23 100644
548 --- a/hmp.c
549 +++ b/hmp.c
550 @@ -131,6 +131,38 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict)
551 qapi_free_MouseInfoList(mice_list);
552 }
553
554 +void hmp_info_backup(Monitor *mon, const QDict *qdict)
555 +{
556 + BackupStatus *info;
557 +
558 + info = qmp_query_backup(NULL);
559 + if (info->has_status) {
560 + if (info->has_errmsg) {
561 + monitor_printf(mon, "Backup status: %s - %s\n",
562 + info->status, info->errmsg);
563 + } else {
564 + monitor_printf(mon, "Backup status: %s\n", info->status);
565 + }
566 + }
567 + if (info->has_backup_file) {
568 + int per = (info->has_total && info->total &&
569 + info->has_transferred && info->transferred) ?
570 + (info->transferred * 100)/info->total : 0;
571 + int zero_per = (info->has_total && info->total &&
572 + info->has_zero_bytes && info->zero_bytes) ?
573 + (info->zero_bytes * 100)/info->total : 0;
574 + monitor_printf(mon, "Backup file: %s\n", info->backup_file);
575 + monitor_printf(mon, "Backup uuid: %s\n", info->uuid);
576 + monitor_printf(mon, "Total size: %zd\n", info->total);
577 + monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n",
578 + info->transferred, per);
579 + monitor_printf(mon, "Zero bytes: %zd (%d%%)\n",
580 + info->zero_bytes, zero_per);
581 + }
582 +
583 + qapi_free_BackupStatus(info);
584 +}
585 +
586 void hmp_info_migrate(Monitor *mon, const QDict *qdict)
587 {
588 MigrationInfo *info;
589 @@ -998,6 +1030,37 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
590 hmp_handle_error(mon, &error);
591 }
592
593 +void hmp_backup_cancel(Monitor *mon, const QDict *qdict)
594 +{
595 + Error *errp = NULL;
596 +
597 + qmp_backup_cancel(&errp);
598 +
599 + if (error_is_set(&errp)) {
600 + monitor_printf(mon, "%s\n", error_get_pretty(errp));
601 + error_free(errp);
602 + return;
603 + }
604 +}
605 +
606 +void hmp_backup(Monitor *mon, const QDict *qdict)
607 +{
608 + const char *backup_file = qdict_get_str(qdict, "backup-file");
609 + const char *devlist = qdict_get_try_str(qdict, "devlist");
610 + int64_t speed = qdict_get_try_int(qdict, "speed", 0);
611 +
612 + Error *errp = NULL;
613 +
614 + qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
615 + devlist, qdict_haskey(qdict, "speed"), speed, &errp);
616 +
617 + if (error_is_set(&errp)) {
618 + monitor_printf(mon, "%s\n", error_get_pretty(errp));
619 + error_free(errp);
620 + return;
621 + }
622 +}
623 +
624 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
625 {
626 Error *error = NULL;
627 diff --git a/hmp.h b/hmp.h
628 index 30b3c20..ad4cf80 100644
629 --- a/hmp.h
630 +++ b/hmp.h
631 @@ -28,6 +28,7 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict);
632 void hmp_info_migrate(Monitor *mon, const QDict *qdict);
633 void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
634 void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
635 +void hmp_info_backup(Monitor *mon, const QDict *qdict);
636 void hmp_info_cpus(Monitor *mon, const QDict *qdict);
637 void hmp_info_block(Monitor *mon, const QDict *qdict);
638 void hmp_info_blockstats(Monitor *mon, const QDict *qdict);
639 @@ -65,6 +66,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict);
640 void hmp_change(Monitor *mon, const QDict *qdict);
641 void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
642 void hmp_block_stream(Monitor *mon, const QDict *qdict);
643 +void hmp_backup(Monitor *mon, const QDict *qdict);
644 +void hmp_backup_cancel(Monitor *mon, const QDict *qdict);
645 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
646 void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
647 void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
648 diff --git a/monitor.c b/monitor.c
649 index 6a0f257..e4a810c 100644
650 --- a/monitor.c
651 +++ b/monitor.c
652 @@ -2666,6 +2666,13 @@ static mon_cmd_t info_cmds[] = {
653 },
654 #endif
655 {
656 + .name = "backup",
657 + .args_type = "",
658 + .params = "",
659 + .help = "show backup status",
660 + .mhandler.cmd = hmp_info_backup,
661 + },
662 + {
663 .name = "migrate",
664 .args_type = "",
665 .params = "",
666 diff --git a/qapi-schema.json b/qapi-schema.json
667 index 7275b5d..09ca8ef 100644
668 --- a/qapi-schema.json
669 +++ b/qapi-schema.json
670 @@ -425,6 +425,39 @@
671 { 'type': 'EventInfo', 'data': {'name': 'str'} }
672
673 ##
674 +# @BackupStatus:
675 +#
676 +# Detailed backup status.
677 +#
678 +# @status: #optional string describing the current backup status.
679 +# This can be 'active', 'done', 'error'. If this field is not
680 +# returned, no backup process has been initiated
681 +#
682 +# @errmsg: #optional error message (only returned if status is 'error')
683 +#
684 +# @total: #optional total amount of bytes involved in the backup process
685 +#
686 +# @transferred: #optional amount of bytes already backed up.
687 +#
688 +# @zero-bytes: #optional amount of 'zero' bytes detected.
689 +#
690 +# @start-time: #optional time (epoch) when backup job started.
691 +#
692 +# @end-time: #optional time (epoch) when backup job finished.
693 +#
694 +# @backupfile: #optional backup file name
695 +#
696 +# @uuid: #optional uuid for this backup job
697 +#
698 +# Since: 1.5.0
699 +##
700 +{ 'type': 'BackupStatus',
701 + 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int',
702 + '*transferred': 'int', '*zero-bytes': 'int',
703 + '*start-time': 'int', '*end-time': 'int',
704 + '*backup-file': 'str', '*uuid': 'str' } }
705 +
706 +##
707 # @query-events:
708 #
709 # Return a list of supported QMP events by this server
710 @@ -1824,6 +1857,68 @@
711 'data': { 'path': 'str' },
712 'returns': [ 'ObjectPropertyInfo' ] }
713
714 +
715 +##
716 +# @BackupFormat
717 +#
718 +# An enumeration of supported backup formats.
719 +#
720 +# @vma: Proxmox vma backup format
721 +##
722 +{ 'enum': 'BackupFormat',
723 + 'data': [ 'vma' ] }
724 +
725 +##
726 +# @backup:
727 +#
728 +# Starts a VM backup.
729 +#
730 +# @backup-file: the backup file name
731 +#
732 +# @format: format of the backup file
733 +#
734 +# @config-filename: #optional name of a configuration file to include into
735 +# the backup archive.
736 +#
737 +# @speed: #optional the maximum speed, in bytes per second
738 +#
739 +# @devlist: #optional list of block device names (separated by ',', ';'
740 +# or ':'). By default the backup includes all writable block devices.
741 +#
742 +# Returns: the uuid of the backup job
743 +#
744 +# Since: 1.5.0
745 +##
746 +{ 'command': 'backup', 'data': { 'backup-file': 'str',
747 + '*format': 'BackupFormat',
748 + '*config-file': 'str',
749 + '*devlist': 'str', '*speed': 'int' },
750 + 'returns': 'str' }
751 +
752 +##
753 +# @query-backup
754 +#
755 +# Returns information about current/last backup task.
756 +#
757 +# Returns: @BackupStatus
758 +#
759 +# Since: 1.5.0
760 +##
761 +{ 'command': 'query-backup', 'returns': 'BackupStatus' }
762 +
763 +##
764 +# @backup-cancel
765 +#
766 +# Cancel the current executing backup process.
767 +#
768 +# Returns: nothing on success
769 +#
770 +# Notes: This command succeeds even if there is no backup process running.
771 +#
772 +# Since: 1.5.0
773 +##
774 +{ 'command': 'backup-cancel' }
775 +
776 ##
777 # @qom-get:
778 #
779 diff --git a/qmp-commands.hx b/qmp-commands.hx
780 index 799adea..17e381b 100644
781 --- a/qmp-commands.hx
782 +++ b/qmp-commands.hx
783 @@ -889,6 +889,18 @@ EQMP
784 },
785
786 {
787 + .name = "backup",
788 + .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
789 + .mhandler.cmd_new = qmp_marshal_input_backup,
790 + },
791 +
792 + {
793 + .name = "backup-cancel",
794 + .args_type = "",
795 + .mhandler.cmd_new = qmp_marshal_input_backup_cancel,
796 + },
797 +
798 + {
799 .name = "block-job-set-speed",
800 .args_type = "device:B,speed:o",
801 .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,
802 @@ -2566,6 +2578,21 @@ EQMP
803 },
804
805 SQMP
806 +
807 +query-backup
808 +-------------
809 +
810 +Backup status.
811 +
812 +EQMP
813 +
814 + {
815 + .name = "query-backup",
816 + .args_type = "",
817 + .mhandler.cmd_new = qmp_marshal_input_query_backup,
818 + },
819 +
820 +SQMP
821 migrate-set-capabilities
822 -------
823
824 --
825 1.7.2.5
826