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