block: add drive-backup QMP command

@drive-backup

Start a point-in-time copy of a block device to a new destination.  The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.

@device: the name of the device which should be copied.

@target: the target of the new image. If the file exists, or if it
         is a device, the existing file/device will be used as the new
         destination.  If it does not exist, a new file will be created.

@format: #optional the format of the new destination, default is to
         probe if @mode is 'existing', else the format of the source

@mode: #optional whether and how QEMU should create a new image, default is
       'absolute-paths'.

@speed: #optional the maximum speed, in bytes per second

@on-source-error: #optional the action to take on an error on the source,
                  default 'report'.  'stop' and 'enospc' can only be used
                  if the block device supports io-status (see BlockInfo).

@on-target-error: #optional the action to take on an error on the target,
                  default 'report' (no limitations, since this applies to
                  a different block device than @device).

Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.

Returns: nothing on success
         If @device is not a valid block device, DeviceNotFound

Since 1.6

Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2013-06-24 17:13:14 +02:00 committed by Kevin Wolf
parent ac3c5d831a
commit 99a9addf56
3 changed files with 189 additions and 0 deletions

View file

@ -1353,6 +1353,103 @@ void qmp_block_commit(const char *device,
drive_get_ref(drive_get_by_blockdev(bs));
}
void qmp_drive_backup(const char *device, const char *target,
bool has_format, const char *format,
bool has_mode, enum NewImageMode mode,
bool has_speed, int64_t speed,
bool has_on_source_error, BlockdevOnError on_source_error,
bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp)
{
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriver *drv = NULL;
Error *local_err = NULL;
int flags;
int64_t size;
int ret;
if (!has_speed) {
speed = 0;
}
if (!has_on_source_error) {
on_source_error = BLOCKDEV_ON_ERROR_REPORT;
}
if (!has_on_target_error) {
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
}
if (!has_mode) {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
}
if (!has_format) {
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
}
if (format) {
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
}
if (bdrv_in_use(bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
return;
}
flags = bs->open_flags | BDRV_O_RDWR;
size = bdrv_getlength(bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
return;
}
if (mode != NEW_IMAGE_MODE_EXISTING) {
assert(format && drv);
bdrv_img_create(target, format,
NULL, NULL, NULL, size, flags, &local_err, false);
}
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return;
}
target_bs = bdrv_new("");
ret = bdrv_open(target_bs, target, NULL, flags, drv);
if (ret < 0) {
bdrv_delete(target_bs);
error_setg_file_open(errp, -ret, target);
return;
}
backup_start(bs, target_bs, speed, on_source_error, on_target_error,
block_job_cb, bs, &local_err);
if (local_err != NULL) {
bdrv_delete(target_bs);
error_propagate(errp, local_err);
return;
}
/* Grab a reference so hotplug does not delete the BlockDriverState from
* underneath us.
*/
drive_get_ref(drive_get_by_blockdev(bs));
}
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
void qmp_drive_mirror(const char *device, const char *target,

View file

@ -1743,6 +1743,52 @@
'data': { 'device': 'str', '*base': 'str', 'top': 'str',
'*speed': 'int' } }
##
# @drive-backup
#
# Start a point-in-time copy of a block device to a new destination. The
# status of ongoing drive-backup operations can be checked with
# query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
# The operation can be stopped before it has completed using the
# block-job-cancel command.
#
# @device: the name of the device which should be copied.
#
# @target: the target of the new image. If the file exists, or if it
# is a device, the existing file/device will be used as the new
# destination. If it does not exist, a new file will be created.
#
# @format: #optional the format of the new destination, default is to
# probe if @mode is 'existing', else the format of the source
#
# @mode: #optional whether and how QEMU should create a new image, default is
# 'absolute-paths'.
#
# @speed: #optional the maximum speed, in bytes per second
#
# @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo).
#
# @on-target-error: #optional the action to take on an error on the target,
# default 'report' (no limitations, since this applies to
# a different block device than @device).
#
# Note that @on-source-error and @on-target-error only affect background I/O.
# If an error occurs during a guest write request, the device's rerror/werror
# actions will be used.
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
#
# Since 1.6
##
{ 'command': 'drive-backup',
'data': { 'device': 'str', 'target': 'str', '*format': 'str',
'*mode': 'NewImageMode', '*speed': 'int',
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } }
##
# @drive-mirror
#

View file

@ -911,6 +911,52 @@ EQMP
.mhandler.cmd_new = qmp_marshal_input_block_commit,
},
{
.name = "drive-backup",
.args_type = "device:B,target:s,speed:i?,mode:s?,format:s?,"
"on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_input_drive_backup,
},
SQMP
drive-backup
------------
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
Arguments:
- "device": the name of the device which should be copied.
(json-string)
- "target": the target of the new image. If the file exists, or if it is a
device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
(json-string)
- "format": the format of the new destination, default is to probe if 'mode' is
'existing', else the format of the source
(json-string, optional)
- "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional)
- "on-source-error": the action to take on an error on the source, default
'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status.
(BlockdevOnError, optional)
- "on-target-error": the action to take on an error on the target, default
'report' (no limitations, since this applies to
a different block device than device).
(BlockdevOnError, optional)
Example:
-> { "execute": "drive-backup", "arguments": { "device": "drive0",
"target": "backup.img" } }
<- { "return": {} }
EQMP
{
.name = "block-job-set-speed",
.args_type = "device:B,speed:o",