]> git.proxmox.com Git - pve-qemu-kvm.git/blob - 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
1 From 7bab4498b955ced91ad838fb711e013d9b7ed81f 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 v3 3/7] 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 | 13 ++
16 blockdev.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 hmp-commands.hx | 31 +++++
18 hmp.c | 63 +++++++++
19 hmp.h | 3 +
20 monitor.c | 7 +
21 qapi-schema.json | 91 +++++++++++++
22 qmp-commands.hx | 27 ++++
23 8 files changed, 631 insertions(+), 0 deletions(-)
24
25 diff --git a/backup.h b/backup.h
26 index 87b9942..c6e5d3c 100644
27 --- a/backup.h
28 +++ b/backup.h
29 @@ -29,4 +29,17 @@ int backup_job_create(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 */
47 diff --git a/blockdev.c b/blockdev.c
48 index e73fd6e..68a8c55 100644
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
59 @@ -1321,6 +1322,401 @@ void qmp_drive_mirror(const char *device, const char *target,
60 drive_get_ref(drive_get_by_blockdev(bs));
61 }
62
63 +/* Backup related function */
64 +
65 +static void backup_run_next_job(void);
66 +
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;
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 +
125 + }
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;
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();
155 + } else {
156 + backup_run_next_job();
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);
170 + if (bcb->bs->job) {
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 +
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 +
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 +
207 + if (backup_job_create(bcb->bs, backup_dump_cb, backup_complete_cb,
208 + bcb) != 0) {
209 + backup_cancel();
210 + break;
211 + }
212 + }
213 +
214 + if (!l) { /* no errors */
215 + backup_run_next_job();
216 + }
217 +}
218 +
219 +char *qmp_backup(const char *backupfile, bool has_format, BackupFormat format,
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 */
232 + format = has_format ? format : BACKUP_FORMAT_VMA;
233 +
234 + /* fixme: find driver for specifued format */
235 + const BackupDriver *driver = NULL;
236 +
237 + if (!driver) {
238 + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
239 + return NULL;
240 + }
241 +
242 + if (has_devlist) {
243 + devs = g_strsplit_set(devlist, ",;:", -1);
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 +
299 + writer = driver->open_cb(backupfile, uuid, has_speed ? speed : 0,
300 + &local_err);
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);
339 + if (driver->register_config_cb(writer, basename, cdata, clen) < 0) {
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 +
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 +
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;
461 diff --git a/hmp-commands.hx b/hmp-commands.hx
462 index 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
510 diff --git a/hmp.c b/hmp.c
511 index 180ba2b..27fd421 100644
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 +
578 + qmp_backup(backupfile, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
579 + devlist, qdict_haskey(qdict, "speed"), speed, &errp);
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;
591 diff --git a/hmp.h b/hmp.h
592 index 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);
612 diff --git a/monitor.c b/monitor.c
613 index 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 = "",
630 diff --git a/qapi-schema.json b/qapi-schema.json
631 index 5dfa052..2d3699b 100644
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.
643 +# This can be 'active', 'done', 'error'. If this field is not
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
674 @@ -1764,6 +1797,64 @@
675 'data': { 'path': 'str' },
676 'returns': [ 'ObjectPropertyInfo' ] }
677
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' ] }
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 +##
707 +{ 'command': 'backup', 'data': { 'backupfile': 'str', '*format': 'BackupFormat',
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 #
739 diff --git a/qmp-commands.hx b/qmp-commands.hx
740 index 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 --
785 1.7.2.5
786