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