[pve-devel] [PATCH v2 qemu 1/1] add bitmap drive-mirror patches
Fabian Grünbichler
f.gruenbichler at proxmox.com
Tue Mar 17 08:55:24 CET 2020
Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
I opted to send them as cherry-picks of the series I submitted for
upstream inclusion, to make matching with future versions and/or
dropping when it gets applied easier.
it's of course also possible to squash them into a single patch, drop
the test-case one, etc.
...d-support-for-sync-bitmap-mode-never.patch | 443 +++
...-support-for-conditional-and-always-.patch | 83 +
...check-for-bitmap-mode-without-bitmap.patch | 33 +
...-to-bdrv_dirty_bitmap_merge_internal.patch | 45 +
...8-iotests-add-test-for-bitmap-mirror.patch | 3447 +++++++++++++++++
.../0039-mirror-move-some-checks-to-qmp.patch | 275 ++
debian/patches/series | 6 +
7 files changed, 4332 insertions(+)
create mode 100644 debian/patches/pve/0034-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
create mode 100644 debian/patches/pve/0035-drive-mirror-add-support-for-conditional-and-always-.patch
create mode 100644 debian/patches/pve/0036-mirror-add-check-for-bitmap-mode-without-bitmap.patch
create mode 100644 debian/patches/pve/0037-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
create mode 100644 debian/patches/pve/0038-iotests-add-test-for-bitmap-mirror.patch
create mode 100644 debian/patches/pve/0039-mirror-move-some-checks-to-qmp.patch
diff --git a/debian/patches/pve/0034-drive-mirror-add-support-for-sync-bitmap-mode-never.patch b/debian/patches/pve/0034-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
new file mode 100644
index 0000000..50229d0
--- /dev/null
+++ b/debian/patches/pve/0034-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
@@ -0,0 +1,443 @@
+From 1bfdb0492d8b70983342cf96576b294d60172e05 Mon Sep 17 00:00:00 2001
+From: John Snow <jsnow at redhat.com>
+Date: Tue, 9 Jul 2019 21:12:21 -0400
+Subject: [PATCH qemu 34/39] drive-mirror: add support for sync=bitmap
+ mode=never
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds support for the "BITMAP" sync mode to drive-mirror and
+blockdev-mirror. It adds support only for the BitmapSyncMode "never,"
+because it's the simplest mode.
+
+This mode simply uses a user-provided bitmap as an initial copy
+manifest, and then does not clear any bits in the bitmap at the
+conclusion of the operation.
+
+Any new writes dirtied during the operation are copied out, in contrast
+to backup. Note that whether these writes are reflected in the bitmap
+at the conclusion of the operation depends on whether that bitmap is
+actually recording!
+
+This patch was originally based on one by Ma Haocong, but it has since
+been modified pretty heavily.
+
+Suggested-by: Ma Haocong <mahaocong at didichuxing.com>
+Signed-off-by: Ma Haocong <mahaocong at didichuxing.com>
+Signed-off-by: John Snow <jsnow at redhat.com>
+Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
+---
+ include/block/block_int.h | 4 +-
+ block/mirror.c | 98 ++++++++++++++++++++++++++++++-------
+ blockdev.c | 39 +++++++++++++--
+ tests/test-block-iothread.c | 4 +-
+ qapi/block-core.json | 29 +++++++++--
+ 5 files changed, 145 insertions(+), 29 deletions(-)
+
+diff --git a/include/block/block_int.h b/include/block/block_int.h
+index 43b00c15ac..bd2e11c4d9 100644
+--- a/include/block/block_int.h
++++ b/include/block/block_int.h
+@@ -1206,7 +1206,9 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *target, const char *replaces,
+ int creation_flags, int64_t speed,
+ uint32_t granularity, int64_t buf_size,
+- MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
++ MirrorSyncMode mode, BdrvDirtyBitmap *bitmap,
++ BitmapSyncMode bitmap_mode,
++ BlockMirrorBackingMode backing_mode,
+ bool zero_target,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+diff --git a/block/mirror.c b/block/mirror.c
+index f0f2d9dff1..fd7f574365 100644
+--- a/block/mirror.c
++++ b/block/mirror.c
+@@ -49,7 +49,7 @@ typedef struct MirrorBlockJob {
+ BlockDriverState *to_replace;
+ /* Used to block operations on the drive-mirror-replace target */
+ Error *replace_blocker;
+- bool is_none_mode;
++ MirrorSyncMode sync_mode;
+ BlockMirrorBackingMode backing_mode;
+ /* Whether the target image requires explicit zero-initialization */
+ bool zero_target;
+@@ -64,6 +64,8 @@ typedef struct MirrorBlockJob {
+ size_t buf_size;
+ int64_t bdev_length;
+ unsigned long *cow_bitmap;
++ BdrvDirtyBitmap *sync_bitmap;
++ BitmapSyncMode bitmap_mode;
+ BdrvDirtyBitmap *dirty_bitmap;
+ BdrvDirtyBitmapIter *dbi;
+ uint8_t *buf;
+@@ -668,7 +670,8 @@ static int mirror_exit_common(Job *job)
+ bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing,
+ &error_abort);
+ if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
+- BlockDriverState *backing = s->is_none_mode ? src : s->base;
++ BlockDriverState *backing;
++ backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base;
+ if (backing_bs(target_bs) != backing) {
+ bdrv_set_backing_hd(target_bs, backing, &local_err);
+ if (local_err) {
+@@ -750,6 +753,16 @@ static void mirror_abort(Job *job)
+ assert(ret == 0);
+ }
+
++/* Always called after commit/abort. */
++static void mirror_clean(Job *job)
++{
++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
++
++ if (s->sync_bitmap) {
++ bdrv_dirty_bitmap_set_busy(s->sync_bitmap, false);
++ }
++}
++
+ static void coroutine_fn mirror_throttle(MirrorBlockJob *s)
+ {
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+@@ -928,7 +941,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
+ mirror_free_init(s);
+
+ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+- if (!s->is_none_mode) {
++ if ((s->sync_mode == MIRROR_SYNC_MODE_TOP) ||
++ (s->sync_mode == MIRROR_SYNC_MODE_FULL)) {
+ ret = mirror_dirty_init(s);
+ if (ret < 0 || job_is_cancelled(&s->common.job)) {
+ goto immediate_exit;
+@@ -1160,6 +1174,7 @@ static const BlockJobDriver mirror_job_driver = {
+ .run = mirror_run,
+ .prepare = mirror_prepare,
+ .abort = mirror_abort,
++ .clean = mirror_clean,
+ .pause = mirror_pause,
+ .complete = mirror_complete,
+ },
+@@ -1175,6 +1190,7 @@ static const BlockJobDriver commit_active_job_driver = {
+ .run = mirror_run,
+ .prepare = mirror_prepare,
+ .abort = mirror_abort,
++ .clean = mirror_clean,
+ .pause = mirror_pause,
+ .complete = mirror_complete,
+ },
+@@ -1520,7 +1536,10 @@ static BlockJob *mirror_start_job(
+ BlockCompletionFunc *cb,
+ void *opaque,
+ const BlockJobDriver *driver,
+- bool is_none_mode, BlockDriverState *base,
++ MirrorSyncMode sync_mode,
++ BdrvDirtyBitmap *bitmap,
++ BitmapSyncMode bitmap_mode,
++ BlockDriverState *base,
+ bool auto_complete, const char *filter_node_name,
+ bool is_mirror, MirrorCopyMode copy_mode,
+ Error **errp)
+@@ -1533,10 +1552,39 @@ static BlockJob *mirror_start_job(
+ Error *local_err = NULL;
+ int ret;
+
+- if (granularity == 0) {
+- granularity = bdrv_get_default_bitmap_granularity(target);
++ if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
++ error_setg(errp, "Sync mode '%s' not supported",
++ MirrorSyncMode_str(sync_mode));
++ return NULL;
++ } else if (sync_mode == MIRROR_SYNC_MODE_BITMAP) {
++ if (!bitmap) {
++ error_setg(errp, "Must provide a valid bitmap name for '%s'"
++ " sync mode",
++ MirrorSyncMode_str(sync_mode));
++ return NULL;
++ } else if (bitmap_mode != BITMAP_SYNC_MODE_NEVER) {
++ error_setg(errp,
++ "Bitmap Sync Mode '%s' is not supported by Mirror",
++ BitmapSyncMode_str(bitmap_mode));
++ }
++ } else if (bitmap) {
++ error_setg(errp,
++ "sync mode '%s' is not compatible with bitmaps",
++ MirrorSyncMode_str(sync_mode));
++ return NULL;
+ }
+
++ if (bitmap) {
++ if (granularity) {
++ error_setg(errp, "granularity (%d)"
++ "cannot be specified when a bitmap is provided",
++ granularity);
++ return NULL;
++ }
++ granularity = bdrv_dirty_bitmap_granularity(bitmap);
++ } else if (granularity == 0) {
++ granularity = bdrv_get_default_bitmap_granularity(target);
++ }
+ assert(is_power_of_2(granularity));
+
+ if (buf_size < 0) {
+@@ -1640,7 +1688,9 @@ static BlockJob *mirror_start_job(
+ s->replaces = g_strdup(replaces);
+ s->on_source_error = on_source_error;
+ s->on_target_error = on_target_error;
+- s->is_none_mode = is_none_mode;
++ s->sync_mode = sync_mode;
++ s->sync_bitmap = bitmap;
++ s->bitmap_mode = bitmap_mode;
+ s->backing_mode = backing_mode;
+ s->zero_target = zero_target;
+ s->copy_mode = copy_mode;
+@@ -1660,6 +1710,18 @@ static BlockJob *mirror_start_job(
+ bdrv_disable_dirty_bitmap(s->dirty_bitmap);
+ }
+
++ if (s->sync_bitmap) {
++ bdrv_dirty_bitmap_set_busy(s->sync_bitmap, true);
++ }
++
++ if (s->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
++ bdrv_merge_dirty_bitmap(s->dirty_bitmap, s->sync_bitmap,
++ NULL, &local_err);
++ if (local_err) {
++ goto fail;
++ }
++ }
++
+ ret = block_job_add_bdrv(&s->common, "source", bs, 0,
+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE |
+ BLK_PERM_CONSISTENT_READ,
+@@ -1713,6 +1775,9 @@ fail:
+ if (s->dirty_bitmap) {
+ bdrv_release_dirty_bitmap(s->dirty_bitmap);
+ }
++ if (s->sync_bitmap) {
++ bdrv_dirty_bitmap_set_busy(s->sync_bitmap, false);
++ }
+ job_early_fail(&s->common.job);
+ }
+
+@@ -1730,29 +1795,23 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *target, const char *replaces,
+ int creation_flags, int64_t speed,
+ uint32_t granularity, int64_t buf_size,
+- MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
++ MirrorSyncMode mode, BdrvDirtyBitmap *bitmap,
++ BitmapSyncMode bitmap_mode,
++ BlockMirrorBackingMode backing_mode,
+ bool zero_target,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+ bool unmap, const char *filter_node_name,
+ MirrorCopyMode copy_mode, Error **errp)
+ {
+- bool is_none_mode;
+ BlockDriverState *base;
+
+- if ((mode == MIRROR_SYNC_MODE_INCREMENTAL) ||
+- (mode == MIRROR_SYNC_MODE_BITMAP)) {
+- error_setg(errp, "Sync mode '%s' not supported",
+- MirrorSyncMode_str(mode));
+- return;
+- }
+- is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
+ base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
+ mirror_start_job(job_id, bs, creation_flags, target, replaces,
+ speed, granularity, buf_size, backing_mode, zero_target,
+ on_source_error, on_target_error, unmap, NULL, NULL,
+- &mirror_job_driver, is_none_mode, base, false,
+- filter_node_name, true, copy_mode, errp);
++ &mirror_job_driver, mode, bitmap, bitmap_mode, base,
++ false, filter_node_name, true, copy_mode, errp);
+ }
+
+ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
+@@ -1778,7 +1837,8 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
+ job_id, bs, creation_flags, base, NULL, speed, 0, 0,
+ MIRROR_LEAVE_BACKING_CHAIN, false,
+ on_error, on_error, true, cb, opaque,
+- &commit_active_job_driver, false, base, auto_complete,
++ &commit_active_job_driver, MIRROR_SYNC_MODE_FULL,
++ NULL, 0, base, auto_complete,
+ filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
+ &local_err);
+ if (local_err) {
+diff --git a/blockdev.c b/blockdev.c
+index e5310cb939..08285b9e86 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3763,6 +3763,10 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *target,
+ bool has_replaces, const char *replaces,
+ enum MirrorSyncMode sync,
++ bool has_bitmap,
++ const char *bitmap_name,
++ bool has_bitmap_mode,
++ BitmapSyncMode bitmap_mode,
+ BlockMirrorBackingMode backing_mode,
+ bool zero_target,
+ bool has_speed, int64_t speed,
+@@ -3781,6 +3785,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
+ Error **errp)
+ {
+ int job_flags = JOB_DEFAULT;
++ BdrvDirtyBitmap *bitmap = NULL;
+
+ if (!has_speed) {
+ speed = 0;
+@@ -3835,6 +3840,29 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
+ sync = MIRROR_SYNC_MODE_FULL;
+ }
+
++ if (has_bitmap) {
++ if (granularity) {
++ error_setg(errp, "Granularity and bitmap cannot both be set");
++ return;
++ }
++
++ if (!has_bitmap_mode) {
++ error_setg(errp, "bitmap-mode must be specified if"
++ " a bitmap is provided");
++ return;
++ }
++
++ bitmap = bdrv_find_dirty_bitmap(bs, bitmap_name);
++ if (!bitmap) {
++ error_setg(errp, "Dirty bitmap '%s' not found", bitmap_name);
++ return;
++ }
++
++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
++ return;
++ }
++ }
++
+ if (has_replaces) {
+ BlockDriverState *to_replace_bs;
+ AioContext *replace_aio_context;
+@@ -3872,8 +3900,8 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
+ * and will allow to check whether the node still exist at mirror completion
+ */
+ mirror_start(job_id, bs, target,
+- has_replaces ? replaces : NULL, job_flags,
+- speed, granularity, buf_size, sync, backing_mode, zero_target,
++ has_replaces ? replaces : NULL, job_flags, speed, granularity,
++ buf_size, sync, bitmap, bitmap_mode, backing_mode, zero_target,
+ on_source_error, on_target_error, unmap, filter_node_name,
+ copy_mode, errp);
+ }
+@@ -4003,6 +4031,8 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
+
+ blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
+ arg->has_replaces, arg->replaces, arg->sync,
++ arg->has_bitmap, arg->bitmap,
++ arg->has_bitmap_mode, arg->bitmap_mode,
+ backing_mode, zero_target,
+ arg->has_speed, arg->speed,
+ arg->has_granularity, arg->granularity,
+@@ -4025,6 +4055,8 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
+ const char *device, const char *target,
+ bool has_replaces, const char *replaces,
+ MirrorSyncMode sync,
++ bool has_bitmap, const char *bitmap,
++ bool has_bitmap_mode, BitmapSyncMode bitmap_mode,
+ bool has_speed, int64_t speed,
+ bool has_granularity, uint32_t granularity,
+ bool has_buf_size, int64_t buf_size,
+@@ -4068,7 +4100,8 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
+ }
+
+ blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
+- has_replaces, replaces, sync, backing_mode,
++ has_replaces, replaces, sync, has_bitmap,
++ bitmap, has_bitmap_mode, bitmap_mode, backing_mode,
+ zero_target, has_speed, speed,
+ has_granularity, granularity,
+ has_buf_size, buf_size,
+diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c
+index 0c861809f0..da87a67a57 100644
+--- a/tests/test-block-iothread.c
++++ b/tests/test-block-iothread.c
+@@ -611,8 +611,8 @@ static void test_propagate_mirror(void)
+
+ /* Start a mirror job */
+ mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0,
+- MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false,
+- BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
++ MIRROR_SYNC_MODE_NONE, NULL, 0, MIRROR_OPEN_BACKING_CHAIN,
++ false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
+ false, "filter_node", MIRROR_COPY_MODE_BACKGROUND,
+ &error_abort);
+ job = job_get("job0");
+diff --git a/qapi/block-core.json b/qapi/block-core.json
+index 0b987ad6e3..e2050bab1d 100644
+--- a/qapi/block-core.json
++++ b/qapi/block-core.json
+@@ -2086,10 +2086,19 @@
+ # (all the disk, only the sectors allocated in the topmost image, or
+ # only new I/O).
+ #
++# @bitmap: The name of a bitmap to use for sync=bitmap mode. This argument must
++# be present for bitmap mode and absent otherwise. The bitmap's
++# granularity is used instead of @granularity (since 4.1).
++#
++# @bitmap-mode: Specifies the type of data the bitmap should contain after
++# the operation concludes. Must be present if sync is "bitmap".
++# Must NOT be present otherwise. (Since 4.1)
++#
+ # @granularity: granularity of the dirty bitmap, default is 64K
+ # if the image format doesn't have clusters, 4K if the clusters
+ # are smaller than that, else the cluster size. Must be a
+-# power of 2 between 512 and 64M (since 1.4).
++# power of 2 between 512 and 64M. Must not be specified if
++# @bitmap is present (since 1.4).
+ #
+ # @buf-size: maximum amount of data in flight from source to
+ # target (since 1.4).
+@@ -2127,7 +2136,9 @@
+ { 'struct': 'DriveMirror',
+ 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
+ '*format': 'str', '*node-name': 'str', '*replaces': 'str',
+- 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
++ 'sync': 'MirrorSyncMode', '*bitmap': 'str',
++ '*bitmap-mode': 'BitmapSyncMode',
++ '*mode': 'NewImageMode',
+ '*speed': 'int', '*granularity': 'uint32',
+ '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
+ '*on-target-error': 'BlockdevOnError',
+@@ -2394,10 +2405,19 @@
+ # (all the disk, only the sectors allocated in the topmost image, or
+ # only new I/O).
+ #
++# @bitmap: The name of a bitmap to use for sync=bitmap mode. This argument must
++# be present for bitmap mode and absent otherwise. The bitmap's
++# granularity is used instead of @granularity (since 4.1).
++#
++# @bitmap-mode: Specifies the type of data the bitmap should contain after
++# the operation concludes. Must be present if sync is "bitmap".
++# Must NOT be present otherwise. (Since 4.1)
++#
+ # @granularity: granularity of the dirty bitmap, default is 64K
+ # if the image format doesn't have clusters, 4K if the clusters
+ # are smaller than that, else the cluster size. Must be a
+-# power of 2 between 512 and 64M
++# power of 2 between 512 and 64M . Must not be specified if
++# @bitmap is present.
+ #
+ # @buf-size: maximum amount of data in flight from source to
+ # target
+@@ -2446,7 +2466,8 @@
+ { 'command': 'blockdev-mirror',
+ 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
+ '*replaces': 'str',
+- 'sync': 'MirrorSyncMode',
++ 'sync': 'MirrorSyncMode', '*bitmap': 'str',
++ '*bitmap-mode': 'BitmapSyncMode',
+ '*speed': 'int', '*granularity': 'uint32',
+ '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
+ '*on-target-error': 'BlockdevOnError',
+--
+2.20.1
+
diff --git a/debian/patches/pve/0035-drive-mirror-add-support-for-conditional-and-always-.patch b/debian/patches/pve/0035-drive-mirror-add-support-for-conditional-and-always-.patch
new file mode 100644
index 0000000..3a2e5b9
--- /dev/null
+++ b/debian/patches/pve/0035-drive-mirror-add-support-for-conditional-and-always-.patch
@@ -0,0 +1,83 @@
+From 4c46f8b0f6fd8efba5e4d0b738b4f01ffcb92d33 Mon Sep 17 00:00:00 2001
+From: John Snow <jsnow at redhat.com>
+Date: Tue, 9 Jul 2019 21:12:30 -0400
+Subject: [PATCH qemu 35/39] drive-mirror: add support for conditional and
+ always bitmap sync modes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Teach mirror two new tricks for using bitmaps:
+
+Always: no matter what, we synchronize the copy_bitmap back to the
+sync_bitmap. In effect, this allows us resume a failed mirror at a later
+date.
+
+Conditional: On success only, we sync the bitmap. This is akin to
+incremental backup modes; we can use this bitmap to later refresh a
+successfully created mirror.
+
+Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
+---
+ block/mirror.c | 24 ++++++++++++++++++------
+ 1 file changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/block/mirror.c b/block/mirror.c
+index fd7f574365..40d174a625 100644
+--- a/block/mirror.c
++++ b/block/mirror.c
+@@ -645,8 +645,6 @@ static int mirror_exit_common(Job *job)
+ bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
+ }
+
+- bdrv_release_dirty_bitmap(s->dirty_bitmap);
+-
+ /* Make sure that the source BDS doesn't go away during bdrv_replace_node,
+ * before we can call bdrv_drained_end */
+ bdrv_ref(src);
+@@ -731,6 +729,18 @@ static int mirror_exit_common(Job *job)
+ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
+ blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
+
++ if (s->sync_bitmap) {
++ if (s->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS ||
++ (s->bitmap_mode == BITMAP_SYNC_MODE_ON_SUCCESS &&
++ job->ret == 0 && ret == 0)) {
++ /* Success; synchronize copy back to sync. */
++ bdrv_clear_dirty_bitmap(s->sync_bitmap, NULL);
++ bdrv_merge_dirty_bitmap(s->sync_bitmap, s->dirty_bitmap,
++ NULL, &error_abort);
++ }
++ }
++ bdrv_release_dirty_bitmap(s->dirty_bitmap);
++
+ bs_opaque->job = NULL;
+
+ bdrv_drained_end(src);
+@@ -1562,10 +1572,6 @@ static BlockJob *mirror_start_job(
+ " sync mode",
+ MirrorSyncMode_str(sync_mode));
+ return NULL;
+- } else if (bitmap_mode != BITMAP_SYNC_MODE_NEVER) {
+- error_setg(errp,
+- "Bitmap Sync Mode '%s' is not supported by Mirror",
+- BitmapSyncMode_str(bitmap_mode));
+ }
+ } else if (bitmap) {
+ error_setg(errp,
+@@ -1582,6 +1588,12 @@ static BlockJob *mirror_start_job(
+ return NULL;
+ }
+ granularity = bdrv_dirty_bitmap_granularity(bitmap);
++
++ if (bitmap_mode != BITMAP_SYNC_MODE_NEVER) {
++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) {
++ return NULL;
++ }
++ }
+ } else if (granularity == 0) {
+ granularity = bdrv_get_default_bitmap_granularity(target);
+ }
+--
+2.20.1
+
diff --git a/debian/patches/pve/0036-mirror-add-check-for-bitmap-mode-without-bitmap.patch b/debian/patches/pve/0036-mirror-add-check-for-bitmap-mode-without-bitmap.patch
new file mode 100644
index 0000000..879e516
--- /dev/null
+++ b/debian/patches/pve/0036-mirror-add-check-for-bitmap-mode-without-bitmap.patch
@@ -0,0 +1,33 @@
+From daec2f64e120a23a8d263f7d058bd84c48397bc8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= <f.gruenbichler at proxmox.com>
+Date: Mon, 17 Feb 2020 11:59:20 +0100
+Subject: [PATCH qemu 36/39] mirror: add check for bitmap-mode without bitmap
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+as one without the other does not make much sense with the current set
+of modes.
+
+Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
+---
+ blockdev.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/blockdev.c b/blockdev.c
+index 08285b9e86..768a0b0fd4 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3861,6 +3861,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
+ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) {
+ return;
+ }
++ } else if (has_bitmap_mode) {
++ error_setg(errp, "Cannot specify bitmap sync mode without a bitmap");
++ return;
+ }
+
+ if (has_replaces) {
+--
+2.20.1
+
diff --git a/debian/patches/pve/0037-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch b/debian/patches/pve/0037-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
new file mode 100644
index 0000000..40d36e8
--- /dev/null
+++ b/debian/patches/pve/0037-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
@@ -0,0 +1,45 @@
+From 31ea5889c25012189aabe51157b32cfbe87a63be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= <f.gruenbichler at proxmox.com>
+Date: Mon, 17 Feb 2020 10:43:22 +0100
+Subject: [PATCH qemu 37/39] mirror: switch to bdrv_dirty_bitmap_merge_internal
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+since sync_bitmap is busy at the point of merging, and we checked access
+beforehand.
+
+Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
+---
+ block/mirror.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/block/mirror.c b/block/mirror.c
+index 40d174a625..d6aca2874e 100644
+--- a/block/mirror.c
++++ b/block/mirror.c
+@@ -735,8 +735,8 @@ static int mirror_exit_common(Job *job)
+ job->ret == 0 && ret == 0)) {
+ /* Success; synchronize copy back to sync. */
+ bdrv_clear_dirty_bitmap(s->sync_bitmap, NULL);
+- bdrv_merge_dirty_bitmap(s->sync_bitmap, s->dirty_bitmap,
+- NULL, &error_abort);
++ bdrv_dirty_bitmap_merge_internal(s->sync_bitmap, s->dirty_bitmap,
++ NULL, true);
+ }
+ }
+ bdrv_release_dirty_bitmap(s->dirty_bitmap);
+@@ -1727,8 +1727,8 @@ static BlockJob *mirror_start_job(
+ }
+
+ if (s->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
+- bdrv_merge_dirty_bitmap(s->dirty_bitmap, s->sync_bitmap,
+- NULL, &local_err);
++ bdrv_dirty_bitmap_merge_internal(s->dirty_bitmap, s->sync_bitmap,
++ NULL, true);
+ if (local_err) {
+ goto fail;
+ }
+--
+2.20.1
+
diff --git a/debian/patches/pve/0038-iotests-add-test-for-bitmap-mirror.patch b/debian/patches/pve/0038-iotests-add-test-for-bitmap-mirror.patch
new file mode 100644
index 0000000..112250e
--- /dev/null
+++ b/debian/patches/pve/0038-iotests-add-test-for-bitmap-mirror.patch
@@ -0,0 +1,3447 @@
+From 145eea36a4a34919e2551cc14774c47dfb32ac3c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= <f.gruenbichler at proxmox.com>
+Date: Mon, 17 Feb 2020 10:47:07 +0100
+Subject: [PATCH qemu 38/39] iotests: add test for bitmap mirror
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+heavily based on/practically forked off iotest 257 for bitmap backups,
+but:
+
+- no writes to filter node 'mirror-top' between completion and
+finalization, as those seem to deadlock?
+- no inclusion of not-yet-available full/top sync modes in combination
+with bitmaps
+- extra set of reference/test mirrors to verify that writes in parallel
+with active mirror work
+
+intentionally keeping copyright and ownership of original test case to
+honor provenance.
+
+Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
+---
+ tests/qemu-iotests/284 | 547 +++++++
+ tests/qemu-iotests/284.out | 2846 ++++++++++++++++++++++++++++++++++++
+ tests/qemu-iotests/group | 1 +
+ 3 files changed, 3394 insertions(+)
+ create mode 100755 tests/qemu-iotests/284
+ create mode 100644 tests/qemu-iotests/284.out
+
+diff --git a/tests/qemu-iotests/284 b/tests/qemu-iotests/284
+new file mode 100755
+index 0000000000..b04a8e651a
+--- /dev/null
++++ b/tests/qemu-iotests/284
+@@ -0,0 +1,547 @@
++#!/usr/bin/env python3
++#
++# Test bitmap-sync mirrors (incremental, differential, and partials)
++#
++# Copyright (c) 2019 John Snow for Red Hat, Inc.
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++#
++# owner=jsnow at redhat.com
++
++import math
++import os
++
++import iotests
++from iotests import log, qemu_img
++
++SIZE = 64 * 1024 * 1024
++GRANULARITY = 64 * 1024
++
++
++class Pattern:
++ def __init__(self, byte, offset, size=GRANULARITY):
++ self.byte = byte
++ self.offset = offset
++ self.size = size
++
++ def bits(self, granularity):
++ lower = self.offset // granularity
++ upper = (self.offset + self.size - 1) // granularity
++ return set(range(lower, upper + 1))
++
++
++class PatternGroup:
++ """Grouping of Pattern objects. Initialize with an iterable of Patterns."""
++ def __init__(self, patterns):
++ self.patterns = patterns
++
++ def bits(self, granularity):
++ """Calculate the unique bits dirtied by this pattern grouping"""
++ res = set()
++ for pattern in self.patterns:
++ res |= pattern.bits(granularity)
++ return res
++
++
++GROUPS = [
++ PatternGroup([
++ # Batch 0: 4 clusters
++ Pattern('0x49', 0x0000000),
++ Pattern('0x6c', 0x0100000), # 1M
++ Pattern('0x6f', 0x2000000), # 32M
++ Pattern('0x76', 0x3ff0000)]), # 64M - 64K
++ PatternGroup([
++ # Batch 1: 6 clusters (3 new)
++ Pattern('0x65', 0x0000000), # Full overwrite
++ Pattern('0x77', 0x00f8000), # Partial-left (1M-32K)
++ Pattern('0x72', 0x2008000), # Partial-right (32M+32K)
++ Pattern('0x69', 0x3fe0000)]), # Adjacent-left (64M - 128K)
++ PatternGroup([
++ # Batch 2: 7 clusters (3 new)
++ Pattern('0x74', 0x0010000), # Adjacent-right
++ Pattern('0x69', 0x00e8000), # Partial-left (1M-96K)
++ Pattern('0x6e', 0x2018000), # Partial-right (32M+96K)
++ Pattern('0x67', 0x3fe0000,
++ 2*GRANULARITY)]), # Overwrite [(64M-128K)-64M)
++ PatternGroup([
++ # Batch 3: 8 clusters (5 new)
++ # Carefully chosen such that nothing re-dirties the one cluster
++ # that copies out successfully before failure in Group #1.
++ Pattern('0xaa', 0x0010000,
++ 3*GRANULARITY), # Overwrite and 2x Adjacent-right
++ Pattern('0xbb', 0x00d8000), # Partial-left (1M-160K)
++ Pattern('0xcc', 0x2028000), # Partial-right (32M+160K)
++ Pattern('0xdd', 0x3fc0000)]), # New; leaving a gap to the right
++]
++
++
++class EmulatedBitmap:
++ def __init__(self, granularity=GRANULARITY):
++ self._bits = set()
++ self.granularity = granularity
++
++ def dirty_bits(self, bits):
++ self._bits |= set(bits)
++
++ def dirty_group(self, n):
++ self.dirty_bits(GROUPS[n].bits(self.granularity))
++
++ def clear(self):
++ self._bits = set()
++
++ def clear_bits(self, bits):
++ self._bits -= set(bits)
++
++ def clear_bit(self, bit):
++ self.clear_bits({bit})
++
++ def clear_group(self, n):
++ self.clear_bits(GROUPS[n].bits(self.granularity))
++
++ @property
++ def first_bit(self):
++ return sorted(self.bits)[0]
++
++ @property
++ def bits(self):
++ return self._bits
++
++ @property
++ def count(self):
++ return len(self.bits)
++
++ def compare(self, qmp_bitmap):
++ """
++ Print a nice human-readable message checking that a bitmap as reported
++ by the QMP interface has as many bits set as we expect it to.
++ """
++
++ name = qmp_bitmap.get('name', '(anonymous)')
++ log("= Checking Bitmap {:s} =".format(name))
++
++ want = self.count
++ have = qmp_bitmap['count'] // qmp_bitmap['granularity']
++
++ log("expecting {:d} dirty sectors; have {:d}. {:s}".format(
++ want, have, "OK!" if want == have else "ERROR!"))
++ log('')
++
++
++class Drive:
++ """Represents, vaguely, a drive attached to a VM.
++ Includes format, graph, and device information."""
++
++ def __init__(self, path, vm=None):
++ self.path = path
++ self.vm = vm
++ self.fmt = None
++ self.size = None
++ self.node = None
++
++ def img_create(self, fmt, size):
++ self.fmt = fmt
++ self.size = size
++ iotests.qemu_img_create('-f', self.fmt, self.path, str(self.size))
++
++ def create_target(self, name, fmt, size):
++ basename = os.path.basename(self.path)
++ file_node_name = "file_{}".format(basename)
++ vm = self.vm
++
++ log(vm.command('blockdev-create', job_id='bdc-file-job',
++ options={
++ 'driver': 'file',
++ 'filename': self.path,
++ 'size': 0,
++ }))
++ vm.run_job('bdc-file-job')
++ log(vm.command('blockdev-add', driver='file',
++ node_name=file_node_name, filename=self.path))
++
++ log(vm.command('blockdev-create', job_id='bdc-fmt-job',
++ options={
++ 'driver': fmt,
++ 'file': file_node_name,
++ 'size': size,
++ }))
++ vm.run_job('bdc-fmt-job')
++ log(vm.command('blockdev-add', driver=fmt,
++ node_name=name,
++ file=file_node_name))
++ self.fmt = fmt
++ self.size = size
++ self.node = name
++
++def blockdev_mirror(vm, device, target, sync, **kwargs):
++ # Strip any arguments explicitly nulled by the caller:
++ kwargs = {key: val for key, val in kwargs.items() if val is not None}
++ result = vm.qmp_log('blockdev-mirror',
++ device=device,
++ target=target,
++ sync=sync,
++ filter_node_name='mirror-top',
++ **kwargs)
++ return result
++
++def blockdev_mirror_mktarget(drive, target_id, filepath, sync, **kwargs):
++ target_drive = Drive(filepath, vm=drive.vm)
++ target_drive.create_target(target_id, drive.fmt, drive.size)
++ blockdev_mirror(drive.vm, drive.node, target_id, sync, **kwargs)
++
++def reference_mirror(drive, n, filepath):
++ log("--- Reference mirror #{:d} ---\n".format(n))
++ target_id = "ref_target_{:d}".format(n)
++ job_id = "ref_mirror_{:d}".format(n)
++ blockdev_mirror_mktarget(drive, target_id, filepath, "full",
++ job_id=job_id)
++ drive.vm.run_job(job_id, auto_dismiss=True)
++ log('')
++
++def mirror(drive, n, filepath, sync, **kwargs):
++ log("--- Test mirror #{:d} ---\n".format(n))
++ target_id = "mirror_target_{:d}".format(n)
++ job_id = "mirror_{:d}".format(n)
++ kwargs.setdefault('auto-finalize', False)
++ blockdev_mirror_mktarget(drive, target_id, filepath, sync,
++ job_id=job_id, **kwargs)
++ return job_id
++
++def perform_writes(drive, n, filter_node_name=None):
++ log("--- Write #{:d} ---\n".format(n))
++ for pattern in GROUPS[n].patterns:
++ cmd = "write -P{:s} 0x{:07x} 0x{:x}".format(
++ pattern.byte,
++ pattern.offset,
++ pattern.size)
++ log(cmd)
++ log(drive.vm.hmp_qemu_io(filter_node_name or drive.node, cmd))
++ bitmaps = drive.vm.query_bitmaps()
++ log({'bitmaps': bitmaps}, indent=2)
++ log('')
++ return bitmaps
++
++
++def compare_images(image, reference, baseimg=None, expected_match=True):
++ """
++ Print a nice human-readable message comparing these images.
++ """
++ expected_ret = 0 if expected_match else 1
++ if baseimg:
++ assert qemu_img("rebase", "-u", "-b", baseimg, image) == 0
++ ret = qemu_img("compare", image, reference)
++ log('qemu_img compare "{:s}" "{:s}" ==> {:s}, {:s}'.format(
++ image, reference,
++ "Identical" if ret == 0 else "Mismatch",
++ "OK!" if ret == expected_ret else "ERROR!"),
++ filters=[iotests.filter_testfiles])
++
++def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
++ """
++ Test bitmap mirror routines.
++
++ :param bsync_mode: Is the Bitmap Sync mode, and can be any of:
++ - on-success: This is the "incremental" style mode. Bitmaps are
++ synchronized to what was copied out only on success.
++ (Partial images must be discarded.)
++ - never: This is the "differential" style mode.
++ Bitmaps are never synchronized.
++ - always: This is a "best effort" style mode.
++ Bitmaps are always synchronized, regardless of failure.
++ (Partial images must be kept.)
++
++ :param msync_mode: The mirror sync mode to use for the first mirror.
++ Can be any one of:
++ - bitmap: mirrors based on bitmap manifest.
++ - full: Full mirrors.
++ - top: Full mirrors of the top layer only.
++
++ :param failure: Is the (optional) failure mode, and can be any of:
++ - None: No failure. Test the normative path. Default.
++ - simulated: Cancel the job right before it completes.
++ This also tests writes "during" the job.
++ - intermediate: This tests a job that fails mid-process and produces
++ an incomplete mirror. Testing limitations prevent
++ testing competing writes.
++ """
++ with iotests.FilePaths(['img', 'bsync1', 'bsync2', 'bsync3',
++ 'fmirror0', 'fmirror1', 'fmirror2', 'fmirror3']) as \
++ (img_path, bsync1, bsync2, bsync3,
++ fmirror0, fmirror1, fmirror2, fmirror3), \
++ iotests.VM() as vm:
++
++ mode = "Mode {:s}; Bitmap Sync {:s}".format(msync_mode, bsync_mode)
++ preposition = "with" if failure else "without"
++ cond = "{:s} {:s}".format(preposition,
++ "{:s} failure".format(failure) if failure
++ else "failure")
++ log("\n=== {:s} {:s} ===\n".format(mode, cond))
++
++ log('--- Preparing image & VM ---\n')
++ drive0 = Drive(img_path, vm=vm)
++ drive0.img_create(iotests.imgfmt, SIZE)
++ vm.add_device("{},id=scsi0".format(iotests.get_virtio_scsi_device()))
++ vm.launch()
++
++ file_config = {
++ 'driver': 'file',
++ 'filename': drive0.path
++ }
++
++ if failure == 'intermediate':
++ file_config = {
++ 'driver': 'blkdebug',
++ 'image': file_config,
++ 'set-state': [{
++ 'event': 'flush_to_disk',
++ 'state': 1,
++ 'new_state': 2
++ }, {
++ 'event': 'read_aio',
++ 'state': 2,
++ 'new_state': 3
++ }, {
++ 'event': 'read_aio',
++ 'state': 3,
++ 'new_state': 4
++ }],
++ 'inject-error': [{
++ 'event': 'read_aio',
++ 'errno': 5,
++ 'state': 3,
++ 'immediately': False,
++ 'once': True
++ }, {
++ 'event': 'read_aio',
++ 'errno': 5,
++ 'state': 4,
++ 'immediately': False,
++ 'once': True
++ }]
++ }
++
++ drive0.node = 'drive0'
++ vm.qmp_log('blockdev-add',
++ filters=[iotests.filter_qmp_testfiles],
++ node_name=drive0.node,
++ driver=drive0.fmt,
++ file=file_config)
++ log('')
++
++ # 0 - Writes and Reference mirror
++ perform_writes(drive0, 0)
++ reference_mirror(drive0, 0, fmirror0)
++ log('--- Add Bitmap ---\n')
++ vm.qmp_log("block-dirty-bitmap-add", node=drive0.node,
++ name="bitmap0", granularity=GRANULARITY)
++ log('')
++ ebitmap = EmulatedBitmap()
++
++ # 1 - Writes and Reference mirror
++ bitmaps = perform_writes(drive0, 1)
++ ebitmap.dirty_group(1)
++ bitmap = vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps)
++ ebitmap.compare(bitmap)
++ reference_mirror(drive0, 1, fmirror1)
++
++ # 1 - Test mirror (w/ Optional induced failure)
++ if failure == 'intermediate':
++ # Activate blkdebug induced failure for second-to-next read
++ log(vm.hmp_qemu_io(drive0.node, 'flush'))
++ log('')
++ job = mirror(drive0, 1, bsync1, msync_mode,
++ bitmap="bitmap0", bitmap_mode=bsync_mode)
++
++ vm.run_job(job, auto_dismiss=True, auto_finalize=False,
++ cancel=(failure == 'simulated'))
++ bitmaps = vm.query_bitmaps()
++ log({'bitmaps': bitmaps}, indent=2)
++ log('')
++
++ if bsync_mode == 'always':
++ if failure == 'intermediate':
++ # We manage to copy one sector (one bit) before the error.
++ ebitmap.clear_bit(ebitmap.first_bit)
++ else:
++ # successful mirror / cancelled complete mirror
++ ebitmap.clear()
++
++ if bsync_mode == 'on-success' and not failure:
++ ebitmap.clear()
++
++ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
++
++ # 2 - Reference mirror
++ reference_mirror(drive0, 2, fmirror2)
++
++ # 2 - Bitmap mirror with writes before completion
++ job = mirror(drive0, 2, bsync2, "bitmap",
++ bitmap="bitmap0", bitmap_mode=bsync_mode)
++
++ bitmaps = perform_writes(drive0, 2)
++ ebitmap.dirty_group(2)
++ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
++
++ # don't use run_job as that logs too much even with use_log=False
++ events = [('JOB_STATUS_CHANGE', {'data': {'id': job}})]
++ while True:
++ ev = iotests.filter_qmp_event(vm.events_wait(events, timeout=10))
++ status = ev['data']['status']
++ if status == 'ready':
++ vm.qmp('job-complete', id=job)
++ elif status == 'standby':
++ vm.qmp('job-resume', id=job)
++ elif status == 'pending':
++ vm.qmp('job-finalize', id=job)
++ elif status == 'null':
++ break
++
++ if bsync_mode != 'never':
++ ebitmap.clear()
++
++ bitmaps = vm.query_bitmaps()
++ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
++
++ # 3 - Writes and Reference mirror
++ bitmaps = perform_writes(drive0, 3)
++ ebitmap.dirty_group(3)
++ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
++ reference_mirror(drive0, 3, fmirror3)
++
++ # 3 - Bitmap mirror (In failure modes, this is a recovery.)
++ job = mirror(drive0, 3, bsync3, "bitmap",
++ bitmap="bitmap0", bitmap_mode=bsync_mode)
++
++ vm.run_job(job, auto_dismiss=True, auto_finalize=False)
++ bitmaps = vm.query_bitmaps()
++
++ log({'bitmaps': bitmaps}, indent=2)
++ log('')
++ if bsync_mode != 'never':
++ ebitmap.clear()
++ ebitmap.compare(vm.get_bitmap(drive0.node, 'bitmap0', bitmaps=bitmaps))
++
++ log('--- Cleanup ---\n')
++ vm.qmp_log("block-dirty-bitmap-remove",
++ node=drive0.node, name="bitmap0")
++ bitmaps = vm.query_bitmaps()
++ log({'bitmaps': bitmaps}, indent=2)
++ vm.shutdown()
++ log('')
++
++ log('--- Verification ---\n')
++ compare_images(bsync1, fmirror1, baseimg=fmirror0,
++ expected_match=failure != 'intermediate')
++ if not failure or bsync_mode == 'always':
++ # Always keep the last mirror on success or when using 'always'
++ base = bsync1
++ else:
++ base = fmirror1
++
++ compare_images(bsync2, fmirror2, baseimg=base, expected_match=0)
++ compare_images(bsync3, fmirror3, baseimg=bsync2)
++ compare_images(img_path, fmirror3)
++ log('')
++
++def test_mirror_api():
++ """
++ Test malformed and prohibited invocations of the mirror API.
++ """
++ with iotests.FilePaths(['img', 'bsync1']) as \
++ (img_path, mirror_path), \
++ iotests.VM() as vm:
++
++ log("\n=== API failure tests ===\n")
++ log('--- Preparing image & VM ---\n')
++ drive0 = Drive(img_path, vm=vm)
++ drive0.img_create(iotests.imgfmt, SIZE)
++ vm.add_device("{},id=scsi0".format(iotests.get_virtio_scsi_device()))
++ vm.launch()
++
++ file_config = {
++ 'driver': 'file',
++ 'filename': drive0.path
++ }
++
++ drive0.node = 'drive0'
++ vm.qmp_log('blockdev-add',
++ filters=[iotests.filter_qmp_testfiles],
++ node_name=drive0.node,
++ driver=drive0.fmt,
++ file=file_config)
++ log('')
++
++ target0 = Drive(mirror_path, vm=vm)
++ target0.create_target("mirror_target", drive0.fmt, drive0.size)
++ log('')
++
++ vm.qmp_log("block-dirty-bitmap-add", node=drive0.node,
++ name="bitmap0", granularity=GRANULARITY)
++ log('')
++
++ log('-- Testing invalid QMP commands --\n')
++
++ error_cases = {
++ 'incremental': {
++ None: ['on-success', 'always', 'never', None],
++ 'bitmap404': ['on-success', 'always', 'never', None],
++ 'bitmap0': ['always', 'never']
++ },
++ 'bitmap': {
++ None: ['on-success', 'always', 'never', None],
++ 'bitmap404': ['on-success', 'always', 'never', None],
++ 'bitmap0': [None],
++ },
++ 'full': {
++ None: ['on-success', 'always', 'never'],
++ 'bitmap404': ['on-success', 'always', 'never', None],
++ 'bitmap0': ['on-success', 'always', 'never', None],
++ },
++ 'top': {
++ None: ['on-success', 'always', 'never'],
++ 'bitmap404': ['on-success', 'always', 'never', None],
++ 'bitmap0': ['on-success', 'always', 'never', None],
++ },
++ 'none': {
++ None: ['on-success', 'always', 'never'],
++ 'bitmap404': ['on-success', 'always', 'never', None],
++ 'bitmap0': ['on-success', 'always', 'never', None],
++ }
++ }
++
++ # Dicts, as always, are not stably-ordered prior to 3.7, so use tuples:
++ for sync_mode in ('incremental', 'bitmap', 'full', 'top', 'none'):
++ log("-- Sync mode {:s} tests --\n".format(sync_mode))
++ for bitmap in (None, 'bitmap404', 'bitmap0'):
++ for policy in error_cases[sync_mode][bitmap]:
++ blockdev_mirror(drive0.vm, drive0.node, "mirror_target",
++ sync_mode, job_id='api_job',
++ bitmap=bitmap, bitmap_mode=policy)
++ log('')
++
++
++def main():
++ for bsync_mode in ("never", "on-success", "always"):
++ for failure in ("simulated", "intermediate", None):
++ test_bitmap_sync(bsync_mode, "bitmap", failure)
++
++# for sync_mode in ('full', 'top'):
++# for bsync_mode in ('on-success', 'always'):
++# for failure in ('simulated', 'intermediate', None):
++# test_bitmap_sync(bsync_mode, sync_mode, failure)
++
++ test_mirror_api()
++
++if __name__ == '__main__':
++ iotests.script_main(main, supported_fmts=['qcow2'],
++ supported_protocols=['file'])
+diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out
+new file mode 100644
+index 0000000000..9b7408b6d6
+--- /dev/null
++++ b/tests/qemu-iotests/284.out
+@@ -0,0 +1,2846 @@
++
++=== Mode bitmap; Bitmap Sync never with simulated failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-cancel", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"id": "mirror_1", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 262144,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 655360,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 983040,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 15 dirty sectors; have 15. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 983040, "offset": 983040, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 983040, "offset": 983040, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 983040,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 15 dirty sectors; have 15. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync never with intermediate failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}, {"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++{"return": ""}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"data": {"action": "report", "device": "mirror_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"action": "report", "device": "mirror_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 262144,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 655360,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 983040,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 15 dirty sectors; have 15. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 983040, "offset": 983040, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 983040, "offset": 983040, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 983040,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 15 dirty sectors; have 15. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync never without failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"id": "mirror_1", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 262144,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 655360,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 983040,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 15 dirty sectors; have 15. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 983040, "offset": 983040, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 983040, "offset": 983040, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 983040,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 15 dirty sectors; have 15. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync on-success with simulated failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-cancel", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"id": "mirror_1", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 262144,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 655360,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 524288,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 8 dirty sectors; have 8. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync on-success with intermediate failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}, {"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++{"return": ""}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"data": {"action": "report", "device": "mirror_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"action": "report", "device": "mirror_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 262144,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 655360,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 10 dirty sectors; have 10. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 524288,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 8 dirty sectors; have 8. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync on-success without failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"id": "mirror_1", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 458752,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 458752,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 7 dirty sectors; have 7. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 524288,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 8 dirty sectors; have 8. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync always with simulated failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-cancel", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"id": "mirror_1", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 458752,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 458752,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 7 dirty sectors; have 7. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 524288,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 8 dirty sectors; have 8. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync always with intermediate failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "blkdebug", "image": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "inject-error": [{"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 3}, {"errno": 5, "event": "read_aio", "immediately": false, "once": true, "state": 4}], "set-state": [{"event": "flush_to_disk", "new-state": 2, "state": 1}, {"event": "read_aio", "new-state": 3, "state": 2}, {"event": "read_aio", "new-state": 4, "state": 3}]}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++{"return": ""}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"data": {"action": "report", "device": "mirror_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"action": "report", "device": "mirror_1", "operation": "read"}, "event": "BLOCK_JOB_ERROR", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "error": "Input/output error", "len": 393216, "offset": 65536, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 327680,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 5 dirty sectors; have 5. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 262144,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 589824,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 9 dirty sectors; have 9. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 524288,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 8 dirty sectors; have 8. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== Mode bitmap; Bitmap Sync always without failure ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++--- Write #0 ---
++
++write -P0x49 0x0000000 0x10000
++{"return": ""}
++write -P0x6c 0x0100000 0x10000
++{"return": ""}
++write -P0x6f 0x2000000 0x10000
++{"return": ""}
++write -P0x76 0x3ff0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {}
++}
++
++--- Reference mirror #0 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_0", "sync": "full", "target": "ref_target_0"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_0"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Add Bitmap ---
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++--- Write #1 ---
++
++write -P0x65 0x0000000 0x10000
++{"return": ""}
++write -P0x77 0x00f8000 0x10000
++{"return": ""}
++write -P0x72 0x2008000 0x10000
++{"return": ""}
++write -P0x69 0x3fe0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 393216,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 6 dirty sectors; have 6. OK!
++
++--- Reference mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_1", "sync": "full", "target": "ref_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_1"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #1 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_1", "sync": "bitmap", "target": "mirror_target_1"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_1"}}
++{"return": {}}
++{"data": {"id": "mirror_1", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_1", "len": 393216, "offset": 393216, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Reference mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_2", "sync": "full", "target": "ref_target_2"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_2"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #2 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_2", "sync": "bitmap", "target": "mirror_target_2"}}
++{"return": {}}
++--- Write #2 ---
++
++write -P0x74 0x0010000 0x10000
++{"return": ""}
++write -P0x69 0x00e8000 0x10000
++{"return": ""}
++write -P0x6e 0x2018000 0x10000
++{"return": ""}
++write -P0x67 0x3fe0000 0x20000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 458752,
++ "granularity": 65536,
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ },
++ {
++ "busy": true,
++ "count": 458752,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "locked"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 7 dirty sectors; have 7. OK!
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Write #3 ---
++
++write -P0xaa 0x0010000 0x30000
++{"return": ""}
++write -P0xbb 0x00d8000 0x10000
++{"return": ""}
++write -P0xcc 0x2028000 0x10000
++{"return": ""}
++write -P0xdd 0x3fc0000 0x10000
++{"return": ""}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 524288,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 8 dirty sectors; have 8. OK!
++
++--- Reference mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "ref_mirror_3", "sync": "full", "target": "ref_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "ref_mirror_3"}}
++{"return": {}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "ref_mirror_3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++
++--- Test mirror #3 ---
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++{"execute": "blockdev-mirror", "arguments": {"auto-finalize": false, "bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "mirror_3", "sync": "bitmap", "target": "mirror_target_3"}}
++{"return": {}}
++{"execute": "job-complete", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"execute": "job-finalize", "arguments": {"id": "mirror_3"}}
++{"return": {}}
++{"data": {"id": "mirror_3", "type": "mirror"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{"data": {"device": "mirror_3", "len": 524288, "offset": 524288, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
++{
++ "bitmaps": {
++ "drive0": [
++ {
++ "busy": false,
++ "count": 0,
++ "granularity": 65536,
++ "name": "bitmap0",
++ "persistent": false,
++ "recording": true,
++ "status": "active"
++ }
++ ]
++ }
++}
++
++= Checking Bitmap bitmap0 =
++expecting 0 dirty sectors; have 0. OK!
++
++--- Cleanup ---
++
++{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++{
++ "bitmaps": {}
++}
++
++--- Verification ---
++
++qemu_img compare "TEST_DIR/PID-bsync1" "TEST_DIR/PID-fmirror1" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-bsync2" "TEST_DIR/PID-fmirror2" ==> Mismatch, OK!
++qemu_img compare "TEST_DIR/PID-bsync3" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
++
++
++=== API failure tests ===
++
++--- Preparing image & VM ---
++
++{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-img"}, "node-name": "drive0"}}
++{"return": {}}
++
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-file-job"}}
++{"return": {}}
++{}
++{}
++{"execute": "job-dismiss", "arguments": {"id": "bdc-fmt-job"}}
++{"return": {}}
++{}
++
++{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmap0", "node": "drive0"}}
++{"return": {}}
++
++-- Testing invalid QMP commands --
++
++-- Sync mode incremental tests --
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'incremental' not supported"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'incremental' not supported"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'incremental' not supported"}}
++
++-- Sync mode bitmap tests --
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'bitmap' sync mode"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++-- Sync mode full tests --
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++-- Sync mode top tests --
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++-- Sync mode none tests --
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'none' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'none' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "sync mode 'none' is not compatible with bitmaps"}}
++
++{"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
++{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++
+diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
+index 6b10a6a762..a4db887d5d 100644
+--- a/tests/qemu-iotests/group
++++ b/tests/qemu-iotests/group
+@@ -286,3 +286,4 @@
+ 272 rw
+ 273 backing quick
+ 277 rw quick
++284 rw
+--
+2.20.1
+
diff --git a/debian/patches/pve/0039-mirror-move-some-checks-to-qmp.patch b/debian/patches/pve/0039-mirror-move-some-checks-to-qmp.patch
new file mode 100644
index 0000000..4f18849
--- /dev/null
+++ b/debian/patches/pve/0039-mirror-move-some-checks-to-qmp.patch
@@ -0,0 +1,275 @@
+From 1beb9c370adff5e8c674d57e7a17672c0a8eaa2a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= <f.gruenbichler at proxmox.com>
+Date: Mon, 10 Feb 2020 10:15:16 +0100
+Subject: [PATCH qemu 39/39] mirror: move some checks to qmp
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+and assert the passing conditions in block/mirror.c. while incremental
+mode was never available for drive-mirror, it makes the interface more
+uniform w.r.t. backup block jobs.
+
+Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
+---
+ block/mirror.c | 28 +++------------
+ blockdev.c | 29 +++++++++++++++
+ tests/qemu-iotests/284.out | 72 +++++++++++++++++++-------------------
+ 3 files changed, 70 insertions(+), 59 deletions(-)
+
+diff --git a/block/mirror.c b/block/mirror.c
+index d6aca2874e..7a3373fca7 100644
+--- a/block/mirror.c
++++ b/block/mirror.c
+@@ -1562,31 +1562,13 @@ static BlockJob *mirror_start_job(
+ Error *local_err = NULL;
+ int ret;
+
+- if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
+- error_setg(errp, "Sync mode '%s' not supported",
+- MirrorSyncMode_str(sync_mode));
+- return NULL;
+- } else if (sync_mode == MIRROR_SYNC_MODE_BITMAP) {
+- if (!bitmap) {
+- error_setg(errp, "Must provide a valid bitmap name for '%s'"
+- " sync mode",
+- MirrorSyncMode_str(sync_mode));
+- return NULL;
+- }
+- } else if (bitmap) {
+- error_setg(errp,
+- "sync mode '%s' is not compatible with bitmaps",
+- MirrorSyncMode_str(sync_mode));
+- return NULL;
+- }
++ /* QMP interface protects us from these cases */
++ assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL);
++ assert((bitmap && sync_mode == MIRROR_SYNC_MODE_BITMAP) ||
++ (!bitmap && sync_mode != MIRROR_SYNC_MODE_BITMAP));
++ assert(!(bitmap && granularity));
+
+ if (bitmap) {
+- if (granularity) {
+- error_setg(errp, "granularity (%d)"
+- "cannot be specified when a bitmap is provided",
+- granularity);
+- return NULL;
+- }
+ granularity = bdrv_dirty_bitmap_granularity(bitmap);
+
+ if (bitmap_mode != BITMAP_SYNC_MODE_NEVER) {
+diff --git a/blockdev.c b/blockdev.c
+index 768a0b0fd4..4b196a2785 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3840,7 +3840,36 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
+ sync = MIRROR_SYNC_MODE_FULL;
+ }
+
++ if ((sync == MIRROR_SYNC_MODE_BITMAP) ||
++ (sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
++ /* done before desugaring 'incremental' to print the right message */
++ if (!has_bitmap) {
++ error_setg(errp, "Must provide a valid bitmap name for "
++ "'%s' sync mode", MirrorSyncMode_str(sync));
++ return;
++ }
++ }
++
++ if (sync == MIRROR_SYNC_MODE_INCREMENTAL) {
++ if (has_bitmap_mode &&
++ bitmap_mode != BITMAP_SYNC_MODE_ON_SUCCESS) {
++ error_setg(errp, "Bitmap sync mode must be '%s' "
++ "when using sync mode '%s'",
++ BitmapSyncMode_str(BITMAP_SYNC_MODE_ON_SUCCESS),
++ MirrorSyncMode_str(sync));
++ return;
++ }
++ has_bitmap_mode = true;
++ sync = MIRROR_SYNC_MODE_BITMAP;
++ bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS;
++ }
++
+ if (has_bitmap) {
++ if (sync != MIRROR_SYNC_MODE_BITMAP) {
++ error_setg(errp, "Sync mode '%s' not supported with bitmap.",
++ MirrorSyncMode_str(sync));
++ return;
++ }
+ if (granularity) {
+ error_setg(errp, "Granularity and bitmap cannot both be set");
+ return;
+diff --git a/tests/qemu-iotests/284.out b/tests/qemu-iotests/284.out
+index 9b7408b6d6..06a2e29058 100644
+--- a/tests/qemu-iotests/284.out
++++ b/tests/qemu-iotests/284.out
+@@ -2681,45 +2681,45 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
+ -- Sync mode incremental tests --
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'incremental' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'incremental' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'incremental' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Sync mode 'incremental' not supported"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'incremental' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+ {"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Sync mode 'incremental' not supported"}}
++{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "incremental", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Sync mode 'incremental' not supported"}}
++{"error": {"class": "GenericError", "desc": "Bitmap sync mode must be 'on-success' when using sync mode 'incremental'"}}
+
+ -- Sync mode bitmap tests --
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'bitmap' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'bitmap' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
++{"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'bitmap' sync mode"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "bitmap", "target": "mirror_target"}}
+ {"error": {"class": "GenericError", "desc": "Must provide a valid bitmap name for 'bitmap' sync mode"}}
+@@ -2751,28 +2751,28 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
+ {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "full", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ -- Sync mode top tests --
+
+@@ -2786,28 +2786,28 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
+ {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'full' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "top", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'full' not supported with bitmap."}}
+
+ -- Sync mode none tests --
+
+@@ -2821,26 +2821,26 @@ qemu_img compare "TEST_DIR/PID-img" "TEST_DIR/PID-fmirror3" ==> Identical, OK!
+ {"error": {"class": "GenericError", "desc": "Cannot specify bitmap sync mode without a bitmap"}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap404' not found"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap404", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "on-success", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'none' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "always", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'none' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "bitmap-mode": "never", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "sync mode 'none' is not compatible with bitmaps"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+ {"execute": "blockdev-mirror", "arguments": {"bitmap": "bitmap0", "device": "drive0", "filter-node-name": "mirror-top", "job-id": "api_job", "sync": "none", "target": "mirror_target"}}
+-{"error": {"class": "GenericError", "desc": "bitmap-mode must be specified if a bitmap is provided"}}
++{"error": {"class": "GenericError", "desc": "Sync mode 'none' not supported with bitmap."}}
+
+--
+2.20.1
+
diff --git a/debian/patches/series b/debian/patches/series
index 0d4ca37..957eeb7 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -30,3 +30,9 @@ pve/0030-PVE-Backup-add-backup-dump-block-driver.patch
pve/0031-PVE-Backup-proxmox-backup-patches-for-qemu.patch
pve/0032-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch
pve/0033-PVE-Backup-aquire-aio_context-before-calling-backup_.patch
+pve/0034-drive-mirror-add-support-for-sync-bitmap-mode-never.patch
+pve/0035-drive-mirror-add-support-for-conditional-and-always-.patch
+pve/0036-mirror-add-check-for-bitmap-mode-without-bitmap.patch
+pve/0037-mirror-switch-to-bdrv_dirty_bitmap_merge_internal.patch
+pve/0038-iotests-add-test-for-bitmap-mirror.patch
+pve/0039-mirror-move-some-checks-to-qmp.patch
--
2.20.1
More information about the pve-devel
mailing list