1*903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 248057fc2SMax Reitz# 348057fc2SMax Reitz# Very specific tests for adjacent commit/stream block jobs 448057fc2SMax Reitz# 548057fc2SMax Reitz# Copyright (C) 2019 Red Hat, Inc. 648057fc2SMax Reitz# 748057fc2SMax Reitz# This program is free software; you can redistribute it and/or modify 848057fc2SMax Reitz# it under the terms of the GNU General Public License as published by 948057fc2SMax Reitz# the Free Software Foundation; either version 2 of the License, or 1048057fc2SMax Reitz# (at your option) any later version. 1148057fc2SMax Reitz# 1248057fc2SMax Reitz# This program is distributed in the hope that it will be useful, 1348057fc2SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 1448057fc2SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1548057fc2SMax Reitz# GNU General Public License for more details. 1648057fc2SMax Reitz# 1748057fc2SMax Reitz# You should have received a copy of the GNU General Public License 1848057fc2SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 1948057fc2SMax Reitz# 2048057fc2SMax Reitz# Creator/Owner: Max Reitz <mreitz@redhat.com> 2148057fc2SMax Reitz 2248057fc2SMax Reitzimport iotests 2348057fc2SMax Reitzfrom iotests import log, qemu_img, qemu_io_silent, \ 2448057fc2SMax Reitz filter_qmp_testfiles, filter_qmp_imgfmt 2548057fc2SMax Reitz 2648057fc2SMax Reitz# Need backing file and change-backing-file support 2748057fc2SMax Reitziotests.verify_image_format(supported_fmts=['qcow2', 'qed']) 2848057fc2SMax Reitziotests.verify_platform(['linux']) 2948057fc2SMax Reitz 3048057fc2SMax Reitz 3148057fc2SMax Reitz# Returns a node for blockdev-add 3248057fc2SMax Reitzdef node(node_name, path, backing=None, fmt=None, throttle=None): 3348057fc2SMax Reitz if fmt is None: 3448057fc2SMax Reitz fmt = iotests.imgfmt 3548057fc2SMax Reitz 3648057fc2SMax Reitz res = { 3748057fc2SMax Reitz 'node-name': node_name, 3848057fc2SMax Reitz 'driver': fmt, 3948057fc2SMax Reitz 'file': { 4048057fc2SMax Reitz 'driver': 'file', 4148057fc2SMax Reitz 'filename': path 4248057fc2SMax Reitz } 4348057fc2SMax Reitz } 4448057fc2SMax Reitz 4548057fc2SMax Reitz if backing is not None: 4648057fc2SMax Reitz res['backing'] = backing 4748057fc2SMax Reitz 4848057fc2SMax Reitz if throttle: 4948057fc2SMax Reitz res['file'] = { 5048057fc2SMax Reitz 'driver': 'throttle', 5148057fc2SMax Reitz 'throttle-group': throttle, 5248057fc2SMax Reitz 'file': res['file'] 5348057fc2SMax Reitz } 5448057fc2SMax Reitz 5548057fc2SMax Reitz return res 5648057fc2SMax Reitz 5748057fc2SMax Reitz# Finds a node in the debug block graph 5848057fc2SMax Reitzdef find_graph_node(graph, node_id): 5948057fc2SMax Reitz return next(node for node in graph['nodes'] if node['id'] == node_id) 6048057fc2SMax Reitz 6148057fc2SMax Reitz 6248057fc2SMax Reitzdef test_concurrent_finish(write_to_stream_node): 6348057fc2SMax Reitz log('') 6448057fc2SMax Reitz log('=== Commit and stream finish concurrently (letting %s write) ===' % \ 6548057fc2SMax Reitz ('stream' if write_to_stream_node else 'commit')) 6648057fc2SMax Reitz log('') 6748057fc2SMax Reitz 6848057fc2SMax Reitz # All chosen in such a way that when the commit job wants to 6948057fc2SMax Reitz # finish, it polls and thus makes stream finish concurrently -- 7048057fc2SMax Reitz # and the other way around, depending on whether the commit job 7148057fc2SMax Reitz # is finalized before stream completes or not. 7248057fc2SMax Reitz 7348057fc2SMax Reitz with iotests.FilePath('node4.img') as node4_path, \ 7448057fc2SMax Reitz iotests.FilePath('node3.img') as node3_path, \ 7548057fc2SMax Reitz iotests.FilePath('node2.img') as node2_path, \ 7648057fc2SMax Reitz iotests.FilePath('node1.img') as node1_path, \ 7748057fc2SMax Reitz iotests.FilePath('node0.img') as node0_path, \ 7848057fc2SMax Reitz iotests.VM() as vm: 7948057fc2SMax Reitz 8048057fc2SMax Reitz # It is important to use raw for the base layer (so that 8148057fc2SMax Reitz # permissions are just handed through to the protocol layer) 8248057fc2SMax Reitz assert qemu_img('create', '-f', 'raw', node0_path, '64M') == 0 8348057fc2SMax Reitz 8448057fc2SMax Reitz stream_throttle=None 8548057fc2SMax Reitz commit_throttle=None 8648057fc2SMax Reitz 8748057fc2SMax Reitz for path in [node1_path, node2_path, node3_path, node4_path]: 8848057fc2SMax Reitz assert qemu_img('create', '-f', iotests.imgfmt, path, '64M') == 0 8948057fc2SMax Reitz 9048057fc2SMax Reitz if write_to_stream_node: 9148057fc2SMax Reitz # This is what (most of the time) makes commit finish 9248057fc2SMax Reitz # earlier and then pull in stream 9348057fc2SMax Reitz assert qemu_io_silent(node2_path, 9448057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 192), 9548057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 64)) == 0 9648057fc2SMax Reitz 9748057fc2SMax Reitz stream_throttle='tg' 9848057fc2SMax Reitz else: 9948057fc2SMax Reitz # And this makes stream finish earlier 10048057fc2SMax Reitz assert qemu_io_silent(node1_path, 10148057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 64)) == 0 10248057fc2SMax Reitz 10348057fc2SMax Reitz commit_throttle='tg' 10448057fc2SMax Reitz 10548057fc2SMax Reitz vm.launch() 10648057fc2SMax Reitz 10748057fc2SMax Reitz vm.qmp_log('object-add', 10848057fc2SMax Reitz qom_type='throttle-group', 10948057fc2SMax Reitz id='tg', 11048057fc2SMax Reitz props={ 11148057fc2SMax Reitz 'x-iops-write': 1, 11248057fc2SMax Reitz 'x-iops-write-max': 1 11348057fc2SMax Reitz }) 11448057fc2SMax Reitz 11548057fc2SMax Reitz vm.qmp_log('blockdev-add', 11648057fc2SMax Reitz filters=[filter_qmp_testfiles, filter_qmp_imgfmt], 11748057fc2SMax Reitz **node('node4', node4_path, throttle=stream_throttle, 11848057fc2SMax Reitz backing=node('node3', node3_path, 11948057fc2SMax Reitz backing=node('node2', node2_path, 12048057fc2SMax Reitz backing=node('node1', node1_path, 12148057fc2SMax Reitz backing=node('node0', node0_path, throttle=commit_throttle, 12248057fc2SMax Reitz fmt='raw')))))) 12348057fc2SMax Reitz 12448057fc2SMax Reitz vm.qmp_log('block-commit', 12548057fc2SMax Reitz job_id='commit', 12648057fc2SMax Reitz device='node4', 12748057fc2SMax Reitz filter_node_name='commit-filter', 12848057fc2SMax Reitz top_node='node1', 12948057fc2SMax Reitz base_node='node0', 13048057fc2SMax Reitz auto_finalize=False) 13148057fc2SMax Reitz 13248057fc2SMax Reitz vm.qmp_log('block-stream', 13348057fc2SMax Reitz job_id='stream', 13448057fc2SMax Reitz device='node3', 13548057fc2SMax Reitz base_node='commit-filter') 13648057fc2SMax Reitz 13748057fc2SMax Reitz if write_to_stream_node: 13848057fc2SMax Reitz vm.run_job('commit', auto_finalize=False, auto_dismiss=True) 13948057fc2SMax Reitz vm.run_job('stream', auto_finalize=True, auto_dismiss=True) 14048057fc2SMax Reitz else: 14148057fc2SMax Reitz # No, the jobs do not really finish concurrently here, 14248057fc2SMax Reitz # the stream job does complete strictly before commit. 14348057fc2SMax Reitz # But still, this is close enough for what we want to 14448057fc2SMax Reitz # test. 14548057fc2SMax Reitz vm.run_job('stream', auto_finalize=True, auto_dismiss=True) 14648057fc2SMax Reitz vm.run_job('commit', auto_finalize=False, auto_dismiss=True) 14748057fc2SMax Reitz 14848057fc2SMax Reitz # Assert that the backing node of node3 is node 0 now 14948057fc2SMax Reitz graph = vm.qmp('x-debug-query-block-graph')['return'] 15048057fc2SMax Reitz for edge in graph['edges']: 15148057fc2SMax Reitz if edge['name'] == 'backing' and \ 15248057fc2SMax Reitz find_graph_node(graph, edge['parent'])['name'] == 'node3': 15348057fc2SMax Reitz assert find_graph_node(graph, edge['child'])['name'] == 'node0' 15448057fc2SMax Reitz break 15548057fc2SMax Reitz 15648057fc2SMax Reitz 15748057fc2SMax Reitzdef main(): 15848057fc2SMax Reitz log('Running tests:') 15948057fc2SMax Reitz test_concurrent_finish(True) 16048057fc2SMax Reitz test_concurrent_finish(False) 16148057fc2SMax Reitz 16248057fc2SMax Reitzif __name__ == '__main__': 16348057fc2SMax Reitz main() 164