diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-03-12 10:08:09 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-03-12 10:08:09 +0000 |
commit | 12c06d6f967a63515399b9e1f6a40f5ce871a8b7 (patch) | |
tree | d5c2f32efcb3b9622c042c94250d0fc3fdb56167 /tests | |
parent | 20f59d120053a26521fa0177ea07f947bc8bfff0 (diff) | |
parent | a1be5921e35dcf84ce9e3c9a5c3029cea530a60b (diff) | |
download | qemu-12c06d6f967a63515399b9e1f6a40f5ce871a8b7.zip qemu-12c06d6f967a63515399b9e1f6a40f5ce871a8b7.tar.gz qemu-12c06d6f967a63515399b9e1f6a40f5ce871a8b7.tar.bz2 |
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches
# gpg: Signature made Fri 09 Mar 2018 15:09:20 GMT
# gpg: using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6
* remotes/kevin/tags/for-upstream: (56 commits)
qemu-iotests: fix 203 migration completion race
iotests: Tweak 030 in order to trigger a race condition with parallel jobs
iotests: Skip test for ENOMEM error
iotests: Mark all tests executable
iotests: Test creating overlay when guest running
qemu-iotests: Test ssh image creation over QMP
qemu-iotests: Test qcow2 over file image creation with QMP
block: Fail bdrv_truncate() with negative size
file-posix: Fix no-op bdrv_truncate() with falloc preallocation
ssh: Support .bdrv_co_create
ssh: Pass BlockdevOptionsSsh to connect_to_ssh()
ssh: QAPIfy host-key-check option
ssh: Use QAPI BlockdevOptionsSsh object
sheepdog: Support .bdrv_co_create
sheepdog: QAPIfy "redundancy" create option
nfs: Support .bdrv_co_create
nfs: Use QAPI options in nfs_client_open()
rbd: Use qemu_rbd_connect() in qemu_rbd_do_create()
rbd: Assign s->snap/image_name in qemu_rbd_open()
rbd: Support .bdrv_co_create
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests')
28 files changed, 1513 insertions, 40 deletions
diff --git a/tests/check-qdict.c b/tests/check-qdict.c index ec628f3..a3faea8 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -665,6 +665,133 @@ static void qdict_crumple_test_empty(void) QDECREF(dst); } +static int qdict_count_entries(QDict *dict) +{ + const QDictEntry *e; + int count = 0; + + for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { + count++; + } + + return count; +} + +static void qdict_rename_keys_test(void) +{ + QDict *dict = qdict_new(); + QDict *copy; + QDictRenames *renames; + Error *local_err = NULL; + + qdict_put_str(dict, "abc", "foo"); + qdict_put_str(dict, "abcdef", "bar"); + qdict_put_int(dict, "number", 42); + qdict_put_bool(dict, "flag", true); + qdict_put_null(dict, "nothing"); + + /* Empty rename list */ + renames = (QDictRenames[]) { + { NULL, "this can be anything" } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Simple rename of all entries */ + renames = (QDictRenames[]) { + { "abc", "str1" }, + { "abcdef", "str2" }, + { "number", "int" }, + { "flag", "bool" }, + { "nothing", "null" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert(!qdict_haskey(copy, "abc")); + g_assert(!qdict_haskey(copy, "abcdef")); + g_assert(!qdict_haskey(copy, "number")); + g_assert(!qdict_haskey(copy, "flag")); + g_assert(!qdict_haskey(copy, "nothing")); + + g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Renames are processed top to bottom */ + renames = (QDictRenames[]) { + { "abc", "tmp" }, + { "abcdef", "abc" }, + { "number", "abcdef" }, + { "flag", "number" }, + { "nothing", "flag" }, + { "tmp", "nothing" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); + g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); + g_assert(!qdict_haskey(copy, "tmp")); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Conflicting rename */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &local_err); + + g_assert(local_err != NULL); + error_free(local_err); + local_err = NULL; + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Renames in an empty dict */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + + QDECREF(dict); + dict = qdict_new(); + + qdict_rename_keys(dict, renames, &error_abort); + g_assert(qdict_first(dict) == NULL); + + QDECREF(dict); +} + static void qdict_crumple_test_bad_inputs(void) { QDict *src; @@ -880,6 +1007,8 @@ int main(int argc, char **argv) g_test_add_func("/public/crumple/bad_inputs", qdict_crumple_test_bad_inputs); + g_test_add_func("/public/rename_keys", qdict_rename_keys_test); + /* The Big one */ if (g_test_slow()) { g_test_add_func("/stress/test", qdict_stress_test); diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 457984b..b5f8895 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -156,7 +156,7 @@ class TestSingleDrive(iotests.QMPTestCase): class TestParallelOps(iotests.QMPTestCase): num_ops = 4 # Number of parallel block-stream operations num_imgs = num_ops * 2 + 1 - image_len = num_ops * 1024 * 1024 + image_len = num_ops * 512 * 1024 imgs = [] def setUp(self): @@ -176,14 +176,14 @@ class TestParallelOps(iotests.QMPTestCase): '-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i]) # Put data into the images we are copying data from - for i in range(self.num_imgs / 2): - img_index = i * 2 + 1 - # Alternate between 512k and 1M. + odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1] + for i in range(len(odd_img_indexes)): + # Alternate between 256KB and 512KB. # This way jobs will not finish in the same order they were created - num_kb = 512 + 512 * (i % 2) + num_kb = 256 + 256 * (i % 2) qemu_io('-f', iotests.imgfmt, - '-c', 'write -P %d %d %d' % (i, i*1024*1024, num_kb * 1024), - self.imgs[img_index]) + '-c', 'write -P 0xFF %dk %dk' % (i * 512, num_kb), + self.imgs[odd_img_indexes[i]]) # Attach the drive to the VM self.vm = iotests.VM() @@ -318,12 +318,14 @@ class TestParallelOps(iotests.QMPTestCase): self.wait_until_completed(drive='commit-drive0') # Test a block-stream and a block-commit job in parallel - def test_stream_commit(self): + # Here the stream job is supposed to finish quickly in order to reproduce + # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507 + def test_stream_commit_1(self): self.assertLessEqual(8, self.num_imgs) self.assert_no_active_block_jobs() # Stream from node0 into node2 - result = self.vm.qmp('block-stream', device='node2', job_id='node2') + result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2') self.assert_qmp(result, 'return', {}) # Commit from the active layer into node3 @@ -348,6 +350,38 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() + # This is similar to test_stream_commit_1 but both jobs are slowed + # down so they can run in parallel for a little while. + def test_stream_commit_2(self): + self.assertLessEqual(8, self.num_imgs) + self.assert_no_active_block_jobs() + + # Stream from node0 into node4 + result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024) + self.assert_qmp(result, 'return', {}) + + # Commit from the active layer into node5 + result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024) + self.assert_qmp(result, 'return', {}) + + # Wait for all jobs to be finished. + pending_jobs = ['node4', 'drive0'] + while len(pending_jobs) > 0: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_COMPLETED': + node_name = self.dictpath(event, 'data/device') + self.assertTrue(node_name in pending_jobs) + self.assert_qmp_absent(event, 'data/error') + pending_jobs.remove(node_name) + if event['event'] == 'BLOCK_JOB_READY': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/type', 'commit') + self.assert_qmp_absent(event, 'data/error') + self.assertTrue('drive0' in pending_jobs) + self.vm.qmp('block-job-complete', device='drive0') + + self.assert_no_active_block_jobs() + # Test the base_node parameter def test_stream_base_node_name(self): self.assert_no_active_block_jobs() diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out index 391c857..42314e9 100644 --- a/tests/qemu-iotests/030.out +++ b/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -....................... +........................ ---------------------------------------------------------------------- -Ran 23 tests +Ran 24 tests OK diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 0032470..0871bff 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -166,11 +166,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' +qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' +qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check preallocation option == @@ -182,7 +182,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234 +qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16 == Check encryption option == @@ -205,7 +205,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16 *** done diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 40f89ea..530bbbe 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -152,9 +152,8 @@ done echo echo "=== Testing afl image with a very large capacity ===" _use_sample_img afl9.vmdk.bz2 -# The sed makes this test pass on machines with little RAM -# (and also with 32 bit builds) -_img_info | sed -e 's/Cannot allocate memory/Invalid argument/' +_img_info | grep -q 'Cannot allocate memory' && _notrun "Insufficent memory, skipped test" +_img_info _cleanup_test_img # success, all done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 1c2bd85..4dbe68e 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -171,12 +171,32 @@ poke_file "$TEST_IMG" "$offset_l2_table_0" "\x80\x00\x00\xff\xff\xff\x00\x00" { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir echo -echo "== Invalid snapshot L1 table ==" +echo "== Invalid snapshot L1 table offset ==" +_make_test_img 64M +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir +poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00" +{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ + -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir +_check_test_img + +echo +echo "== Invalid snapshot L1 table size ==" _make_test_img 64M { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00" { $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ + -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir +_check_test_img # success, all done echo "*** done" diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out index 6a7fda1..4e0f7f7 100644 --- a/tests/qemu-iotests/080.out +++ b/tests/qemu-iotests/080.out @@ -18,18 +18,18 @@ can't open device TEST_DIR/t.qcow2: Reference count table too large == Misaligned refcount table == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow2: Invalid reference count table offset +can't open device TEST_DIR/t.qcow2: Reference count table offset invalid == Huge refcount offset == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow2: Invalid reference count table offset +can't open device TEST_DIR/t.qcow2: Reference count table offset invalid == Invalid snapshot table == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow2: Too many snapshots -can't open device TEST_DIR/t.qcow2: Too many snapshots -can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset -can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset +can't open device TEST_DIR/t.qcow2: Snapshot table too large +can't open device TEST_DIR/t.qcow2: Snapshot table too large +can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid +can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid == Hitting snapshot table size limit == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -41,8 +41,8 @@ read 512/512 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 can't open device TEST_DIR/t.qcow2: Active L1 table too large can't open device TEST_DIR/t.qcow2: Active L1 table too large -can't open device TEST_DIR/t.qcow2: Invalid L1 table offset -can't open device TEST_DIR/t.qcow2: Invalid L1 table offset +can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid +can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid == Invalid L1 table (with internal snapshot in the image) == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -59,9 +59,49 @@ wrote 512/512 bytes at offset 0 qemu-img: Could not create snapshot 'test': -27 (File too large) qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable) -== Invalid snapshot L1 table == +== Invalid snapshot L1 table offset == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid +qemu-img: Snapshot L1 table offset invalid +qemu-img: Error while amending options: Invalid argument +Failed to flush the refcount block cache: Invalid argument +write failed: Invalid argument +qemu-img: Snapshot L1 table offset invalid +qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument +qemu-img: Could not delete snapshot 'test': Snapshot L1 table offset invalid +ERROR snapshot 1 (test) l1_offset=0x400200: L1 table is not cluster aligned; snapshot table entry corrupted +Leaked cluster 4 refcount=2 reference=1 +Leaked cluster 5 refcount=2 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. + +3 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +== Invalid snapshot L1 table size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table too large +qemu-img: Snapshot L1 table too large +qemu-img: Error while amending options: File too large +Failed to flush the refcount block cache: File too large +write failed: File too large +qemu-img: Snapshot L1 table too large +qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large +qemu-img: Could not delete snapshot 'test': Snapshot L1 table too large +ERROR snapshot 1 (test) l1_size=0x10000000: L1 table is too large; snapshot table entry corrupted +Leaked cluster 4 refcount=2 reference=1 +Leaked cluster 5 refcount=2 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. + +3 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. *** done diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 index aeeb375..aeeb375 100644..100755 --- a/tests/qemu-iotests/096 +++ b/tests/qemu-iotests/096 diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out index 81b04d1..86f0410 100644 --- a/tests/qemu-iotests/112.out +++ b/tests/qemu-iotests/112.out @@ -21,9 +21,9 @@ refcount bits: 16 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount bits: 16 -qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater) +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater) +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 === Snapshot limit on refcount_bits=1 === diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 8e76e62..8e76e62 100644..100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129 index 9e87e1c..9e87e1c 100644..100755 --- a/tests/qemu-iotests/129 +++ b/tests/qemu-iotests/129 diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132 index f53ef6e..f53ef6e 100644..100755 --- a/tests/qemu-iotests/132 +++ b/tests/qemu-iotests/132 diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index 88b97ea..88b97ea 100644..100755 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index cc7fe33..cc7fe33 100644..100755 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148 index e01b061..e01b061 100644..100755 --- a/tests/qemu-iotests/148 +++ b/tests/qemu-iotests/148 diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152 index fec546d..fec546d 100644..100755 --- a/tests/qemu-iotests/152 +++ b/tests/qemu-iotests/152 diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index fa25eb2..adfd026 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -32,6 +32,7 @@ _cleanup() { _cleanup_test_img rm -f "${TEST_IMG}.base" + rm -f "${TEST_IMG}.overlay" rm -f "${TEST_IMG}.convert" rm -f "${TEST_IMG}.a" rm -f "${TEST_IMG}.b" @@ -177,8 +178,6 @@ rm -f "${TEST_IMG}.lnk" &>/dev/null ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link" _run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}" -echo -echo "== Closing an image should unlock it ==" _launch_qemu _send_qemu_cmd $QEMU_HANDLE \ @@ -193,7 +192,10 @@ _send_qemu_cmd $QEMU_HANDLE \ _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' -echo "Closing drive" +echo "Creating overlay with qemu-img when the guest is running should be allowed" +_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" "${TEST_IMG}.overlay" + +echo "== Closing an image should unlock it ==" _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_del d0' } }" \ diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index 5b917b1..34309cf 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -372,15 +372,16 @@ Is another process using the image? == Symbolic link == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock Is another process using the image? - -== Closing an image should unlock it == {"return": {}} Adding drive _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock Is another process using the image? -Closing drive +Creating overlay with qemu-img when the guest is running should be allowed + +_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay +== Closing an image should unlock it == _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 Adding two and closing one diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index 4038423..4038423 100644..100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203 index 2c81191..4874a1a 100755 --- a/tests/qemu-iotests/203 +++ b/tests/qemu-iotests/203 @@ -49,11 +49,18 @@ with iotests.FilePath('disk0.img') as disk0_img_path, \ node_name='drive1-node', iothread='iothread0', force=True)) + iotests.log('Enabling migration QMP events...') + iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[ + { + 'capability': 'events', + 'state': True + } + ])) + iotests.log('Starting migration...') iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null')) while True: - vm.get_qmp_event(wait=60.0) - result = vm.qmp('query-migrate') - status = result.get('return', {}).get('status', None) - if status == 'completed': + event = vm.event_wait('MIGRATION') + iotests.log(event, filters=[iotests.filter_qmp_event]) + if event['data']['status'] == 'completed': break diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out index 3f1ff90..1a11f09 100644 --- a/tests/qemu-iotests/203.out +++ b/tests/qemu-iotests/203.out @@ -2,5 +2,10 @@ Launching VM... Setting IOThreads... {u'return': {}} {u'return': {}} +Enabling migration QMP events... +{u'return': {}} Starting migration... {u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index e7b2eae..e7b2eae 100644..100755 --- a/tests/qemu-iotests/205 +++ b/tests/qemu-iotests/205 diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 new file mode 100755 index 0000000..0a18b2b --- /dev/null +++ b/tests/qemu-iotests/206 @@ -0,0 +1,436 @@ +#!/bin/bash +# +# Test qcow2 and file image creation +# +# Copyright (C) 2018 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/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +size=$((128 * 1024 * 1024)) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0 + } +} +{ "execute": "blockdev-add", + "arguments": { + "driver": "file", + "node-name": "imgfile", + "filename": "$TEST_IMG" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "imgfile", + "size": $size + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (inline blockdev-add, explicit defaults) ===" +echo + +# Choose a different size to show that we got a new image +size=$((64 * 1024 * 1024)) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0, + "preallocation": "off", + "nocow": false + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "version": "v3", + "cluster-size": 65536, + "preallocation": "off", + "lazy-refcounts": false, + "refcount-bits": 16 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (v3 non-default options) ===" +echo + +# Choose a different size to show that we got a new image +size=$((32 * 1024 * 1024)) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0, + "preallocation": "falloc", + "nocow": true + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "version": "v3", + "cluster-size": 2097152, + "preallocation": "metadata", + "lazy-refcounts": true, + "refcount-bits": 1 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (v2 non-default options) ===" +echo + +mv $TEST_IMG $TEST_IMG.base + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "backing-file": "$TEST_IMG.base", + "backing-fmt": "qcow2", + "version": "v2", + "cluster-size": 512 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (encrypted) ===" +echo + +run_qemu -object secret,id=keysec0,data="foo" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "encrypt": { + "format": "luks", + "key-secret": "keysec0", + "cipher-alg": "twofish-128", + "cipher-mode": "ctr", + "ivgen-alg": "plain64", + "ivgen-hash-alg": "md5", + "hash-alg": "sha1", + "iter-time": 10 + } + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific | _filter_img_info --format-specific + +echo +echo "=== Invalid BlockdevRef ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "this doesn't exist", + "size": $size + } +} +{ "execute": "quit" } +EOF + + +echo +echo "=== Invalid sizes ===" +echo + +# TODO Negative image sizes aren't handled correctly, but this is a problem +# with QAPI's implementation of the 'size' type and affects other commands as +# well. Once this is fixed, we may want to add a test case here. + +# 1. Misaligned image size +# 2. 2^64 - 512 +# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) +# 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size) + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 1234 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 18446744073709551104 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 9223372036854775808 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 9223372036854775296 + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid version ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "version": "v1" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "version": "v2", + "lazy-refcounts": true + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "version": "v2", + "refcount-bits": 8 + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid backing file options ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "backing-file": "/dev/null", + "preallocation": "full" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "backing-fmt": "$IMGFMT" + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid cluster size ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 1234 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 128 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 4194304 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 281474976710656, + "cluster-size": 512 + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid refcount width ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "refcount-bits": 128 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "refcount-bits": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "refcount-bits": 7 + } +} +{ "execute": "quit" } +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out new file mode 100644 index 0000000..042342a --- /dev/null +++ b/tests/qemu-iotests/206.out @@ -0,0 +1,209 @@ +QA output created by 206 + +=== Successful image creation (defaults) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 128M (134217728 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + corrupt: false + +=== Successful image creation (inline blockdev-add, explicit defaults) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + corrupt: false + +=== Successful image creation (v3 non-default options) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 32M (33554432 bytes) +cluster_size: 2097152 +Format specific information: + compat: 1.1 + lazy refcounts: true + refcount bits: 1 + corrupt: false + +=== Successful image creation (v2 non-default options) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 32M (33554432 bytes) +cluster_size: 512 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +Format specific information: + compat: 0.10 + refcount bits: 16 + +=== Successful image creation (encrypted) === + +Testing: -object secret,id=keysec0,data=foo +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 32M (33554432 bytes) +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + encrypt: + ivgen alg: plain64 + hash alg: sha1 + cipher alg: twofish-128 + uuid: 00000000-0000-0000-0000-000000000000 + format: luks + cipher mode: ctr + slots: + [0]: + active: true + iters: 1024 + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 69632 + [2]: + active: false + key offset: 135168 + [3]: + active: false + key offset: 200704 + [4]: + active: false + key offset: 266240 + [5]: + active: false + key offset: 331776 + [6]: + active: false + key offset: 397312 + [7]: + active: false + key offset: 462848 + payload offset: 528384 + master key iters: 1024 + corrupt: false + +=== Invalid BlockdevRef === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid sizes === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid version === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} +{"error": {"class": "GenericError", "desc": "Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)"}} +{"error": {"class": "GenericError", "desc": "Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid backing file options === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Backing file and preallocation cannot be used at the same time"}} +{"error": {"class": "GenericError", "desc": "Backing format cannot be used without backing file"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid cluster size === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid refcount width === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} +{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} +{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +*** done diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 new file mode 100755 index 0000000..f5c7785 --- /dev/null +++ b/tests/qemu-iotests/207 @@ -0,0 +1,261 @@ +#!/bin/bash +# +# Test ssh image creation +# +# Copyright (C) 2018 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/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto ssh +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info +echo +TEST_IMG=$TEST_IMG_FILE _img_info | _filter_img_info + +echo +echo "=== Test host-key-check options ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "none" + } + }, + "size": 8388608 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "known_hosts" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + + +key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | + cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "md5", + "hash": "wrong" + } + }, + "size": 8388608 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "md5", + "hash": "$key" + } + }, + "size": 8388608 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + + +key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | + cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "sha1", + "hash": "wrong" + } + }, + "size": 4194304 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "sha1", + "hash": "$key" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + +echo +echo "=== Invalid path and user ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "/this/is/not/an/existing/path", + "server": { + "host": "127.0.0.1", + "port": "22" + } + }, + "size": 4194304 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "user": "invalid user", + "server": { + "host": "127.0.0.1", + "port": "22" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out new file mode 100644 index 0000000..417deee --- /dev/null +++ b/tests/qemu-iotests/207.out @@ -0,0 +1,75 @@ +QA output created by 207 + +=== Successful image creation (defaults) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) + +=== Test host-key-check options === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 8.0M (8388608 bytes) +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 8.0M (8388608 bytes) +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) + +=== Invalid path and user === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}} +{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index a2dfe79..c401791 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -202,3 +202,5 @@ 203 rw auto 204 rw auto quick 205 rw auto quick +206 rw auto +207 rw auto diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 5d5a3da..2c422ab 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "qemu/option_int.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -23,6 +24,8 @@ static QemuOptsList opts_list_01 = { { .name = "str1", .type = QEMU_OPT_STRING, + .help = "Help texts are preserved in qemu_opts_append", + .def_value_str = "default", },{ .name = "str2", .type = QEMU_OPT_STRING, @@ -32,6 +35,7 @@ static QemuOptsList opts_list_01 = { },{ .name = "number1", .type = QEMU_OPT_NUMBER, + .help = "Having help texts only for some options is okay", },{ .name = "number2", .type = QEMU_OPT_NUMBER, @@ -743,6 +747,250 @@ static void test_opts_parse_size(void) qemu_opts_reset(&opts_list_02); } +static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping) +{ + int i = 0; + + if (with_overlapping) { + g_assert_cmpstr(desc[i].name, ==, "str1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, + "Help texts are preserved in qemu_opts_append"); + g_assert_cmpstr(desc[i].def_value_str, ==, "default"); + i++; + + g_assert_cmpstr(desc[i].name, ==, "str2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + } + + g_assert_cmpstr(desc[i].name, ==, "str3"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "number1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); + g_assert_cmpstr(desc[i].help, ==, + "Having help texts only for some options is okay"); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "number2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, NULL); +} + +static void append_verify_list_02(QemuOptDesc *desc) +{ + int i = 0; + + g_assert_cmpstr(desc[i].name, ==, "str1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "str2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "bool1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "bool2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size3"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); +} + +static void test_opts_append_to_null(void) +{ + QemuOptsList *merged; + + merged = qemu_opts_append(NULL, &opts_list_01); + g_assert(merged != &opts_list_01); + + g_assert_cmpstr(merged->name, ==, NULL); + g_assert_cmpstr(merged->implied_opt_name, ==, NULL); + g_assert_false(merged->merge_lists); + + append_verify_list_01(merged->desc, true); + + qemu_opts_free(merged); +} + +static void test_opts_append(void) +{ + QemuOptsList *first, *merged; + + first = qemu_opts_append(NULL, &opts_list_02); + merged = qemu_opts_append(first, &opts_list_01); + g_assert(first != &opts_list_02); + g_assert(merged != &opts_list_01); + + g_assert_cmpstr(merged->name, ==, NULL); + g_assert_cmpstr(merged->implied_opt_name, ==, NULL); + g_assert_false(merged->merge_lists); + + append_verify_list_02(&merged->desc[0]); + append_verify_list_01(&merged->desc[7], false); + + qemu_opts_free(merged); +} + +static void test_opts_to_qdict_basic(void) +{ + QemuOpts *opts; + QDict *dict; + + opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42", + false, &error_abort); + g_assert(opts != NULL); + + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + + QDECREF(dict); + qemu_opts_del(opts); +} + +static void test_opts_to_qdict_filtered(void) +{ + QemuOptsList *first, *merged; + QemuOpts *opts; + QDict *dict; + + first = qemu_opts_append(NULL, &opts_list_02); + merged = qemu_opts_append(first, &opts_list_01); + + opts = qemu_opts_parse(merged, + "str1=foo,str2=,str3=bar,bool1=off,number1=42", + false, &error_abort); + g_assert(opts != NULL); + + /* Convert to QDict without deleting from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); + QDECREF(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); + QDECREF(dict); + + /* Now delete converted options from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); + QDECREF(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); + g_assert_false(qdict_haskey(dict, "str1")); + g_assert_false(qdict_haskey(dict, "str2")); + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); + QDECREF(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + + qemu_opts_del(opts); + qemu_opts_free(merged); +} + +static void test_opts_to_qdict_duplicates(void) +{ + QemuOpts *opts; + QemuOpt *opt; + QDict *dict; + + opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort); + g_assert(opts != NULL); + + /* Verify that opts has two options with the same name */ + opt = QTAILQ_FIRST(&opts->head); + g_assert_cmpstr(opt->name, ==, "foo"); + g_assert_cmpstr(opt->str , ==, "a"); + + opt = QTAILQ_NEXT(opt, next); + g_assert_cmpstr(opt->name, ==, "foo"); + g_assert_cmpstr(opt->str , ==, "b"); + + opt = QTAILQ_NEXT(opt, next); + g_assert(opt == NULL); + + /* In the conversion to QDict, the last one wins */ + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); + QDECREF(dict); + + /* The last one still wins if entries are deleted, and both are deleted */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); + QDECREF(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + + qemu_opts_del(opts); +} + int main(int argc, char *argv[]) { register_opts(); @@ -761,6 +1009,11 @@ int main(int argc, char *argv[]) g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size); + g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null); + g_test_add_func("/qemu-opts/append", test_opts_append); + g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic); + g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered); + g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates); g_test_run(); return 0; } |