1#!/usr/bin/env python3 2# 3# This test covers what happens when a mirror block job is cancelled 4# in various phases of its existence. 5# 6# Note that this test only checks the emitted events (i.e. 7# BLOCK_JOB_COMPLETED vs. BLOCK_JOB_CANCELLED), it does not compare 8# whether the target is in sync with the source when the 9# BLOCK_JOB_COMPLETED event occurs. This is covered by other tests 10# (such as 041). 11# 12# Copyright (C) 2018 Red Hat, Inc. 13# 14# This program is free software; you can redistribute it and/or modify 15# it under the terms of the GNU General Public License as published by 16# the Free Software Foundation; either version 2 of the License, or 17# (at your option) any later version. 18# 19# This program is distributed in the hope that it will be useful, 20# but WITHOUT ANY WARRANTY; without even the implied warranty of 21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22# GNU General Public License for more details. 23# 24# You should have received a copy of the GNU General Public License 25# along with this program. If not, see <http://www.gnu.org/licenses/>. 26# 27# Creator/Owner: Max Reitz <mreitz@redhat.com> 28 29import iotests 30from iotests import log, qemu_img, qemu_io_silent 31 32iotests.verify_image_format(supported_fmts=['qcow2', 'raw']) 33 34 35# Launches the VM, adds two null-co nodes (source and target), and 36# starts a blockdev-mirror job on them. 37# 38# Either both or none of speed and buf_size must be given. 39 40def start_mirror(vm, speed=None, buf_size=None): 41 vm.launch() 42 43 ret = vm.qmp('blockdev-add', 44 node_name='source', 45 driver='null-co', 46 size=1048576) 47 assert ret['return'] == {} 48 49 ret = vm.qmp('blockdev-add', 50 node_name='target', 51 driver='null-co', 52 size=1048576) 53 assert ret['return'] == {} 54 55 if speed is not None: 56 ret = vm.qmp('blockdev-mirror', 57 job_id='mirror', 58 device='source', 59 target='target', 60 sync='full', 61 speed=speed, 62 buf_size=buf_size) 63 else: 64 ret = vm.qmp('blockdev-mirror', 65 job_id='mirror', 66 device='source', 67 target='target', 68 sync='full') 69 70 assert ret['return'] == {} 71 72 73log('') 74log('=== Cancel mirror job before convergence ===') 75log('') 76 77log('--- force=false ---') 78log('') 79 80with iotests.VM() as vm: 81 # Low speed so it does not converge 82 start_mirror(vm, 65536, 65536) 83 84 log('Cancelling job') 85 log(vm.qmp('block-job-cancel', device='mirror', force=False)) 86 87 log(vm.event_wait('BLOCK_JOB_CANCELLED'), 88 filters=[iotests.filter_qmp_event]) 89 90log('') 91log('--- force=true ---') 92log('') 93 94with iotests.VM() as vm: 95 # Low speed so it does not converge 96 start_mirror(vm, 65536, 65536) 97 98 log('Cancelling job') 99 log(vm.qmp('block-job-cancel', device='mirror', force=True)) 100 101 log(vm.event_wait('BLOCK_JOB_CANCELLED'), 102 filters=[iotests.filter_qmp_event]) 103 104 105log('') 106log('=== Cancel mirror job after convergence ===') 107log('') 108 109log('--- force=false ---') 110log('') 111 112with iotests.VM() as vm: 113 start_mirror(vm) 114 115 log(vm.event_wait('BLOCK_JOB_READY'), 116 filters=[iotests.filter_qmp_event]) 117 118 log('Cancelling job') 119 log(vm.qmp('block-job-cancel', device='mirror', force=False)) 120 121 log(vm.event_wait('BLOCK_JOB_COMPLETED'), 122 filters=[iotests.filter_qmp_event]) 123 124log('') 125log('--- force=true ---') 126log('') 127 128with iotests.VM() as vm: 129 start_mirror(vm) 130 131 log(vm.event_wait('BLOCK_JOB_READY'), 132 filters=[iotests.filter_qmp_event]) 133 134 log('Cancelling job') 135 log(vm.qmp('block-job-cancel', device='mirror', force=True)) 136 137 log(vm.event_wait('BLOCK_JOB_CANCELLED'), 138 filters=[iotests.filter_qmp_event]) 139 140log('') 141log('=== Cancel mirror job from throttled node by quitting ===') 142log('') 143 144with iotests.VM() as vm, \ 145 iotests.FilePath('src.img') as src_img_path: 146 147 assert qemu_img('create', '-f', iotests.imgfmt, src_img_path, '64M') == 0 148 assert qemu_io_silent('-f', iotests.imgfmt, src_img_path, 149 '-c', 'write -P 42 0M 64M') == 0 150 151 vm.launch() 152 153 ret = vm.qmp('object-add', qom_type='throttle-group', id='tg', 154 props={'x-bps-read': 4096}) 155 assert ret['return'] == {} 156 157 ret = vm.qmp('blockdev-add', 158 node_name='source', 159 driver=iotests.imgfmt, 160 file={ 161 'driver': 'file', 162 'filename': src_img_path 163 }) 164 assert ret['return'] == {} 165 166 ret = vm.qmp('blockdev-add', 167 node_name='throttled-source', 168 driver='throttle', 169 throttle_group='tg', 170 file='source') 171 assert ret['return'] == {} 172 173 ret = vm.qmp('blockdev-add', 174 node_name='target', 175 driver='null-co', 176 size=(64 * 1048576)) 177 assert ret['return'] == {} 178 179 ret = vm.qmp('blockdev-mirror', 180 job_id='mirror', 181 device='throttled-source', 182 target='target', 183 sync='full') 184 assert ret['return'] == {} 185 186 log(vm.qmp('quit')) 187 188 with iotests.Timeout(5, 'Timeout waiting for VM to quit'): 189 vm.shutdown(has_quit=True) 190