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