diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-01-15 14:19:18 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-01-15 14:19:18 +0000 |
commit | 44ba6010635641a538c9b9b1f377dfa288751906 (patch) | |
tree | 080c6791a68fe518db0b523090c012eea98c321b /tests | |
parent | b2f7c27f56bf1116ebb7848c75914aa7c5d6a040 (diff) | |
parent | 636192c4b6052820ea126a5287c58a8f53f3c84f (diff) |
Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2019-01-14' into staging
nbd patches for 2019-01-14
Promote bitmap/NBD interfaces to stable for use in incremental
backups. Add 'qemu-nbd --bitmap'.
- John Snow: 0/11 bitmaps: remove x- prefix from QMP api
- Philippe Mathieu-Daudé: qemu-nbd: Rename 'exp' variable clashing with math::exp() symbol
- Eric Blake: 0/8 Promote x-nbd-server-add-bitmap to stable
# gpg: Signature made Mon 14 Jan 2019 16:13:45 GMT
# gpg: using RSA key A7A16B4A2527436A
# gpg: Good signature from "Eric Blake <eblake@redhat.com>"
# gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>"
# gpg: aka "[jpeg image of size 6874]"
# Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A
* remotes/ericb/tags/pull-nbd-2019-01-14:
qemu-nbd: Add --bitmap=NAME option
nbd: Merge nbd_export_bitmap into nbd_export_new
nbd: Remove x-nbd-server-add-bitmap
nbd: Allow bitmap export during QMP nbd-server-add
nbd: Merge nbd_export_set_name into nbd_export_new
nbd: Only require disabled bitmap for read-only exports
nbd: Forbid nbd-server-stop when server is not running
nbd: Add some error case testing to iotests 223
qemu-nbd: Rename 'exp' variable clashing with math::exp() symbol
iotests: add iotest 236 for testing bitmap merge
iotests: implement pretty-print for log and qmp_log
iotests: change qmp_log filters to expect QMP objects only
iotests: remove default filters from qmp_log
iotests: add qmp recursive sorting function
iotests: add filter_generated_node_ids
iotests.py: don't abort if IMGKEYSECRET is undefined
block: remove 'x' prefix from experimental bitmap APIs
blockdev: n-ary bitmap merge
block/dirty-bitmap: remove assertion from restore
blockdev: abort transactions in reverse order
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests')
-rwxr-xr-x | tests/qemu-iotests/206 | 8 | ||||
-rwxr-xr-x | tests/qemu-iotests/223 | 52 | ||||
-rw-r--r-- | tests/qemu-iotests/223.out | 23 | ||||
-rwxr-xr-x | tests/qemu-iotests/236 | 161 | ||||
-rw-r--r-- | tests/qemu-iotests/236.out | 351 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 1 | ||||
-rw-r--r-- | tests/qemu-iotests/iotests.py | 64 |
7 files changed, 635 insertions, 25 deletions
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index 128c334c7c..5bb738bf23 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -26,7 +26,9 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['qcow2']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', + filters=[iotests.filter_qmp_testfiles], + job_id='job0', options=options) if 'return' in result: assert result['return'] == {} @@ -52,7 +54,9 @@ with iotests.FilePath('t.qcow2') as disk_path, \ 'filename': disk_path, 'size': 0 }) - vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + vm.qmp_log('blockdev-add', + filters=[iotests.filter_qmp_testfiles], + driver='file', filename=disk_path, node_name='imgfile') blockdev_create(vm, { 'driver': imgfmt, diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 index 397b865d34..773892dbe6 100755 --- a/tests/qemu-iotests/223 +++ b/tests/qemu-iotests/223 @@ -25,6 +25,7 @@ status=1 # failure is the default! _cleanup() { + nbd_server_stop _cleanup_test_img _cleanup_qemu rm -f "$TEST_DIR/nbd" @@ -35,6 +36,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter . ./common.qemu +. ./common.nbd _supported_fmt qcow2 _supported_proto file # uses NBD as well @@ -61,6 +63,8 @@ echo "=== Create partially sparse image, then add dirty bitmaps ===" echo # Two bitmaps, to contrast granularity issues +# Also note that b will be disabled, while b2 is left enabled, to +# check for read-only interactions _make_test_img -o cluster_size=4k 4M $QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io run_qemu <<EOF @@ -107,26 +111,37 @@ echo _launch_qemu 2> >(_filter_nbd) +# Intentionally provoke some errors as well, to check error handling silent= _send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", "arguments":{"driver":"qcow2", "node-name":"n", "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" -_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", +_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", "arguments":{"node":"n", "name":"b"}}' "return" -_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", - "arguments":{"node":"n", "name":"b2"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "error" # Attempt add without server _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", "arguments":{"addr":{"type":"unix", "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "bitmap":"b"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"nosuch"}}' "error" # Attempt to export missing node _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", - "arguments":{"device":"n"}}' "return" -_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", - "arguments":{"name":"n", "bitmap":"b"}}' "return" + "arguments":{"device":"n"}}' "error" # Attempt to export same name twice _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", - "arguments":{"device":"n", "name":"n2"}}' "return" -_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", - "arguments":{"name":"n2", "bitmap":"b2"}}' "return" + "arguments":{"device":"n", "name":"n2", + "bitmap":"b2"}}' "error" # enabled vs. read-only +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2", + "bitmap":"b3"}}' "error" # Missing bitmap +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2", "writable":true, + "bitmap":"b2"}}' "return" echo echo "=== Contrast normal status to large granularity dirty-bitmap ===" @@ -150,16 +165,33 @@ $QEMU_IMG map --output=json --image-opts \ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map echo -echo "=== End NBD server ===" +echo "=== End qemu NBD server ===" echo _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", "arguments":{"name":"n"}}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", "arguments":{"name":"n2"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}}' "error" # Attempt duplicate clean _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "error" # Again _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" +echo +echo "=== Use qemu-nbd as server ===" +echo + +nbd_server_start_unix_socket -r -f $IMGFMT -B b "$TEST_IMG" +IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket" +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map + +nbd_server_start_unix_socket -f $IMGFMT -B b2 "$TEST_IMG" +IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket" +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out index 99ca172fbb..0de5240a75 100644 --- a/tests/qemu-iotests/223.out +++ b/tests/qemu-iotests/223.out @@ -27,11 +27,14 @@ wrote 2097152/2097152 bytes at offset 2097152 {"return": {}} {"return": {}} {"return": {}} +{"error": {"class": "GenericError", "desc": "NBD server not running"}} {"return": {}} +{"error": {"class": "GenericError", "desc": "NBD server already running"}} {"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} +{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}} +{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}} +{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} +{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} {"return": {}} === Contrast normal status to large granularity dirty-bitmap === @@ -58,10 +61,22 @@ read 2097152/2097152 bytes at offset 2097152 { "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] -=== End NBD server === +=== End qemu NBD server === {"return": {}} {"return": {}} +{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} {"return": {}} +{"error": {"class": "GenericError", "desc": "NBD server not running"}} {"return": {}} + +=== Use qemu-nbd as server === + +[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, +{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] +[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true}, +{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, +{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] *** done diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236 new file mode 100755 index 0000000000..79a6381f8e --- /dev/null +++ b/tests/qemu-iotests/236 @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# +# Test bitmap merges. +# +# Copyright (c) 2018 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@redhat.com + +import iotests +from iotests import log + +iotests.verify_image_format(supported_fmts=['generic']) +size = 64 * 1024 * 1024 +granularity = 64 * 1024 + +patterns = [("0x5d", "0", "64k"), + ("0xd5", "1M", "64k"), + ("0xdc", "32M", "64k"), + ("0xcd", "0x3ff0000", "64k")] # 64M - 64K + +overwrite = [("0xab", "0", "64k"), # Full overwrite + ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) + ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) + ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) + +def query_bitmaps(vm): + res = vm.qmp("query-block") + return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for + device in res['return'] } } + +with iotests.FilePath('img') as img_path, \ + iotests.VM() as vm: + + log('--- Preparing image & VM ---\n') + iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) + vm.add_drive(img_path) + vm.launch() + + log('\n--- Adding preliminary bitmaps A & B ---\n') + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="bitmapA", granularity=granularity) + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="bitmapB", granularity=granularity) + + # Dirties 4 clusters. count=262144 + log('\n--- Emulating writes ---\n') + for p in patterns: + cmd = "write -P%s %s %s" % p + log(cmd) + log(vm.hmp_qemu_io("drive0", cmd)) + + log(query_bitmaps(vm), indent=2) + + log('\n--- Submitting & Aborting Transaction ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapB" }}, + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapC", + "granularity": granularity }}, + { "type": "block-dirty-bitmap-clear", + "data": { "node": "drive0", "name": "bitmapA" }}, + { "type": "abort", "data": {}} + ]) + log(query_bitmaps(vm), indent=2) + + log('\n--- Disabling B & Adding C ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapB" }}, + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapC", + "granularity": granularity }}, + # Purely extraneous, but test that it works: + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapC" }}, + { "type": "block-dirty-bitmap-enable", + "data": { "node": "drive0", "name": "bitmapC" }}, + ]) + + log('\n--- Emulating further writes ---\n') + # Dirties 6 clusters, 3 of which are new in contrast to "A". + # A = 64 * 1024 * (4 + 3) = 458752 + # C = 64 * 1024 * 6 = 393216 + for p in overwrite: + cmd = "write -P%s %s %s" % p + log(cmd) + log(vm.hmp_qemu_io("drive0", cmd)) + + log('\n--- Disabling A & C ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapA" }}, + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapC" }} + ]) + + # A: 7 clusters + # B: 4 clusters + # C: 6 clusters + log(query_bitmaps(vm), indent=2) + + log('\n--- Submitting & Aborting Merge Transaction ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapD", + "disabled": True, "granularity": granularity }}, + { "type": "block-dirty-bitmap-merge", + "data": { "node": "drive0", "target": "bitmapD", + "bitmaps": ["bitmapB", "bitmapC"] }}, + { "type": "abort", "data": {}} + ]) + log(query_bitmaps(vm), indent=2) + + log('\n--- Creating D as a merge of B & C ---\n') + # Good hygiene: create a disabled bitmap as a merge target. + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapD", + "disabled": True, "granularity": granularity }}, + { "type": "block-dirty-bitmap-merge", + "data": { "node": "drive0", "target": "bitmapD", + "bitmaps": ["bitmapB", "bitmapC"] }} + ]) + + # A and D should now both have 7 clusters apiece. + # B and C remain unchanged with 4 and 6 respectively. + log(query_bitmaps(vm), indent=2) + + # A and D should be equivalent. + # Some formats round the size of the disk, so don't print the checksums. + check_a = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node="drive0", name="bitmapA")['return']['sha256'] + check_d = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node="drive0", name="bitmapD")['return']['sha256'] + assert(check_a == check_d) + + log('\n--- Removing bitmaps A, B, C, and D ---\n') + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapA") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapB") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapC") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapD") + + log('\n--- Final Query ---\n') + log(query_bitmaps(vm), indent=2) + + log('\n--- Done ---\n') + vm.shutdown() diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out new file mode 100644 index 0000000000..1dad24db0d --- /dev/null +++ b/tests/qemu-iotests/236.out @@ -0,0 +1,351 @@ +--- Preparing image & VM --- + + +--- Adding preliminary bitmaps A & B --- + +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapA", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapB", "node": "drive0"}} +{"return": {}} + +--- Emulating writes --- + +write -P0x5d 0 64k +{"return": ""} +write -P0xd5 1M 64k +{"return": ""} +write -P0xdc 32M 64k +{"return": ""} +write -P0xcd 0x3ff0000 64k +{"return": ""} +{ + "bitmaps": { + "drive0": [ + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "status": "active" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapA", + "status": "active" + } + ] + } +} + +--- Submitting & Aborting Transaction --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "node": "drive0", + "name": "bitmapB" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "node": "drive0", + "name": "bitmapC", + "granularity": 65536 + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "node": "drive0", + "name": "bitmapA" + }, + "type": "block-dirty-bitmap-clear" + }, + { + "data": {}, + "type": "abort" + } + ] + } +} +{ + "error": { + "class": "GenericError", + "desc": "Transaction aborted using Abort action" + } +} +{ + "bitmaps": { + "drive0": [ + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "status": "active" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapA", + "status": "active" + } + ] + } +} + +--- Disabling B & Adding C --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "node": "drive0", + "name": "bitmapB" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "node": "drive0", + "name": "bitmapC", + "granularity": 65536 + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "node": "drive0", + "name": "bitmapC" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "node": "drive0", + "name": "bitmapC" + }, + "type": "block-dirty-bitmap-enable" + } + ] + } +} +{ + "return": {} +} + +--- Emulating further writes --- + +write -P0xab 0 64k +{"return": ""} +write -P0xad 0x00f8000 64k +{"return": ""} +write -P0x1d 0x2008000 64k +{"return": ""} +write -P0xea 0x3fe0000 64k +{"return": ""} + +--- Disabling A & C --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "node": "drive0", + "name": "bitmapA" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "node": "drive0", + "name": "bitmapC" + }, + "type": "block-dirty-bitmap-disable" + } + ] + } +} +{ + "return": {} +} +{ + "bitmaps": { + "drive0": [ + { + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "status": "disabled" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "status": "disabled" + }, + { + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "status": "disabled" + } + ] + } +} + +--- Submitting & Aborting Merge Transaction --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "node": "drive0", + "disabled": true, + "name": "bitmapD", + "granularity": 65536 + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "node": "drive0", + "target": "bitmapD", + "bitmaps": [ + "bitmapB", + "bitmapC" + ] + }, + "type": "block-dirty-bitmap-merge" + }, + { + "data": {}, + "type": "abort" + } + ] + } +} +{ + "error": { + "class": "GenericError", + "desc": "Transaction aborted using Abort action" + } +} +{ + "bitmaps": { + "drive0": [ + { + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "status": "disabled" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "status": "disabled" + }, + { + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "status": "disabled" + } + ] + } +} + +--- Creating D as a merge of B & C --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "node": "drive0", + "disabled": true, + "name": "bitmapD", + "granularity": 65536 + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "node": "drive0", + "target": "bitmapD", + "bitmaps": [ + "bitmapB", + "bitmapC" + ] + }, + "type": "block-dirty-bitmap-merge" + } + ] + } +} +{ + "return": {} +} +{ + "bitmaps": { + "drive0": [ + { + "count": 458752, + "granularity": 65536, + "name": "bitmapD", + "status": "disabled" + }, + { + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "status": "disabled" + }, + { + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "status": "disabled" + }, + { + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "status": "disabled" + } + ] + } +} + +--- Removing bitmaps A, B, C, and D --- + +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapA", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapB", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapC", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapD", "node": "drive0"}} +{"return": {}} + +--- Final Query --- + +{ + "bitmaps": { + "drive0": [] + } +} + +--- Done --- + diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 61a6d98ebd..f6b245917a 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -233,3 +233,4 @@ 233 auto quick 234 auto quick migration 235 auto quick +236 auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index d537538ba0..cbedfaf1df 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -30,6 +30,7 @@ import signal import logging import atexit import io +from collections import OrderedDict sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) import qtest @@ -63,7 +64,7 @@ socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper') debug = False luks_default_secret_object = 'secret,id=keysec0,data=' + \ - os.environ['IMGKEYSECRET'] + os.environ.get('IMGKEYSECRET', '') luks_default_key_secret_opt = 'key-secret=keysec0' @@ -75,6 +76,16 @@ def qemu_img(*args): sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) return exitcode +def ordered_kwargs(kwargs): + # kwargs prior to 3.6 are not ordered, so: + od = OrderedDict() + for k, v in sorted(kwargs.items()): + if isinstance(v, dict): + od[k] = ordered_kwargs(v) + else: + od[k] = v + return od + def qemu_img_create(*args): args = list(args) @@ -235,10 +246,36 @@ def filter_qmp_event(event): event['timestamp']['microseconds'] = 'USECS' return event +def filter_qmp(qmsg, filter_fn): + '''Given a string filter, filter a QMP object's values. + filter_fn takes a (key, value) pair.''' + # Iterate through either lists or dicts; + if isinstance(qmsg, list): + items = enumerate(qmsg) + else: + items = qmsg.items() + + for k, v in items: + if isinstance(v, list) or isinstance(v, dict): + qmsg[k] = filter_qmp(v, filter_fn) + else: + qmsg[k] = filter_fn(k, v) + return qmsg + def filter_testfiles(msg): prefix = os.path.join(test_dir, "%s-" % (os.getpid())) return msg.replace(prefix, 'TEST_DIR/PID-') +def filter_qmp_testfiles(qmsg): + def _filter(key, value): + if key == 'filename' or key == 'backing-file': + return filter_testfiles(value) + return value + return filter_qmp(qmsg, _filter) + +def filter_generated_node_ids(msg): + return re.sub("#block[0-9]+", "NODE_NAME", msg) + def filter_img_info(output, filename): lines = [] for line in output.split('\n'): @@ -251,11 +288,18 @@ def filter_img_info(output, filename): lines.append(line) return '\n'.join(lines) -def log(msg, filters=[]): +def log(msg, filters=[], indent=None): + '''Logs either a string message or a JSON serializable message (like QMP). + If indent is provided, JSON serializable messages are pretty-printed.''' for flt in filters: msg = flt(msg) - if type(msg) is dict or type(msg) is list: - print(json.dumps(msg, sort_keys=True)) + if isinstance(msg, dict) or isinstance(msg, list): + # Python < 3.4 needs to know not to add whitespace when pretty-printing: + separators = (', ', ': ') if indent is None else (',', ': ') + # Don't sort if it's already sorted + do_sort = not isinstance(msg, OrderedDict) + print(json.dumps(msg, sort_keys=do_sort, + indent=indent, separators=separators)) else: print(msg) @@ -444,12 +488,14 @@ class VM(qtest.QEMUQtestMachine): result.append(filter_qmp_event(ev)) return result - def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): - logmsg = '{"execute": "%s", "arguments": %s}' % \ - (cmd, json.dumps(kwargs, sort_keys=True)) - log(logmsg, filters) + def qmp_log(self, cmd, filters=[], indent=None, **kwargs): + full_cmd = OrderedDict(( + ("execute", cmd), + ("arguments", ordered_kwargs(kwargs)) + )) + log(full_cmd, filters, indent=indent) result = self.qmp(cmd, **kwargs) - log(json.dumps(result, sort_keys=True), filters) + log(result, filters, indent=indent) return result def run_job(self, job, auto_finalize=True, auto_dismiss=False): |