1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
#!/usr/bin/env python3
# group: rw auto quick
#
# Test case for ejecting BDSs with block jobs still running on them
#
# Originally written in bash by Hanna Czenczek, ported to Python by Stefan
# Hajnoczi.
#
# Copyright Red Hat
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import iotests
# Common filters to mask values that vary in the test output
QMP_FILTERS = [iotests.filter_qmp_testfiles, \
iotests.filter_qmp_imgfmt]
class TestCase:
def __init__(self, name, vm, image_path, cancel_event):
self.name = name
self.vm = vm
self.image_path = image_path
self.cancel_event = cancel_event
def __enter__(self):
iotests.log(f'=== Testing {self.name} ===')
self.vm.qmp_log('blockdev-add', \
node_name='drv0', \
driver=iotests.imgfmt, \
file={'driver': 'file', 'filename': self.image_path}, \
filters=QMP_FILTERS)
def __exit__(self, *exc_details):
# This is expected to fail because the job still exists
self.vm.qmp_log('blockdev-del', node_name='drv0', \
filters=[iotests.filter_qmp_generated_node_ids])
self.vm.qmp_log('block-job-cancel', device='job0')
event = self.vm.event_wait(self.cancel_event)
iotests.log(event, filters=[iotests.filter_qmp_event])
# This time it succeeds
self.vm.qmp_log('blockdev-del', node_name='drv0')
# Separate test cases in output
iotests.log('')
def main() -> None:
with iotests.FilePath('bottom', 'middle', 'top', 'target') as \
(bottom_path, middle_path, top_path, target_path), \
iotests.VM() as vm:
iotests.log('Creating bottom <- middle <- top backing file chain...')
IMAGE_SIZE='1M'
iotests.qemu_img_create('-f', iotests.imgfmt, bottom_path, IMAGE_SIZE)
iotests.qemu_img_create('-f', iotests.imgfmt, \
'-F', iotests.imgfmt, \
'-b', bottom_path, \
middle_path, \
IMAGE_SIZE)
iotests.qemu_img_create('-f', iotests.imgfmt, \
'-F', iotests.imgfmt, \
'-b', middle_path, \
top_path, \
IMAGE_SIZE)
iotests.log('Starting VM...')
vm.add_args('-nodefaults')
vm.launch()
# drive-backup will not send BLOCK_JOB_READY by itself, and cancelling
# the job will consequently result in BLOCK_JOB_CANCELLED being
# emitted.
with TestCase('drive-backup', vm, top_path, 'BLOCK_JOB_CANCELLED'):
vm.qmp_log('drive-backup', \
job_id='job0', \
device='drv0', \
target=target_path, \
format=iotests.imgfmt, \
sync='none', \
filters=QMP_FILTERS)
# drive-mirror will send BLOCK_JOB_READY basically immediately, and
# cancelling the job will consequently result in BLOCK_JOB_COMPLETED
# being emitted.
with TestCase('drive-mirror', vm, top_path, 'BLOCK_JOB_COMPLETED'):
vm.qmp_log('drive-mirror', \
job_id='job0', \
device='drv0', \
target=target_path, \
format=iotests.imgfmt, \
sync='none', \
filters=QMP_FILTERS)
event = vm.event_wait('BLOCK_JOB_READY')
assert event is not None # silence mypy
iotests.log(event, filters=[iotests.filter_qmp_event])
# An active block-commit will send BLOCK_JOB_READY basically
# immediately, and cancelling the job will consequently result in
# BLOCK_JOB_COMPLETED being emitted.
with TestCase('active block-commit', vm, top_path, \
'BLOCK_JOB_COMPLETED'):
vm.qmp_log('block-commit', \
job_id='job0', \
device='drv0')
event = vm.event_wait('BLOCK_JOB_READY')
assert event is not None # silence mypy
iotests.log(event, filters=[iotests.filter_qmp_event])
# Give block-commit something to work on, otherwise it would be done
# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would
# work just fine without the block job still running.
iotests.qemu_io(middle_path, '-c', f'write 0 {IMAGE_SIZE}')
with TestCase('non-active block-commit', vm, top_path, \
'BLOCK_JOB_CANCELLED'):
vm.qmp_log('block-commit', \
job_id='job0', \
device='drv0', \
top=middle_path, \
speed=1, \
filters=[iotests.filter_qmp_testfiles])
# Give block-stream something to work on, otherwise it would be done
# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would
# work just fine without the block job still running.
iotests.qemu_io(bottom_path, '-c', f'write 0 {IMAGE_SIZE}')
with TestCase('block-stream', vm, top_path, 'BLOCK_JOB_CANCELLED'):
vm.qmp_log('block-stream', \
job_id='job0', \
device='drv0', \
speed=1)
if __name__ == '__main__':
iotests.script_main(main, supported_fmts=['qcow2', 'qed'],
supported_protocols=['file'])
|