1903cb1bfSPhilippe 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# Returns a node for blockdev-add 2748057fc2SMax Reitzdef node(node_name, path, backing=None, fmt=None, throttle=None): 2848057fc2SMax Reitz if fmt is None: 2948057fc2SMax Reitz fmt = iotests.imgfmt 3048057fc2SMax Reitz 3148057fc2SMax Reitz res = { 3248057fc2SMax Reitz 'node-name': node_name, 3348057fc2SMax Reitz 'driver': fmt, 3448057fc2SMax Reitz 'file': { 3548057fc2SMax Reitz 'driver': 'file', 3648057fc2SMax Reitz 'filename': path 3748057fc2SMax Reitz } 3848057fc2SMax Reitz } 3948057fc2SMax Reitz 4048057fc2SMax Reitz if backing is not None: 4148057fc2SMax Reitz res['backing'] = backing 4248057fc2SMax Reitz 4348057fc2SMax Reitz if throttle: 4448057fc2SMax Reitz res['file'] = { 4548057fc2SMax Reitz 'driver': 'throttle', 4648057fc2SMax Reitz 'throttle-group': throttle, 4748057fc2SMax Reitz 'file': res['file'] 4848057fc2SMax Reitz } 4948057fc2SMax Reitz 5048057fc2SMax Reitz return res 5148057fc2SMax Reitz 5248057fc2SMax Reitz# Finds a node in the debug block graph 5348057fc2SMax Reitzdef find_graph_node(graph, node_id): 5448057fc2SMax Reitz return next(node for node in graph['nodes'] if node['id'] == node_id) 5548057fc2SMax Reitz 5648057fc2SMax Reitz 5748057fc2SMax Reitzdef test_concurrent_finish(write_to_stream_node): 5848057fc2SMax Reitz log('') 5948057fc2SMax Reitz log('=== Commit and stream finish concurrently (letting %s write) ===' % \ 6048057fc2SMax Reitz ('stream' if write_to_stream_node else 'commit')) 6148057fc2SMax Reitz log('') 6248057fc2SMax Reitz 6348057fc2SMax Reitz # All chosen in such a way that when the commit job wants to 6448057fc2SMax Reitz # finish, it polls and thus makes stream finish concurrently -- 6548057fc2SMax Reitz # and the other way around, depending on whether the commit job 6648057fc2SMax Reitz # is finalized before stream completes or not. 6748057fc2SMax Reitz 6848057fc2SMax Reitz with iotests.FilePath('node4.img') as node4_path, \ 6948057fc2SMax Reitz iotests.FilePath('node3.img') as node3_path, \ 7048057fc2SMax Reitz iotests.FilePath('node2.img') as node2_path, \ 7148057fc2SMax Reitz iotests.FilePath('node1.img') as node1_path, \ 7248057fc2SMax Reitz iotests.FilePath('node0.img') as node0_path, \ 7348057fc2SMax Reitz iotests.VM() as vm: 7448057fc2SMax Reitz 7548057fc2SMax Reitz # It is important to use raw for the base layer (so that 7648057fc2SMax Reitz # permissions are just handed through to the protocol layer) 7748057fc2SMax Reitz assert qemu_img('create', '-f', 'raw', node0_path, '64M') == 0 7848057fc2SMax Reitz 7948057fc2SMax Reitz stream_throttle=None 8048057fc2SMax Reitz commit_throttle=None 8148057fc2SMax Reitz 8248057fc2SMax Reitz for path in [node1_path, node2_path, node3_path, node4_path]: 8348057fc2SMax Reitz assert qemu_img('create', '-f', iotests.imgfmt, path, '64M') == 0 8448057fc2SMax Reitz 8548057fc2SMax Reitz if write_to_stream_node: 8648057fc2SMax Reitz # This is what (most of the time) makes commit finish 8748057fc2SMax Reitz # earlier and then pull in stream 8848057fc2SMax Reitz assert qemu_io_silent(node2_path, 8948057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 192), 9048057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 64)) == 0 9148057fc2SMax Reitz 9248057fc2SMax Reitz stream_throttle='tg' 9348057fc2SMax Reitz else: 9448057fc2SMax Reitz # And this makes stream finish earlier 9548057fc2SMax Reitz assert qemu_io_silent(node1_path, 9648057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 64)) == 0 9748057fc2SMax Reitz 9848057fc2SMax Reitz commit_throttle='tg' 9948057fc2SMax Reitz 10048057fc2SMax Reitz vm.launch() 10148057fc2SMax Reitz 10248057fc2SMax Reitz vm.qmp_log('object-add', 10348057fc2SMax Reitz qom_type='throttle-group', 10448057fc2SMax Reitz id='tg', 10548057fc2SMax Reitz props={ 10648057fc2SMax Reitz 'x-iops-write': 1, 10748057fc2SMax Reitz 'x-iops-write-max': 1 10848057fc2SMax Reitz }) 10948057fc2SMax Reitz 11048057fc2SMax Reitz vm.qmp_log('blockdev-add', 11148057fc2SMax Reitz filters=[filter_qmp_testfiles, filter_qmp_imgfmt], 11248057fc2SMax Reitz **node('node4', node4_path, throttle=stream_throttle, 11348057fc2SMax Reitz backing=node('node3', node3_path, 11448057fc2SMax Reitz backing=node('node2', node2_path, 11548057fc2SMax Reitz backing=node('node1', node1_path, 11648057fc2SMax Reitz backing=node('node0', node0_path, throttle=commit_throttle, 11748057fc2SMax Reitz fmt='raw')))))) 11848057fc2SMax Reitz 11948057fc2SMax Reitz vm.qmp_log('block-commit', 12048057fc2SMax Reitz job_id='commit', 12148057fc2SMax Reitz device='node4', 12248057fc2SMax Reitz filter_node_name='commit-filter', 12348057fc2SMax Reitz top_node='node1', 12448057fc2SMax Reitz base_node='node0', 12548057fc2SMax Reitz auto_finalize=False) 12648057fc2SMax Reitz 12748057fc2SMax Reitz vm.qmp_log('block-stream', 12848057fc2SMax Reitz job_id='stream', 12948057fc2SMax Reitz device='node3', 13048057fc2SMax Reitz base_node='commit-filter') 13148057fc2SMax Reitz 13248057fc2SMax Reitz if write_to_stream_node: 13348057fc2SMax Reitz vm.run_job('commit', auto_finalize=False, auto_dismiss=True) 13448057fc2SMax Reitz vm.run_job('stream', auto_finalize=True, auto_dismiss=True) 13548057fc2SMax Reitz else: 13648057fc2SMax Reitz # No, the jobs do not really finish concurrently here, 13748057fc2SMax Reitz # the stream job does complete strictly before commit. 13848057fc2SMax Reitz # But still, this is close enough for what we want to 13948057fc2SMax Reitz # test. 14048057fc2SMax Reitz vm.run_job('stream', auto_finalize=True, auto_dismiss=True) 14148057fc2SMax Reitz vm.run_job('commit', auto_finalize=False, auto_dismiss=True) 14248057fc2SMax Reitz 14348057fc2SMax Reitz # Assert that the backing node of node3 is node 0 now 14448057fc2SMax Reitz graph = vm.qmp('x-debug-query-block-graph')['return'] 14548057fc2SMax Reitz for edge in graph['edges']: 14648057fc2SMax Reitz if edge['name'] == 'backing' and \ 14748057fc2SMax Reitz find_graph_node(graph, edge['parent'])['name'] == 'node3': 14848057fc2SMax Reitz assert find_graph_node(graph, edge['child'])['name'] == 'node0' 14948057fc2SMax Reitz break 15048057fc2SMax Reitz 15148057fc2SMax Reitz 15248057fc2SMax Reitzdef main(): 15348057fc2SMax Reitz log('Running tests:') 15448057fc2SMax Reitz test_concurrent_finish(True) 15548057fc2SMax Reitz test_concurrent_finish(False) 15648057fc2SMax Reitz 15748057fc2SMax Reitzif __name__ == '__main__': 158*5e089febSJohn Snow # Need backing file and change-backing-file support 159*5e089febSJohn Snow iotests.script_main(main, 160*5e089febSJohn Snow supported_fmts=['qcow2', 'qed'], 161*5e089febSJohn Snow supported_platforms=['linux']) 162