#!/usr/bin/env python3 # # Very specific tests for adjacent commit/stream block jobs # # Copyright (C) 2019 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 . # # Creator/Owner: Max Reitz import iotests from iotests import log, qemu_img, qemu_io_silent, \ filter_qmp_testfiles, filter_qmp_imgfmt # Need backing file and change-backing-file support iotests.verify_image_format(supported_fmts=['qcow2', 'qed']) iotests.verify_platform(['linux']) # Returns a node for blockdev-add def node(node_name, path, backing=None, fmt=None, throttle=None): if fmt is None: fmt = iotests.imgfmt res = { 'node-name': node_name, 'driver': fmt, 'file': { 'driver': 'file', 'filename': path } } if backing is not None: res['backing'] = backing if throttle: res['file'] = { 'driver': 'throttle', 'throttle-group': throttle, 'file': res['file'] } return res # Finds a node in the debug block graph def find_graph_node(graph, node_id): return next(node for node in graph['nodes'] if node['id'] == node_id) def test_concurrent_finish(write_to_stream_node): log('') log('=== Commit and stream finish concurrently (letting %s write) ===' % \ ('stream' if write_to_stream_node else 'commit')) log('') # All chosen in such a way that when the commit job wants to # finish, it polls and thus makes stream finish concurrently -- # and the other way around, depending on whether the commit job # is finalized before stream completes or not. with iotests.FilePath('node4.img') as node4_path, \ iotests.FilePath('node3.img') as node3_path, \ iotests.FilePath('node2.img') as node2_path, \ iotests.FilePath('node1.img') as node1_path, \ iotests.FilePath('node0.img') as node0_path, \ iotests.VM() as vm: # It is important to use raw for the base layer (so that # permissions are just handed through to the protocol layer) assert qemu_img('create', '-f', 'raw', node0_path, '64M') == 0 stream_throttle=None commit_throttle=None for path in [node1_path, node2_path, node3_path, node4_path]: assert qemu_img('create', '-f', iotests.imgfmt, path, '64M') == 0 if write_to_stream_node: # This is what (most of the time) makes commit finish # earlier and then pull in stream assert qemu_io_silent(node2_path, '-c', 'write %iK 64K' % (65536 - 192), '-c', 'write %iK 64K' % (65536 - 64)) == 0 stream_throttle='tg' else: # And this makes stream finish earlier assert qemu_io_silent(node1_path, '-c', 'write %iK 64K' % (65536 - 64)) == 0 commit_throttle='tg' vm.launch() vm.qmp_log('object-add', qom_type='throttle-group', id='tg', props={ 'x-iops-write': 1, 'x-iops-write-max': 1 }) vm.qmp_log('blockdev-add', filters=[filter_qmp_testfiles, filter_qmp_imgfmt], **node('node4', node4_path, throttle=stream_throttle, backing=node('node3', node3_path, backing=node('node2', node2_path, backing=node('node1', node1_path, backing=node('node0', node0_path, throttle=commit_throttle, fmt='raw')))))) vm.qmp_log('block-commit', job_id='commit', device='node4', filter_node_name='commit-filter', top_node='node1', base_node='node0', auto_finalize=False) vm.qmp_log('block-stream', job_id='stream', device='node3', base_node='commit-filter') if write_to_stream_node: vm.run_job('commit', auto_finalize=False, auto_dismiss=True) vm.run_job('stream', auto_finalize=True, auto_dismiss=True) else: # No, the jobs do not really finish concurrently here, # the stream job does complete strictly before commit. # But still, this is close enough for what we want to # test. vm.run_job('stream', auto_finalize=True, auto_dismiss=True) vm.run_job('commit', auto_finalize=False, auto_dismiss=True) # Assert that the backing node of node3 is node 0 now graph = vm.qmp('x-debug-query-block-graph')['return'] for edge in graph['edges']: if edge['name'] == 'backing' and \ find_graph_node(graph, edge['parent'])['name'] == 'node3': assert find_graph_node(graph, edge['child'])['name'] == 'node0' break def main(): log('Running tests:') test_concurrent_finish(True) test_concurrent_finish(False) if __name__ == '__main__': main()