]> git.proxmox.com Git - pve-qemu-kvm.git/blob - debian/patches/0003-add-backup-related-monitor-commands.patch
79f5e3206f4043bf8d873089c7f859cdfe0e3708
[pve-qemu-kvm.git] / debian / patches / 0003-add-backup-related-monitor-commands.patch
1 From d97630338d62186229192d0724fae489fc500acc 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/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 | 13 ++
16 blockdev.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
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, 610 insertions(+), 0 deletions(-)
24
25 diff --git a/backup.h b/backup.h
26 index 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 */
47 diff --git a/blockdev.c b/blockdev.c
48 index e73fd6e..08ac7ad 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,380 @@ 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 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 +
209 +char *qmp_backup(const char *backupfile, bool has_format, BackupFormat format,
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 */
222 + format = has_format ? format : BACKUP_FORMAT_VMA;
223 +
224 + /* fixme: find driver for specifued format */
225 + const BackupDriver *driver = NULL;
226 +
227 + if (!driver) {
228 + error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
229 + return NULL;
230 + }
231 +
232 + if (has_devlist) {
233 + devs = g_strsplit_set(devlist, ",;:", -1);
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);
328 + if (driver->register_config_cb(writer, basename, cdata, clen) < 0) {
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;
440 diff --git a/hmp-commands.hx b/hmp-commands.hx
441 index 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
489 diff --git a/hmp.c b/hmp.c
490 index 180ba2b..77076fa 100644
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 +
557 + qmp_backup(backupfile, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
558 + devlist, qdict_haskey(qdict, "speed"), speed, &errp);
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;
570 diff --git a/hmp.h b/hmp.h
571 index 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);
591 diff --git a/monitor.c b/monitor.c
592 index 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 = "",
609 diff --git a/qapi-schema.json b/qapi-schema.json
610 index 5dfa052..2d3699b 100644
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.
622 +# This can be 'active', 'done', 'error'. If this field is not
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
653 @@ -1764,6 +1797,64 @@
654 'data': { 'path': 'str' },
655 'returns': [ 'ObjectPropertyInfo' ] }
656
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' ] }
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 +##
686 +{ 'command': 'backup', 'data': { 'backupfile': 'str', '*format': 'BackupFormat',
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 #
718 diff --git a/qmp-commands.hx b/qmp-commands.hx
719 index 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 --
764 1.7.2.5
765