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