From 7229e121fd3e299192b03f1a9b59c946e20ed627 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 3 Jul 2019 19:28:08 +0200 Subject: iotests: Fix throttling in 030 Currently, TestParallelOps in 030 creates images that are too small for job throttling to be effective. This is reflected by the fact that it never undoes the throttling. Increase the image size and undo the throttling when the job should be completed. Also, add throttling in test_overlapping_4, or the jobs may not be so overlapping after all. In fact, the error usually emitted here is that node2 simply does not exist, not that overlapping jobs are not allowed -- the fact that this job ignores the exact error messages and just checks the error class is something that should be fixed in a follow-up patch. Signed-off-by: Max Reitz Tested-by: Andrey Shinkevich Reviewed-by: Alberto Garcia Message-id: 20190703172813.6868-8-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/030 | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index c6311d1..2cf8d54 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -154,7 +154,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 * 512 * 1024 + image_len = num_ops * 4 * 1024 * 1024 imgs = [] def setUp(self): @@ -176,11 +176,11 @@ class TestParallelOps(iotests.QMPTestCase): # Put data into the images we are copying data from 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. + # Alternate between 2MB and 4MB. # This way jobs will not finish in the same order they were created - num_kb = 256 + 256 * (i % 2) + num_mb = 2 + 2 * (i % 2) qemu_io('-f', iotests.imgfmt, - '-c', 'write -P 0xFF %dk %dk' % (i * 512, num_kb), + '-c', 'write -P 0xFF %dM %dM' % (i * 4, num_mb), self.imgs[odd_img_indexes[i]]) # Attach the drive to the VM @@ -213,6 +213,10 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=512*1024) self.assert_qmp(result, 'return', {}) + for job in pending_jobs: + result = self.vm.qmp('block-job-set-speed', device=job, speed=0) + self.assert_qmp(result, 'return', {}) + # Wait for all jobs to be finished. while len(pending_jobs) > 0: for event in self.vm.get_qmp_events(wait=True): @@ -260,6 +264,9 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[0], top=self.imgs[1], job_id='commit-node0') self.assert_qmp(result, 'error/class', 'GenericError') + result = self.vm.qmp('block-job-set-speed', device='stream-node4', speed=0) + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(drive='stream-node4') self.assert_no_active_block_jobs() @@ -289,6 +296,9 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='drive0', base=self.imgs[5], job_id='stream-drive0') self.assert_qmp(result, 'error/class', 'GenericError') + result = self.vm.qmp('block-job-set-speed', device='commit-node3', speed=0) + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(drive='commit-node3') # Similar to test_overlapping_2, but here block-commit doesn't use the 'top' parameter. @@ -309,6 +319,9 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') + result = self.vm.qmp('block-job-set-speed', device='commit-drive0', speed=0) + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('block-job-complete', device='commit-drive0') self.assert_qmp(result, 'return', {}) @@ -321,13 +334,18 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() # Commit from node2 into node0 - result = self.vm.qmp('block-commit', device='drive0', top=self.imgs[2], base=self.imgs[0]) + result = self.vm.qmp('block-commit', device='drive0', + top=self.imgs[2], base=self.imgs[0], + speed=1024*1024) self.assert_qmp(result, 'return', {}) # Stream from node2 into node4 result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='node4') self.assert_qmp(result, 'error/class', 'GenericError') + result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) + self.assert_qmp(result, 'return', {}) + self.wait_until_completed() self.assert_no_active_block_jobs() @@ -378,6 +396,10 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024) self.assert_qmp(result, 'return', {}) + for job in ['drive0', 'node4']: + result = self.vm.qmp('block-job-set-speed', device=job, speed=0) + self.assert_qmp(result, 'return', {}) + # Wait for all jobs to be finished. pending_jobs = ['node4', 'drive0'] while len(pending_jobs) > 0: -- cgit v1.1 From 3f92d54c00ad707cb43c027a5dfc2a68510176f9 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 3 Jul 2019 19:28:09 +0200 Subject: iotests: Compare error messages in 030 Currently, 030 just compares the error class, which does not say anything. Before HEAD^ added throttling to test_overlapping_4, that test actually usually failed because node2 was already gone, not because it was the commit and stream job were not allowed to overlap. Prevent such problems in the future by comparing the error description instead. Signed-off-by: Max Reitz Tested-by: Andrey Shinkevich Reviewed-by: Alberto Garcia Message-id: 20190703172813.6868-9-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/030 | 66 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 2cf8d54..10fe1de 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -144,11 +144,12 @@ class TestSingleDrive(iotests.QMPTestCase): def test_device_not_found(self): result = self.vm.qmp('block-stream', device='nonexistent') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + 'Cannot find device=nonexistent nor node_name=nonexistent') def test_job_id_missing(self): result = self.vm.qmp('block-stream', device='mid') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Invalid job ID ''") class TestParallelOps(iotests.QMPTestCase): @@ -245,24 +246,30 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-stream', device='node5', job_id='stream-node5', base=self.imgs[2]) - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node4' is busy: block device is in use by block job: stream") result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3', base=self.imgs[2]) - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node3' is busy: block device is in use by block job: stream") result = self.vm.qmp('block-stream', device='node4', job_id='stream-node4-v2') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node4' is busy: block device is in use by block job: stream") # block-commit should also fail if it touches nodes used by the stream job result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[4], job_id='commit-node4') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node4' is busy: block device is in use by block job: stream") result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[1], top=self.imgs[3], job_id='commit-node1') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node3' is busy: block device is in use by block job: stream") # This fails because it needs to modify the backing string in node2, which is blocked result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[0], top=self.imgs[1], job_id='commit-node0') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node2' is busy: block device is in use by block job: stream") result = self.vm.qmp('block-job-set-speed', device='stream-node4', speed=0) self.assert_qmp(result, 'return', {}) @@ -281,20 +288,25 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-stream', device='node3', job_id='stream-node3') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node3' is busy: block device is in use by block job: commit") result = self.vm.qmp('block-stream', device='node6', base=self.imgs[2], job_id='stream-node6') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node5' is busy: block device is in use by block job: commit") result = self.vm.qmp('block-stream', device='node4', base=self.imgs[2], job_id='stream-node4') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node4' is busy: block device is in use by block job: commit") result = self.vm.qmp('block-stream', device='node6', base=self.imgs[4], job_id='stream-node6-v2') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node5' is busy: block device is in use by block job: commit") # This fails because block-commit currently blocks the active layer even if it's not used result = self.vm.qmp('block-stream', device='drive0', base=self.imgs[5], job_id='stream-drive0') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'drive0' is busy: block device is in use by block job: commit") result = self.vm.qmp('block-job-set-speed', device='commit-node3', speed=0) self.assert_qmp(result, 'return', {}) @@ -312,7 +324,8 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node5' is busy: block device is in use by block job: commit") event = self.vm.event_wait(name='BLOCK_JOB_READY') self.assert_qmp(event, 'data/device', 'commit-drive0') @@ -328,20 +341,21 @@ class TestParallelOps(iotests.QMPTestCase): self.wait_until_completed(drive='commit-drive0') # In this case the base node of the stream job is the same as the - # top node of commit job. Since block-commit removes the top node - # when it finishes, this is not allowed. + # top node of commit job. Since this results in the commit filter + # node being part of the stream chain, this is not allowed. def test_overlapping_4(self): self.assert_no_active_block_jobs() # Commit from node2 into node0 result = self.vm.qmp('block-commit', device='drive0', top=self.imgs[2], base=self.imgs[0], - speed=1024*1024) + filter_node_name='commit-filter', speed=1024*1024) self.assert_qmp(result, 'return', {}) # Stream from node2 into node4 result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='node4') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Cannot freeze 'backing' link to 'commit-filter'") result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) self.assert_qmp(result, 'return', {}) @@ -428,19 +442,23 @@ class TestParallelOps(iotests.QMPTestCase): # Error: the base node does not exist result = self.vm.qmp('block-stream', device='node4', base_node='none', job_id='stream') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + 'Cannot find device= nor node_name=none') # Error: the base node is not a backing file of the top node result = self.vm.qmp('block-stream', device='node4', base_node='node6', job_id='stream') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node6' is not a backing image of 'node4'") # Error: the base node is the same as the top node result = self.vm.qmp('block-stream', device='node4', base_node='node4', job_id='stream') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "Node 'node4' is not a backing image of 'node4'") # Error: cannot specify 'base' and 'base-node' at the same time result = self.vm.qmp('block-stream', device='node4', base=self.imgs[2], base_node='node2', job_id='stream') - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', + "'base' and 'base-node' cannot be specified at the same time") # Success: the base node is a backing file of the top node result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='stream') @@ -873,7 +891,7 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_no_active_block_jobs() result = self.vm.qmp('block-stream', device='drive0', speed=-1) - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Invalid parameter 'speed'") self.assert_no_active_block_jobs() @@ -882,7 +900,7 @@ class TestSetSpeed(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) - self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Invalid parameter 'speed'") self.cancel_and_wait(resume=True) -- cgit v1.1 From 15427f63bccfaa0735612c30dcbc08e1f8deb17d Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 3 Jul 2019 19:28:10 +0200 Subject: iotests: Add @use_log to VM.run_job() unittest-style tests generally do not use the log file, but VM.run_job() can still be useful to them. Add a parameter to it that hides its output from the log file. Signed-off-by: Max Reitz Message-id: 20190703172813.6868-10-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/iotests.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 3ecef5b..ce74177 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -542,7 +542,7 @@ class VM(qtest.QEMUQtestMachine): # Returns None on success, and an error string on failure def run_job(self, job, auto_finalize=True, auto_dismiss=False, - pre_finalize=None, wait=60.0): + pre_finalize=None, use_log=True, wait=60.0): match_device = {'data': {'device': job}} match_id = {'data': {'id': job}} events = [ @@ -557,7 +557,8 @@ class VM(qtest.QEMUQtestMachine): while True: ev = filter_qmp_event(self.events_wait(events)) if ev['event'] != 'JOB_STATUS_CHANGE': - log(ev) + if use_log: + log(ev) continue status = ev['data']['status'] if status == 'aborting': @@ -565,13 +566,20 @@ class VM(qtest.QEMUQtestMachine): for j in result['return']: if j['id'] == job: error = j['error'] - log('Job failed: %s' % (j['error'])) + if use_log: + log('Job failed: %s' % (j['error'])) elif status == 'pending' and not auto_finalize: if pre_finalize: pre_finalize() - self.qmp_log('job-finalize', id=job) + if use_log: + self.qmp_log('job-finalize', id=job) + else: + self.qmp('job-finalize', id=job) elif status == 'concluded' and not auto_dismiss: - self.qmp_log('job-dismiss', id=job) + if use_log: + self.qmp_log('job-dismiss', id=job) + else: + self.qmp('job-dismiss', id=job) elif status == 'null': return error -- cgit v1.1 From 13658cd70bf0dd7c39b103aedc94d43371278226 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 3 Jul 2019 19:28:11 +0200 Subject: iotests: Add new case to 030 We recently removed the dependency of the stream job on its base node. That makes it OK to use a commit filter node there. Test that. Signed-off-by: Max Reitz Tested-by: Andrey Shinkevich Reviewed-by: Alberto Garcia Message-id: 20190703172813.6868-11-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/030 | 25 +++++++++++++++++++++++++ tests/qemu-iotests/030.out | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 10fe1de..a039707 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -363,6 +363,31 @@ class TestParallelOps(iotests.QMPTestCase): self.wait_until_completed() self.assert_no_active_block_jobs() + # In this case the base node of the stream job is the commit job's + # filter node. stream does not have a real dependency on its base + # node, so even though commit removes it when it is done, there is + # no conflict. + def test_overlapping_5(self): + self.assert_no_active_block_jobs() + + # Commit from node2 into node0 + result = self.vm.qmp('block-commit', device='drive0', + top_node='node2', base_node='node0', + filter_node_name='commit-filter', speed=1024*1024) + self.assert_qmp(result, 'return', {}) + + # Stream from node2 into node4 + result = self.vm.qmp('block-stream', device='node4', + base_node='commit-filter', job_id='node4') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0) + self.assert_qmp(result, 'return', {}) + + self.vm.run_job(job='drive0', auto_dismiss=True, use_log=False) + self.vm.run_job(job='node4', auto_dismiss=True, use_log=False) + self.assert_no_active_block_jobs() + # Test a block-stream and a block-commit job in parallel # Here the stream job is supposed to finish quickly in order to reproduce # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507 diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out index 4fd1c2d..5eb508d 100644 --- a/tests/qemu-iotests/030.out +++ b/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -......................... +.......................... ---------------------------------------------------------------------- -Ran 25 tests +Ran 26 tests OK -- cgit v1.1 From 0e4a0644bf18b6aab136f926b0e63bc24db6bdfe Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Wed, 3 Jul 2019 19:28:12 +0200 Subject: iotests: Add read-only test case to 030 This tests that the stream job exits cleanly (without abort) when the top node is read-only and cannot be reopened read/write. Signed-off-by: Max Reitz Message-id: 20190703172813.6868-12-mreitz@redhat.com Signed-off-by: Max Reitz --- tests/qemu-iotests/030 | 29 ++++++++++++++++++++++++++++- tests/qemu-iotests/030.out | 4 ++-- 2 files changed, 30 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index a039707..1b69f31 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -36,7 +36,9 @@ class TestSingleDrive(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 512', backing_img) qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 524288 512', mid_img) - self.vm = iotests.VM().add_drive("blkdebug::" + test_img, "backing.node-name=mid") + self.vm = iotests.VM().add_drive("blkdebug::" + test_img, + "backing.node-name=mid," + + "backing.backing.node-name=base") self.vm.launch() def tearDown(self): @@ -151,6 +153,31 @@ class TestSingleDrive(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='mid') self.assert_qmp(result, 'error/desc', "Invalid job ID ''") + def test_read_only(self): + # Create a new file that we can attach (we need a read-only top) + with iotests.FilePath('ro-top.img') as ro_top_path: + qemu_img('create', '-f', iotests.imgfmt, ro_top_path, + str(self.image_len)) + + result = self.vm.qmp('blockdev-add', + node_name='ro-top', + driver=iotests.imgfmt, + read_only=True, + file={ + 'driver': 'file', + 'filename': ro_top_path, + 'read-only': True + }, + backing='mid') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('block-stream', job_id='stream', + device='ro-top', base_node='base') + self.assert_qmp(result, 'error/desc', 'Block node is read-only') + + result = self.vm.qmp('blockdev-del', node_name='ro-top') + self.assert_qmp(result, 'return', {}) + class TestParallelOps(iotests.QMPTestCase): num_ops = 4 # Number of parallel block-stream operations diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out index 5eb508d..6d9bee1 100644 --- a/tests/qemu-iotests/030.out +++ b/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -.......................... +........................... ---------------------------------------------------------------------- -Ran 26 tests +Ran 27 tests OK -- cgit v1.1