* [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" @ 2014-12-17 12:51 Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng ` (3 more replies) 0 siblings, 4 replies; 10+ messages in thread From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw) To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow v5: Address Max's and Markus' comments: Split patch 1. (Markus) Fix typos and pastos. (Markus, Max) Actually acquire aio context. (Max) Drop unnecessary initialization of fields in blockdev_backup_prepare. (Max) Add "sync" in the document example. Add Max's rev-by in patch 4. The existing drive-backup command accepts a target file path, but that interface provides little flexibility on the properties of target block device, compared to what is possible with "blockdev-add", "drive_add" or "-drive". This is also a building block to allow image fleecing (creating a point in time snapshot and export with nbd-server-add). (For symmetry, blockdev-mirror will be added in a separate series.) Fam Fam Zheng (4): qapi: Comment version info in TransactionAction qmp: Add command 'blockdev-backup' block: Add blockdev-backup to transaction qemu-iotests: Test blockdev-backup in 055 block/backup.c | 28 ++++++ blockdev.c | 133 ++++++++++++++++++++++++++++ qapi-schema.json | 8 ++ qapi/block-core.json | 54 ++++++++++++ qmp-commands.hx | 42 +++++++++ tests/qemu-iotests/055 | 211 +++++++++++++++++++++++++++++++++++++-------- tests/qemu-iotests/055.out | 4 +- 7 files changed, 441 insertions(+), 39 deletions(-) -- 1.9.3 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction 2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng @ 2014-12-17 12:51 ` Fam Zheng 2014-12-17 21:18 ` Eric Blake 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng ` (2 subsequent siblings) 3 siblings, 1 reply; 10+ messages in thread From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw) To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow Signed-off-by: Fam Zheng <famz@redhat.com> --- qapi-schema.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qapi-schema.json b/qapi-schema.json index 563b4ad..47d99cf 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1254,6 +1254,12 @@ # # A discriminated record of operations that can be performed with # @transaction. +# +# Since 1.1 +# +# drive-backup since 1.6 +# abort since 1.6 +# blockdev-snapshot-internal-sync since 1.7 ## { 'union': 'TransactionAction', 'data': { -- 1.9.3 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng @ 2014-12-17 21:18 ` Eric Blake 0 siblings, 0 replies; 10+ messages in thread From: Eric Blake @ 2014-12-17 21:18 UTC (permalink / raw) To: Fam Zheng, qemu-devel Cc: Kevin Wolf, jsnow, Markus Armbruster, Stefan Hajnoczi, mreitz [-- Attachment #1: Type: text/plain, Size: 776 bytes --] On 12/17/2014 05:51 AM, Fam Zheng wrote: > Signed-off-by: Fam Zheng <famz@redhat.com> > --- > qapi-schema.json | 6 ++++++ > 1 file changed, 6 insertions(+) Reviewed-by: Eric Blake <eblake@redhat.com> > > diff --git a/qapi-schema.json b/qapi-schema.json > index 563b4ad..47d99cf 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -1254,6 +1254,12 @@ > # > # A discriminated record of operations that can be performed with > # @transaction. > +# > +# Since 1.1 > +# > +# drive-backup since 1.6 > +# abort since 1.6 > +# blockdev-snapshot-internal-sync since 1.7 > ## > { 'union': 'TransactionAction', > 'data': { > -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 539 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' 2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng @ 2014-12-17 12:51 ` Fam Zheng 2014-12-17 20:53 ` John Snow 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 Fam Zheng 3 siblings, 1 reply; 10+ messages in thread From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw) To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow Similar to drive-backup, but this command uses a device id as target instead of creating/opening an image file. Also add blocker on target bs, since the target is also a named device now. Add check and report error for bs == target which became possible but is an illegal case with introduction of blockdev-backup. Signed-off-by: Fam Zheng <famz@redhat.com> --- block/backup.c | 28 +++++++++++++++++++++++++++ blockdev.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qmp-commands.hx | 42 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) diff --git a/block/backup.c b/block/backup.c index 792e655..1c535b1 100644 --- a/block/backup.c +++ b/block/backup.c @@ -360,6 +360,7 @@ static void coroutine_fn backup_run(void *opaque) hbitmap_free(job->bitmap); bdrv_iostatus_disable(target); + bdrv_op_unblock_all(target, job->common.blocker); data = g_malloc(sizeof(*data)); data->ret = ret; @@ -379,6 +380,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, assert(target); assert(cb); + if (bs == target) { + error_setg(errp, "Source and target cannot be the same"); + return; + } + if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { @@ -386,6 +392,26 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, return; } + if (!bdrv_is_inserted(bs)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(bs)); + return; + } + + if (!bdrv_is_inserted(target)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(target)); + return; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { + return; + } + + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { + return; + } + len = bdrv_getlength(bs); if (len < 0) { error_setg_errno(errp, -len, "unable to get length for '%s'", @@ -399,6 +425,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, return; } + bdrv_op_block_all(target, job->common.blocker); + job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->target = target; diff --git a/blockdev.c b/blockdev.c index 5651a8e..dbbab1d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2240,6 +2240,8 @@ void qmp_drive_backup(const char *device, const char *target, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); + /* Although backup_run has this check too, we need to use bs->drv below, so + * do an early check redundantly. */ if (!bdrv_is_inserted(bs)) { error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; @@ -2256,6 +2258,7 @@ void qmp_drive_backup(const char *device, const char *target, } } + /* Early check to avoid creating target */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { goto out; } @@ -2323,6 +2326,57 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(); } +void qmp_blockdev_backup(const char *device, const char *target, + enum MirrorSyncMode sync, + 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; + Error *local_err = NULL; + AioContext *aio_context; + + 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; + } + + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + target_bs = bdrv_find(target); + if (!target_bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, target); + goto out; + } + + bdrv_ref(target_bs); + bdrv_set_aio_context(target_bs, bdrv_get_aio_context(bs)); + backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, + block_job_cb, bs, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); + } +out: + aio_context_release(aio_context); +} + #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) void qmp_drive_mirror(const char *device, const char *target, diff --git a/qapi/block-core.json b/qapi/block-core.json index 6e8db15..d9f0598 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -703,6 +703,41 @@ '*on-target-error': 'BlockdevOnError' } } ## +# @BlockdevBackup +# +# @device: the name of the device which should be copied. +# +# @target: the name of the backup target device. +# +# @sync: what parts of the disk image should be copied to the destination +# (all the disk, only the sectors allocated in the topmost image, or +# only new I/O). +# +# @speed: #optional the maximum speed, in bytes per second. The default is 0, +# for unlimited. +# +# @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. +# +# Since: 2.3 +## +{ 'type': 'BlockdevBackup', + 'data': { 'device': 'str', 'target': 'str', + 'sync': 'MirrorSyncMode', + '*speed': 'int', + '*on-source-error': 'BlockdevOnError', + '*on-target-error': 'BlockdevOnError' } } + +## # @blockdev-snapshot-sync # # Generates a synchronous snapshot of a block device. @@ -822,6 +857,25 @@ { 'command': 'drive-backup', 'data': 'DriveBackup' } ## +# @blockdev-backup +# +# Start a point-in-time copy of a block device to a new destination. The +# status of ongoing blockdev-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. +# +# For the arguments, see the documentation of BlockdevBackup. +# +# Returns: Nothing on success. +# If @device or @target is not a valid block device, DeviceNotFound. +# +# Since 2.3 +## +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } + + +## # @query-named-block-nodes # # Get the named block driver list diff --git a/qmp-commands.hx b/qmp-commands.hx index 3348782..a7bb90b 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1094,6 +1094,48 @@ Example: "sync": "full", "target": "backup.img" } } <- { "return": {} } + +EQMP + + { + .name = "blockdev-backup", + .args_type = "sync:s,device:B,target:B,speed:i?," + "on-source-error:s?,on-target-error:s?", + .mhandler.cmd_new = qmp_marshal_input_blockdev_backup, + }, + +SQMP +blockdev-backup +------------ + +The device version of drive-backup: this command takes an existing named device +as backup target. + +Arguments: + +- "device": the name of the device which should be copied. + (json-string) +- "target": the name of the backup target device. (json-string) +- "sync": what parts of the disk image should be copied to the destination; + possibilities include "full" for all the disk, "top" for only the + sectors allocated in the topmost image, or "none" to only replicate + new I/O (MirrorSyncMode). +- "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": "blockdev-backup", "arguments": { "device": "src-id", + "sync": "full", + "target": "tgt-id" } } +<- { "return": {} } + EQMP { -- 1.9.3 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng @ 2014-12-17 20:53 ` John Snow 2014-12-18 10:22 ` Fam Zheng 0 siblings, 1 reply; 10+ messages in thread From: John Snow @ 2014-12-17 20:53 UTC (permalink / raw) To: Fam Zheng, qemu-devel Cc: Kevin Wolf, Markus Armbruster, Stefan Hajnoczi, mreitz On 12/17/2014 07:51 AM, Fam Zheng wrote: > Similar to drive-backup, but this command uses a device id as target > instead of creating/opening an image file. > > Also add blocker on target bs, since the target is also a named device > now. > > Add check and report error for bs == target which became possible but is > an illegal case with introduction of blockdev-backup. > > Signed-off-by: Fam Zheng <famz@redhat.com> > --- > block/backup.c | 28 +++++++++++++++++++++++++++ > blockdev.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > qapi/block-core.json | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > qmp-commands.hx | 42 ++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 178 insertions(+) > > diff --git a/block/backup.c b/block/backup.c > index 792e655..1c535b1 100644 > --- a/block/backup.c > +++ b/block/backup.c > @@ -360,6 +360,7 @@ static void coroutine_fn backup_run(void *opaque) > hbitmap_free(job->bitmap); > > bdrv_iostatus_disable(target); > + bdrv_op_unblock_all(target, job->common.blocker); > > data = g_malloc(sizeof(*data)); > data->ret = ret; > @@ -379,6 +380,11 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, > assert(target); > assert(cb); > > + if (bs == target) { > + error_setg(errp, "Source and target cannot be the same"); > + return; > + } > + > if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || > on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && > !bdrv_iostatus_is_enabled(bs)) { > @@ -386,6 +392,26 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, > return; > } > > + if (!bdrv_is_inserted(bs)) { > + error_setg(errp, "Device is not inserted: %s", > + bdrv_get_device_name(bs)); > + return; > + } > + > + if (!bdrv_is_inserted(target)) { > + error_setg(errp, "Device is not inserted: %s", > + bdrv_get_device_name(target)); > + return; > + } > + > + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { > + return; > + } > + > + if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { > + return; > + } > + > len = bdrv_getlength(bs); > if (len < 0) { > error_setg_errno(errp, -len, "unable to get length for '%s'", > @@ -399,6 +425,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target, > return; > } > > + bdrv_op_block_all(target, job->common.blocker); > + > job->on_source_error = on_source_error; > job->on_target_error = on_target_error; > job->target = target; > diff --git a/blockdev.c b/blockdev.c > index 5651a8e..dbbab1d 100644 > --- a/blockdev.c > +++ b/blockdev.c > @@ -2240,6 +2240,8 @@ void qmp_drive_backup(const char *device, const char *target, > aio_context = bdrv_get_aio_context(bs); > aio_context_acquire(aio_context); > > + /* Although backup_run has this check too, we need to use bs->drv below, so > + * do an early check redundantly. */ > if (!bdrv_is_inserted(bs)) { > error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); > goto out; > @@ -2256,6 +2258,7 @@ void qmp_drive_backup(const char *device, const char *target, > } > } > > + /* Early check to avoid creating target */ > if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { > goto out; > } > @@ -2323,6 +2326,57 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) > return bdrv_named_nodes_list(); > } > > +void qmp_blockdev_backup(const char *device, const char *target, > + enum MirrorSyncMode sync, > + 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; > + Error *local_err = NULL; > + AioContext *aio_context; > + > + 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; > + } > + > + bs = bdrv_find(device); > + if (!bs) { > + error_set(errp, QERR_DEVICE_NOT_FOUND, device); > + return; > + } > + > + aio_context = bdrv_get_aio_context(bs); > + aio_context_acquire(aio_context); > + > + target_bs = bdrv_find(target); > + if (!target_bs) { > + error_set(errp, QERR_DEVICE_NOT_FOUND, target); > + goto out; > + } > + > + bdrv_ref(target_bs); > + bdrv_set_aio_context(target_bs, bdrv_get_aio_context(bs)); why call bdrv_get_aio_context(bs) again instead of just using aio_context? Will this cause issues? > + backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, > + block_job_cb, bs, &local_err); > + if (local_err != NULL) { > + bdrv_unref(target_bs); > + error_propagate(errp, local_err); > + } > +out: > + aio_context_release(aio_context); > +} > + > #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) > > void qmp_drive_mirror(const char *device, const char *target, > diff --git a/qapi/block-core.json b/qapi/block-core.json > index 6e8db15..d9f0598 100644 > --- a/qapi/block-core.json > +++ b/qapi/block-core.json > @@ -703,6 +703,41 @@ > '*on-target-error': 'BlockdevOnError' } } > > ## > +# @BlockdevBackup > +# > +# @device: the name of the device which should be copied. > +# > +# @target: the name of the backup target device. > +# > +# @sync: what parts of the disk image should be copied to the destination > +# (all the disk, only the sectors allocated in the topmost image, or > +# only new I/O). > +# > +# @speed: #optional the maximum speed, in bytes per second. The default is 0, > +# for unlimited. > +# > +# @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. > +# > +# Since: 2.3 > +## > +{ 'type': 'BlockdevBackup', > + 'data': { 'device': 'str', 'target': 'str', > + 'sync': 'MirrorSyncMode', > + '*speed': 'int', > + '*on-source-error': 'BlockdevOnError', > + '*on-target-error': 'BlockdevOnError' } } > + > +## > # @blockdev-snapshot-sync > # > # Generates a synchronous snapshot of a block device. > @@ -822,6 +857,25 @@ > { 'command': 'drive-backup', 'data': 'DriveBackup' } > > ## > +# @blockdev-backup > +# > +# Start a point-in-time copy of a block device to a new destination. The > +# status of ongoing blockdev-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. > +# > +# For the arguments, see the documentation of BlockdevBackup. > +# > +# Returns: Nothing on success. > +# If @device or @target is not a valid block device, DeviceNotFound. > +# > +# Since 2.3 > +## > +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } > + > + > +## > # @query-named-block-nodes > # > # Get the named block driver list > diff --git a/qmp-commands.hx b/qmp-commands.hx > index 3348782..a7bb90b 100644 > --- a/qmp-commands.hx > +++ b/qmp-commands.hx > @@ -1094,6 +1094,48 @@ Example: > "sync": "full", > "target": "backup.img" } } > <- { "return": {} } > + > +EQMP > + > + { > + .name = "blockdev-backup", > + .args_type = "sync:s,device:B,target:B,speed:i?," > + "on-source-error:s?,on-target-error:s?", > + .mhandler.cmd_new = qmp_marshal_input_blockdev_backup, > + }, > + > +SQMP > +blockdev-backup > +------------ ^ Pad out the dashes to match the line length of the command. Mentioning only because Eric got me on that one last time :) > + > +The device version of drive-backup: this command takes an existing named device > +as backup target. > + > +Arguments: > + > +- "device": the name of the device which should be copied. > + (json-string) > +- "target": the name of the backup target device. (json-string) > +- "sync": what parts of the disk image should be copied to the destination; > + possibilities include "full" for all the disk, "top" for only the > + sectors allocated in the topmost image, or "none" to only replicate > + new I/O (MirrorSyncMode). > +- "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": "blockdev-backup", "arguments": { "device": "src-id", > + "sync": "full", > + "target": "tgt-id" } } > +<- { "return": {} } > + > EQMP > > { > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' 2014-12-17 20:53 ` John Snow @ 2014-12-18 10:22 ` Fam Zheng 0 siblings, 0 replies; 10+ messages in thread From: Fam Zheng @ 2014-12-18 10:22 UTC (permalink / raw) To: John Snow Cc: Kevin Wolf, mreitz, qemu-devel, Stefan Hajnoczi, Markus Armbruster On Wed, 12/17 15:53, John Snow wrote: > >+ aio_context = bdrv_get_aio_context(bs); > >+ aio_context_acquire(aio_context); > >+ > >+ target_bs = bdrv_find(target); > >+ if (!target_bs) { > >+ error_set(errp, QERR_DEVICE_NOT_FOUND, target); > >+ goto out; > >+ } > >+ > >+ bdrv_ref(target_bs); > >+ bdrv_set_aio_context(target_bs, bdrv_get_aio_context(bs)); > > why call bdrv_get_aio_context(bs) again instead of just using aio_context? > Will this cause issues? Could be simply aio_context. [...] > >diff --git a/qmp-commands.hx b/qmp-commands.hx > >index 3348782..a7bb90b 100644 > >--- a/qmp-commands.hx > >+++ b/qmp-commands.hx > >@@ -1094,6 +1094,48 @@ Example: > > "sync": "full", > > "target": "backup.img" } } > > <- { "return": {} } > >+ > >+EQMP > >+ > >+ { > >+ .name = "blockdev-backup", > >+ .args_type = "sync:s,device:B,target:B,speed:i?," > >+ "on-source-error:s?,on-target-error:s?", > >+ .mhandler.cmd_new = qmp_marshal_input_blockdev_backup, > >+ }, > >+ > >+SQMP > >+blockdev-backup > >+------------ > > ^ Pad out the dashes to match the line length of the command. Mentioning > only because Eric got me on that one last time :) OK, thanks. Fam ^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction 2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng @ 2014-12-17 12:51 ` Fam Zheng 2014-12-17 21:20 ` John Snow 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 Fam Zheng 3 siblings, 1 reply; 10+ messages in thread From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw) To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow Signed-off-by: Fam Zheng <famz@redhat.com> --- blockdev.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi-schema.json | 2 ++ 2 files changed, 81 insertions(+) diff --git a/blockdev.c b/blockdev.c index dbbab1d..9f9ae88 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common) } } +typedef struct BlockdevBackupState { + BlkTransactionState common; + BlockDriverState *bs; + BlockJob *job; + AioContext *aio_context; +} BlockdevBackupState; + +static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockdevBackup *backup; + BlockDriverState *bs, *target; + Error *local_err = NULL; + + assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); + backup = common->action->blockdev_backup; + + bs = bdrv_find(backup->device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); + return; + } + + target = bdrv_find(backup->target); + if (!target) { + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target); + return; + } + + /* AioContext is released in .clean() */ + state->aio_context = bdrv_get_aio_context(bs); + if (state->aio_context != bdrv_get_aio_context(target)) { + state->aio_context = NULL; + error_setg(errp, "Backup between two IO threads is not implemented"); + return; + } + aio_context_acquire(state->aio_context); + + qmp_blockdev_backup(backup->device, backup->target, + backup->sync, + backup->has_speed, backup->speed, + backup->has_on_source_error, backup->on_source_error, + backup->has_on_target_error, backup->on_target_error, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + state->bs = bdrv_find(backup->device); + state->job = state->bs->job; +} + +static void blockdev_backup_abort(BlkTransactionState *common) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + BlockDriverState *bs = state->bs; + + /* Only cancel if it's the job we started */ + if (bs && bs->job && bs->job == state->job) { + block_job_cancel_sync(bs->job); + } +} + +static void blockdev_backup_clean(BlkTransactionState *common) +{ + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); + + if (state->aio_context) { + aio_context_release(state->aio_context); + } +} + static void abort_prepare(BlkTransactionState *common, Error **errp) { error_setg(errp, "Transaction aborted using Abort action"); @@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = { .abort = drive_backup_abort, .clean = drive_backup_clean, }, + [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { + .instance_size = sizeof(BlockdevBackupState), + .prepare = blockdev_backup_prepare, + .abort = blockdev_backup_abort, + .clean = blockdev_backup_clean, + }, [TRANSACTION_ACTION_KIND_ABORT] = { .instance_size = sizeof(BlkTransactionState), .prepare = abort_prepare, diff --git a/qapi-schema.json b/qapi-schema.json index 47d99cf..fbfc52f 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1260,11 +1260,13 @@ # drive-backup since 1.6 # abort since 1.6 # blockdev-snapshot-internal-sync since 1.7 +# blockdev-backup since 2.3 ## { 'union': 'TransactionAction', 'data': { 'blockdev-snapshot-sync': 'BlockdevSnapshot', 'drive-backup': 'DriveBackup', + 'blockdev-backup': 'BlockdevBackup', 'abort': 'Abort', 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal' } } -- 1.9.3 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng @ 2014-12-17 21:20 ` John Snow 2014-12-18 10:31 ` Fam Zheng 0 siblings, 1 reply; 10+ messages in thread From: John Snow @ 2014-12-17 21:20 UTC (permalink / raw) To: Fam Zheng Cc: kwolf >> Kevin Wolf, mrei >> Max Reitz, qemu-devel, stefan Hajnoczi, arm >> Markus Armbruster On 12/17/2014 07:51 AM, Fam Zheng wrote: > Signed-off-by: Fam Zheng <famz@redhat.com> > --- > blockdev.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > qapi-schema.json | 2 ++ > 2 files changed, 81 insertions(+) > > diff --git a/blockdev.c b/blockdev.c > index dbbab1d..9f9ae88 100644 > --- a/blockdev.c > +++ b/blockdev.c > @@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common) > } > } > > +typedef struct BlockdevBackupState { > + BlkTransactionState common; > + BlockDriverState *bs; > + BlockJob *job; > + AioContext *aio_context; > +} BlockdevBackupState; > + > +static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) > +{ > + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); > + BlockdevBackup *backup; > + BlockDriverState *bs, *target; > + Error *local_err = NULL; > + > + assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); > + backup = common->action->blockdev_backup; > + > + bs = bdrv_find(backup->device); > + if (!bs) { > + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); > + return; > + } > + > + target = bdrv_find(backup->target); > + if (!target) { > + error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target); > + return; > + } > + > + /* AioContext is released in .clean() */ > + state->aio_context = bdrv_get_aio_context(bs); > + if (state->aio_context != bdrv_get_aio_context(target)) { > + state->aio_context = NULL; > + error_setg(errp, "Backup between two IO threads is not implemented"); > + return; > + } > + aio_context_acquire(state->aio_context); > + > + qmp_blockdev_backup(backup->device, backup->target, > + backup->sync, > + backup->has_speed, backup->speed, > + backup->has_on_source_error, backup->on_source_error, > + backup->has_on_target_error, backup->on_target_error, > + &local_err); > + if (local_err) { > + error_propagate(errp, local_err); > + return; > + } > + > + state->bs = bdrv_find(backup->device); Just some questions: why not use 'bs' instead of calling the function again? Granted, it should work a second time if it worked once. > + state->job = state->bs->job; Why stash both bs and bs->job? Can the BDS change what its job member points to between .prepare() and .abort() ? > +} > + > +static void blockdev_backup_abort(BlkTransactionState *common) > +{ > + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); > + BlockDriverState *bs = state->bs; > + > + /* Only cancel if it's the job we started */ > + if (bs && bs->job && bs->job == state->job) { > + block_job_cancel_sync(bs->job); > + } > +} > + > +static void blockdev_backup_clean(BlkTransactionState *common) > +{ > + BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); > + > + if (state->aio_context) { > + aio_context_release(state->aio_context); > + } > +} > + > static void abort_prepare(BlkTransactionState *common, Error **errp) > { > error_setg(errp, "Transaction aborted using Abort action"); > @@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = { > .abort = drive_backup_abort, > .clean = drive_backup_clean, > }, > + [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { > + .instance_size = sizeof(BlockdevBackupState), > + .prepare = blockdev_backup_prepare, > + .abort = blockdev_backup_abort, > + .clean = blockdev_backup_clean, > + }, > [TRANSACTION_ACTION_KIND_ABORT] = { > .instance_size = sizeof(BlkTransactionState), > .prepare = abort_prepare, > diff --git a/qapi-schema.json b/qapi-schema.json > index 47d99cf..fbfc52f 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -1260,11 +1260,13 @@ > # drive-backup since 1.6 > # abort since 1.6 > # blockdev-snapshot-internal-sync since 1.7 > +# blockdev-backup since 2.3 > ## > { 'union': 'TransactionAction', > 'data': { > 'blockdev-snapshot-sync': 'BlockdevSnapshot', > 'drive-backup': 'DriveBackup', > + 'blockdev-backup': 'BlockdevBackup', > 'abort': 'Abort', > 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal' > } } > At any rate, regardless of the answers to my questions: Reviewed-by: John Snow <jsnow@redhat.com> ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction 2014-12-17 21:20 ` John Snow @ 2014-12-18 10:31 ` Fam Zheng 0 siblings, 0 replies; 10+ messages in thread From: Fam Zheng @ 2014-12-18 10:31 UTC (permalink / raw) To: John Snow Cc: Kevin Wolf, Markus Armbruster, qemu-devel, stefan Hajnoczi, Max Reitz On Wed, 12/17 16:20, John Snow wrote: > On 12/17/2014 07:51 AM, Fam Zheng wrote: > >Signed-off-by: Fam Zheng <famz@redhat.com> > >--- > > blockdev.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > qapi-schema.json | 2 ++ > > 2 files changed, 81 insertions(+) > > > >diff --git a/blockdev.c b/blockdev.c > >index dbbab1d..9f9ae88 100644 > >--- a/blockdev.c > >+++ b/blockdev.c > >@@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common) > > } > > } > > > >+typedef struct BlockdevBackupState { > >+ BlkTransactionState common; > >+ BlockDriverState *bs; > >+ BlockJob *job; > >+ AioContext *aio_context; > >+} BlockdevBackupState; > >+ > >+static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) > >+{ > >+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); > >+ BlockdevBackup *backup; > >+ BlockDriverState *bs, *target; > >+ Error *local_err = NULL; > >+ > >+ assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); > >+ backup = common->action->blockdev_backup; > >+ > >+ bs = bdrv_find(backup->device); > >+ if (!bs) { > >+ error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); > >+ return; > >+ } > >+ > >+ target = bdrv_find(backup->target); > >+ if (!target) { > >+ error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target); > >+ return; > >+ } > >+ > >+ /* AioContext is released in .clean() */ > >+ state->aio_context = bdrv_get_aio_context(bs); > >+ if (state->aio_context != bdrv_get_aio_context(target)) { > >+ state->aio_context = NULL; > >+ error_setg(errp, "Backup between two IO threads is not implemented"); > >+ return; > >+ } > >+ aio_context_acquire(state->aio_context); > >+ > >+ qmp_blockdev_backup(backup->device, backup->target, > >+ backup->sync, > >+ backup->has_speed, backup->speed, > >+ backup->has_on_source_error, backup->on_source_error, > >+ backup->has_on_target_error, backup->on_target_error, > >+ &local_err); > >+ if (local_err) { > >+ error_propagate(errp, local_err); > >+ return; > >+ } > >+ > >+ state->bs = bdrv_find(backup->device); > > Just some questions: > > why not use 'bs' instead of calling the function again? Granted, it should > work a second time if it worked once. Yeah, it should be "bs", a mistake but not wrong. > > >+ state->job = state->bs->job; > > Why stash both bs and bs->job? Can the BDS change what its job member points > to between .prepare() and .abort() ? Copied from drive_backup_prepare. Actually there is a comment about this in blockdev_backup_abort below: > > >+} > >+ > >+static void blockdev_backup_abort(BlkTransactionState *common) > >+{ > >+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); > >+ BlockDriverState *bs = state->bs; > >+ > >+ /* Only cancel if it's the job we started */ > >+ if (bs && bs->job && bs->job == state->job) { I think there is one case this is useful: a transaction containing two block jobs, which *will* fail and abort(). With this check, only the right (started) block_job_cancel_sync is called. > >+ block_job_cancel_sync(bs->job); > >+ } > >+} > >+ > >+static void blockdev_backup_clean(BlkTransactionState *common) > >+{ > >+ BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); > >+ > >+ if (state->aio_context) { > >+ aio_context_release(state->aio_context); > >+ } > >+} > >+ > > static void abort_prepare(BlkTransactionState *common, Error **errp) > > { > > error_setg(errp, "Transaction aborted using Abort action"); > >@@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = { > > .abort = drive_backup_abort, > > .clean = drive_backup_clean, > > }, > >+ [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { > >+ .instance_size = sizeof(BlockdevBackupState), > >+ .prepare = blockdev_backup_prepare, > >+ .abort = blockdev_backup_abort, > >+ .clean = blockdev_backup_clean, > >+ }, > > [TRANSACTION_ACTION_KIND_ABORT] = { > > .instance_size = sizeof(BlkTransactionState), > > .prepare = abort_prepare, > >diff --git a/qapi-schema.json b/qapi-schema.json > >index 47d99cf..fbfc52f 100644 > >--- a/qapi-schema.json > >+++ b/qapi-schema.json > >@@ -1260,11 +1260,13 @@ > > # drive-backup since 1.6 > > # abort since 1.6 > > # blockdev-snapshot-internal-sync since 1.7 > >+# blockdev-backup since 2.3 > > ## > > { 'union': 'TransactionAction', > > 'data': { > > 'blockdev-snapshot-sync': 'BlockdevSnapshot', > > 'drive-backup': 'DriveBackup', > >+ 'blockdev-backup': 'BlockdevBackup', > > 'abort': 'Abort', > > 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal' > > } } > > > > At any rate, regardless of the answers to my questions: > Reviewed-by: John Snow <jsnow@redhat.com> > Thanks! I'm going to fix the above bdrv_find() while keeping your rev-by. I hope that is OK for you. :) Fam ^ permalink raw reply [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng ` (2 preceding siblings ...) 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng @ 2014-12-17 12:51 ` Fam Zheng 3 siblings, 0 replies; 10+ messages in thread From: Fam Zheng @ 2014-12-17 12:51 UTC (permalink / raw) To: qemu-devel; +Cc: Kevin Wolf, Markus Armbruster, mreitz, Stefan Hajnoczi, jsnow This applies cases on drive-backup on blockdev-backup, except cases with target format and mode. Also add a case to check source == target. Signed-off-by: Fam Zheng <famz@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> --- tests/qemu-iotests/055 | 211 +++++++++++++++++++++++++++++++++++++-------- tests/qemu-iotests/055.out | 4 +- 2 files changed, 176 insertions(+), 39 deletions(-) diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055 index 0872444..e81d4d0 100755 --- a/tests/qemu-iotests/055 +++ b/tests/qemu-iotests/055 @@ -1,8 +1,8 @@ #!/usr/bin/env python # -# Tests for drive-backup +# Tests for drive-backup and blockdev-backup # -# Copyright (C) 2013 Red Hat, Inc. +# Copyright (C) 2013, 2014 Red Hat, Inc. # # Based on 041. # @@ -27,6 +27,7 @@ from iotests import qemu_img, qemu_io test_img = os.path.join(iotests.test_dir, 'test.img') target_img = os.path.join(iotests.test_dir, 'target.img') +blockdev_target_img = os.path.join(iotests.test_dir, 'blockdev-target.img') class TestSingleDrive(iotests.QMPTestCase): image_len = 64 * 1024 * 1024 # MB @@ -38,34 +39,41 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) + qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) - self.vm = iotests.VM().add_drive(test_img) + self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) self.vm.launch() def tearDown(self): self.vm.shutdown() os.remove(test_img) + os.remove(blockdev_target_img) try: os.remove(target_img) except OSError: pass - def test_cancel(self): + def do_test_cancel(self, cmd, target): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') self.assert_qmp(result, 'return', {}) event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') - def test_pause(self): + def test_cancel_drive_backup(self): + self.do_test_cancel('drive-backup', target_img) + + def test_cancel_blockdev_backup(self): + self.do_test_cancel('blockdev-backup', 'drive1') + + def do_test_pause(self, cmd, target, image): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full') self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-job-pause', device='drive0') @@ -86,14 +94,25 @@ class TestSingleDrive(iotests.QMPTestCase): self.wait_until_completed() self.vm.shutdown() - self.assertTrue(iotests.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, image), 'target image does not match source after backup') + def test_pause_drive_backup(self): + self.do_test_pause('drive-backup', target_img, target_img) + + def test_pause_blockdev_backup(self): + self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) + def test_medium_not_found(self): result = self.vm.qmp('drive-backup', device='ide1-cd0', target=target_img, sync='full') self.assert_qmp(result, 'error/class', 'GenericError') + def test_medium_not_found_blockdev_backup(self): + result = self.vm.qmp('blockdev-backup', device='ide1-cd0', + target='drive1', sync='full') + self.assert_qmp(result, 'error/class', 'GenericError') + def test_image_not_found(self): result = self.vm.qmp('drive-backup', device='drive0', target=target_img, sync='full', mode='existing') @@ -105,31 +124,53 @@ class TestSingleDrive(iotests.QMPTestCase): format='spaghetti-noodles') self.assert_qmp(result, 'error/class', 'GenericError') - def test_device_not_found(self): - result = self.vm.qmp('drive-backup', device='nonexistent', - target=target_img, sync='full') + def do_test_device_not_found(self, cmd, **args): + result = self.vm.qmp(cmd, **args) self.assert_qmp(result, 'error/class', 'DeviceNotFound') + def test_device_not_found(self): + self.do_test_device_not_found('drive-backup', device='nonexistent', + target=target_img, sync='full') + + self.do_test_device_not_found('blockdev-backup', device='nonexistent', + target='drive0', sync='full') + + self.do_test_device_not_found('blockdev-backup', device='drive0', + target='nonexistent', sync='full') + + self.do_test_device_not_found('blockdev-backup', device='nonexistent', + target='nonexistent', sync='full') + + def test_target_is_source(self): + result = self.vm.qmp('blockdev-backup', device='drive0', + target='drive0', sync='full') + self.assert_qmp(result, 'error/class', 'GenericError') + class TestSetSpeed(iotests.QMPTestCase): image_len = 80 * 1024 * 1024 # MB def setUp(self): qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len)) qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img) - self.vm = iotests.VM().add_drive(test_img) + qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) + + self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) self.vm.launch() def tearDown(self): self.vm.shutdown() os.remove(test_img) - os.remove(target_img) + os.remove(blockdev_target_img) + try: + os.remove(target_img) + except OSError: + pass - def test_set_speed(self): + def do_test_set_speed(self, cmd, target): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', target=target, sync='full') self.assert_qmp(result, 'return', {}) # Default speed is 0 @@ -148,10 +189,10 @@ class TestSetSpeed(iotests.QMPTestCase): event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') - # Check setting speed in drive-backup works + # Check setting speed option works self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full', speed=4*1024*1024) + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full', speed=4*1024*1024) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block-jobs') @@ -161,18 +202,24 @@ class TestSetSpeed(iotests.QMPTestCase): event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') - def test_set_speed_invalid(self): + def test_set_speed_drive_backup(self): + self.do_test_set_speed('drive-backup', target_img) + + def test_set_speed_blockdev_backup(self): + self.do_test_set_speed('blockdev-backup', 'drive1') + + def do_test_set_speed_invalid(self, cmd, target): self.assert_no_active_block_jobs() - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full', speed=-1) + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full', speed=-1) self.assert_qmp(result, 'error/class', 'GenericError') self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') - result = self.vm.qmp('drive-backup', device='drive0', - target=target_img, sync='full') + result = self.vm.qmp(cmd, device='drive0', + target=target, sync='full') self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) @@ -181,6 +228,12 @@ class TestSetSpeed(iotests.QMPTestCase): event = self.cancel_and_wait(resume=True) self.assert_qmp(event, 'data/type', 'backup') + def test_set_speed_invalid_drive_backup(self): + self.do_test_set_speed_invalid('drive-backup', target_img) + + def test_set_speed_invalid_blockdev_backup(self): + self.do_test_set_speed_invalid('blockdev-backup', 'drive1') + class TestSingleTransaction(iotests.QMPTestCase): image_len = 64 * 1024 * 1024 # MB @@ -190,41 +243,50 @@ class TestSingleTransaction(iotests.QMPTestCase): qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img) qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img) qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img) + qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len)) - self.vm = iotests.VM().add_drive(test_img) + self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img) self.vm.launch() def tearDown(self): self.vm.shutdown() os.remove(test_img) + os.remove(blockdev_target_img) try: os.remove(target_img) except OSError: pass - def test_cancel(self): + def do_test_cancel(self, cmd, target): self.assert_no_active_block_jobs() result = self.vm.qmp('transaction', actions=[{ - 'type': 'drive-backup', + 'type': cmd, 'data': { 'device': 'drive0', - 'target': target_img, + 'target': target, 'sync': 'full' }, } ]) + self.assert_qmp(result, 'return', {}) event = self.cancel_and_wait() self.assert_qmp(event, 'data/type', 'backup') - def test_pause(self): + def test_cancel_drive_backup(self): + self.do_test_cancel('drive-backup', target_img) + + def test_cancel_blockdev_backup(self): + self.do_test_cancel('blockdev-backup', 'drive1') + + def do_test_pause(self, cmd, target, image): self.assert_no_active_block_jobs() self.vm.pause_drive('drive0') result = self.vm.qmp('transaction', actions=[{ - 'type': 'drive-backup', + 'type': cmd, 'data': { 'device': 'drive0', - 'target': target_img, + 'target': target, 'sync': 'full' }, } ]) @@ -248,19 +310,31 @@ class TestSingleTransaction(iotests.QMPTestCase): self.wait_until_completed() self.vm.shutdown() - self.assertTrue(iotests.compare_images(test_img, target_img), + self.assertTrue(iotests.compare_images(test_img, image), 'target image does not match source after backup') - def test_medium_not_found(self): + def test_pause_drive_backup(self): + self.do_test_pause('drive-backup', target_img, target_img) + + def test_pause_blockdev_backup(self): + self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img) + + def do_test_medium_not_found(self, cmd, target): result = self.vm.qmp('transaction', actions=[{ - 'type': 'drive-backup', + 'type': cmd, 'data': { 'device': 'ide1-cd0', - 'target': target_img, + 'target': target, 'sync': 'full' }, } ]) self.assert_qmp(result, 'error/class', 'GenericError') + def test_medium_not_found_drive_backup(self): + self.do_test_medium_not_found('drive-backup', target_img) + + def test_medium_not_found_blockdev_backup(self): + self.do_test_medium_not_found('blockdev-backup', 'drive1') + def test_image_not_found(self): result = self.vm.qmp('transaction', actions=[{ 'type': 'drive-backup', @@ -283,6 +357,43 @@ class TestSingleTransaction(iotests.QMPTestCase): ]) self.assert_qmp(result, 'error/class', 'DeviceNotFound') + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'nonexistent', + 'target': 'drive1', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'nonexistent', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'nonexistent', + 'target': 'nonexistent', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'DeviceNotFound') + + def test_target_is_source(self): + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'drive0', + 'sync': 'full' }, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + def test_abort(self): result = self.vm.qmp('transaction', actions=[{ 'type': 'drive-backup', @@ -298,5 +409,31 @@ class TestSingleTransaction(iotests.QMPTestCase): self.assert_qmp(result, 'error/class', 'GenericError') self.assert_no_active_block_jobs() + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'nonexistent', + 'target': 'drive1', + 'sync': 'full' }, + }, { + 'type': 'Abort', + 'data': {}, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_no_active_block_jobs() + + result = self.vm.qmp('transaction', actions=[{ + 'type': 'blockdev-backup', + 'data': { 'device': 'drive0', + 'target': 'nonexistent', + 'sync': 'full' }, + }, { + 'type': 'Abort', + 'data': {}, + } + ]) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_no_active_block_jobs() + if __name__ == '__main__': iotests.main(supported_fmts=['raw', 'qcow2']) diff --git a/tests/qemu-iotests/055.out b/tests/qemu-iotests/055.out index 6323079..42314e9 100644 --- a/tests/qemu-iotests/055.out +++ b/tests/qemu-iotests/055.out @@ -1,5 +1,5 @@ -.............. +........................ ---------------------------------------------------------------------- -Ran 14 tests +Ran 24 tests OK -- 1.9.3 ^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2014-12-18 10:30 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-12-17 12:51 [Qemu-devel] [PATCH v5 0/4] qmp: Add "blockdev-backup" Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 1/4] qapi: Comment version info in TransactionAction Fam Zheng 2014-12-17 21:18 ` Eric Blake 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 2/4] qmp: Add command 'blockdev-backup' Fam Zheng 2014-12-17 20:53 ` John Snow 2014-12-18 10:22 ` Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 3/4] block: Add blockdev-backup to transaction Fam Zheng 2014-12-17 21:20 ` John Snow 2014-12-18 10:31 ` Fam Zheng 2014-12-17 12:51 ` [Qemu-devel] [PATCH v5 4/4] qemu-iotests: Test blockdev-backup in 055 Fam Zheng
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).