1*48057fc2SMax Reitz#!/usr/bin/env python 2*48057fc2SMax Reitz# 3*48057fc2SMax Reitz# Very specific tests for adjacent commit/stream block jobs 4*48057fc2SMax Reitz# 5*48057fc2SMax Reitz# Copyright (C) 2019 Red Hat, Inc. 6*48057fc2SMax Reitz# 7*48057fc2SMax Reitz# This program is free software; you can redistribute it and/or modify 8*48057fc2SMax Reitz# it under the terms of the GNU General Public License as published by 9*48057fc2SMax Reitz# the Free Software Foundation; either version 2 of the License, or 10*48057fc2SMax Reitz# (at your option) any later version. 11*48057fc2SMax Reitz# 12*48057fc2SMax Reitz# This program is distributed in the hope that it will be useful, 13*48057fc2SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 14*48057fc2SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15*48057fc2SMax Reitz# GNU General Public License for more details. 16*48057fc2SMax Reitz# 17*48057fc2SMax Reitz# You should have received a copy of the GNU General Public License 18*48057fc2SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 19*48057fc2SMax Reitz# 20*48057fc2SMax Reitz# Creator/Owner: Max Reitz <mreitz@redhat.com> 21*48057fc2SMax Reitz 22*48057fc2SMax Reitzimport iotests 23*48057fc2SMax Reitzfrom iotests import log, qemu_img, qemu_io_silent, \ 24*48057fc2SMax Reitz filter_qmp_testfiles, filter_qmp_imgfmt 25*48057fc2SMax Reitz 26*48057fc2SMax Reitz# Need backing file and change-backing-file support 27*48057fc2SMax Reitziotests.verify_image_format(supported_fmts=['qcow2', 'qed']) 28*48057fc2SMax Reitziotests.verify_platform(['linux']) 29*48057fc2SMax Reitz 30*48057fc2SMax Reitz 31*48057fc2SMax Reitz# Returns a node for blockdev-add 32*48057fc2SMax Reitzdef node(node_name, path, backing=None, fmt=None, throttle=None): 33*48057fc2SMax Reitz if fmt is None: 34*48057fc2SMax Reitz fmt = iotests.imgfmt 35*48057fc2SMax Reitz 36*48057fc2SMax Reitz res = { 37*48057fc2SMax Reitz 'node-name': node_name, 38*48057fc2SMax Reitz 'driver': fmt, 39*48057fc2SMax Reitz 'file': { 40*48057fc2SMax Reitz 'driver': 'file', 41*48057fc2SMax Reitz 'filename': path 42*48057fc2SMax Reitz } 43*48057fc2SMax Reitz } 44*48057fc2SMax Reitz 45*48057fc2SMax Reitz if backing is not None: 46*48057fc2SMax Reitz res['backing'] = backing 47*48057fc2SMax Reitz 48*48057fc2SMax Reitz if throttle: 49*48057fc2SMax Reitz res['file'] = { 50*48057fc2SMax Reitz 'driver': 'throttle', 51*48057fc2SMax Reitz 'throttle-group': throttle, 52*48057fc2SMax Reitz 'file': res['file'] 53*48057fc2SMax Reitz } 54*48057fc2SMax Reitz 55*48057fc2SMax Reitz return res 56*48057fc2SMax Reitz 57*48057fc2SMax Reitz# Finds a node in the debug block graph 58*48057fc2SMax Reitzdef find_graph_node(graph, node_id): 59*48057fc2SMax Reitz return next(node for node in graph['nodes'] if node['id'] == node_id) 60*48057fc2SMax Reitz 61*48057fc2SMax Reitz 62*48057fc2SMax Reitzdef test_concurrent_finish(write_to_stream_node): 63*48057fc2SMax Reitz log('') 64*48057fc2SMax Reitz log('=== Commit and stream finish concurrently (letting %s write) ===' % \ 65*48057fc2SMax Reitz ('stream' if write_to_stream_node else 'commit')) 66*48057fc2SMax Reitz log('') 67*48057fc2SMax Reitz 68*48057fc2SMax Reitz # All chosen in such a way that when the commit job wants to 69*48057fc2SMax Reitz # finish, it polls and thus makes stream finish concurrently -- 70*48057fc2SMax Reitz # and the other way around, depending on whether the commit job 71*48057fc2SMax Reitz # is finalized before stream completes or not. 72*48057fc2SMax Reitz 73*48057fc2SMax Reitz with iotests.FilePath('node4.img') as node4_path, \ 74*48057fc2SMax Reitz iotests.FilePath('node3.img') as node3_path, \ 75*48057fc2SMax Reitz iotests.FilePath('node2.img') as node2_path, \ 76*48057fc2SMax Reitz iotests.FilePath('node1.img') as node1_path, \ 77*48057fc2SMax Reitz iotests.FilePath('node0.img') as node0_path, \ 78*48057fc2SMax Reitz iotests.VM() as vm: 79*48057fc2SMax Reitz 80*48057fc2SMax Reitz # It is important to use raw for the base layer (so that 81*48057fc2SMax Reitz # permissions are just handed through to the protocol layer) 82*48057fc2SMax Reitz assert qemu_img('create', '-f', 'raw', node0_path, '64M') == 0 83*48057fc2SMax Reitz 84*48057fc2SMax Reitz stream_throttle=None 85*48057fc2SMax Reitz commit_throttle=None 86*48057fc2SMax Reitz 87*48057fc2SMax Reitz for path in [node1_path, node2_path, node3_path, node4_path]: 88*48057fc2SMax Reitz assert qemu_img('create', '-f', iotests.imgfmt, path, '64M') == 0 89*48057fc2SMax Reitz 90*48057fc2SMax Reitz if write_to_stream_node: 91*48057fc2SMax Reitz # This is what (most of the time) makes commit finish 92*48057fc2SMax Reitz # earlier and then pull in stream 93*48057fc2SMax Reitz assert qemu_io_silent(node2_path, 94*48057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 192), 95*48057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 64)) == 0 96*48057fc2SMax Reitz 97*48057fc2SMax Reitz stream_throttle='tg' 98*48057fc2SMax Reitz else: 99*48057fc2SMax Reitz # And this makes stream finish earlier 100*48057fc2SMax Reitz assert qemu_io_silent(node1_path, 101*48057fc2SMax Reitz '-c', 'write %iK 64K' % (65536 - 64)) == 0 102*48057fc2SMax Reitz 103*48057fc2SMax Reitz commit_throttle='tg' 104*48057fc2SMax Reitz 105*48057fc2SMax Reitz vm.launch() 106*48057fc2SMax Reitz 107*48057fc2SMax Reitz vm.qmp_log('object-add', 108*48057fc2SMax Reitz qom_type='throttle-group', 109*48057fc2SMax Reitz id='tg', 110*48057fc2SMax Reitz props={ 111*48057fc2SMax Reitz 'x-iops-write': 1, 112*48057fc2SMax Reitz 'x-iops-write-max': 1 113*48057fc2SMax Reitz }) 114*48057fc2SMax Reitz 115*48057fc2SMax Reitz vm.qmp_log('blockdev-add', 116*48057fc2SMax Reitz filters=[filter_qmp_testfiles, filter_qmp_imgfmt], 117*48057fc2SMax Reitz **node('node4', node4_path, throttle=stream_throttle, 118*48057fc2SMax Reitz backing=node('node3', node3_path, 119*48057fc2SMax Reitz backing=node('node2', node2_path, 120*48057fc2SMax Reitz backing=node('node1', node1_path, 121*48057fc2SMax Reitz backing=node('node0', node0_path, throttle=commit_throttle, 122*48057fc2SMax Reitz fmt='raw')))))) 123*48057fc2SMax Reitz 124*48057fc2SMax Reitz vm.qmp_log('block-commit', 125*48057fc2SMax Reitz job_id='commit', 126*48057fc2SMax Reitz device='node4', 127*48057fc2SMax Reitz filter_node_name='commit-filter', 128*48057fc2SMax Reitz top_node='node1', 129*48057fc2SMax Reitz base_node='node0', 130*48057fc2SMax Reitz auto_finalize=False) 131*48057fc2SMax Reitz 132*48057fc2SMax Reitz vm.qmp_log('block-stream', 133*48057fc2SMax Reitz job_id='stream', 134*48057fc2SMax Reitz device='node3', 135*48057fc2SMax Reitz base_node='commit-filter') 136*48057fc2SMax Reitz 137*48057fc2SMax Reitz if write_to_stream_node: 138*48057fc2SMax Reitz vm.run_job('commit', auto_finalize=False, auto_dismiss=True) 139*48057fc2SMax Reitz vm.run_job('stream', auto_finalize=True, auto_dismiss=True) 140*48057fc2SMax Reitz else: 141*48057fc2SMax Reitz # No, the jobs do not really finish concurrently here, 142*48057fc2SMax Reitz # the stream job does complete strictly before commit. 143*48057fc2SMax Reitz # But still, this is close enough for what we want to 144*48057fc2SMax Reitz # test. 145*48057fc2SMax Reitz vm.run_job('stream', auto_finalize=True, auto_dismiss=True) 146*48057fc2SMax Reitz vm.run_job('commit', auto_finalize=False, auto_dismiss=True) 147*48057fc2SMax Reitz 148*48057fc2SMax Reitz # Assert that the backing node of node3 is node 0 now 149*48057fc2SMax Reitz graph = vm.qmp('x-debug-query-block-graph')['return'] 150*48057fc2SMax Reitz for edge in graph['edges']: 151*48057fc2SMax Reitz if edge['name'] == 'backing' and \ 152*48057fc2SMax Reitz find_graph_node(graph, edge['parent'])['name'] == 'node3': 153*48057fc2SMax Reitz assert find_graph_node(graph, edge['child'])['name'] == 'node0' 154*48057fc2SMax Reitz break 155*48057fc2SMax Reitz 156*48057fc2SMax Reitz 157*48057fc2SMax Reitzdef main(): 158*48057fc2SMax Reitz log('Running tests:') 159*48057fc2SMax Reitz test_concurrent_finish(True) 160*48057fc2SMax Reitz test_concurrent_finish(False) 161*48057fc2SMax Reitz 162*48057fc2SMax Reitzif __name__ == '__main__': 163*48057fc2SMax Reitz main() 164