#!/usr/bin/env python3 # group: rw # # Test graph changes while I/O is happening # # Copyright (C) 2022 Red Hat, Inc. # # 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 . # import os from threading import Thread import iotests from iotests import imgfmt, qemu_img, qemu_img_create, qemu_io, \ QMPTestCase, QemuStorageDaemon top = os.path.join(iotests.test_dir, 'top.img') nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') def do_qemu_img_bench(count: int = 2000000) -> None: """ Do some I/O requests on `nbd_sock`. """ qemu_img('bench', '-f', 'raw', '-c', str(count), f'nbd+unix:///node0?socket={nbd_sock}') class TestGraphChangesWhileIO(QMPTestCase): def setUp(self) -> None: # Create an overlay that can be added at runtime on top of the # null-co block node that will receive I/O qemu_img_create('-f', imgfmt, '-F', 'raw', '-b', 'null-co://', top) # QSD instance with a null-co block node in an I/O thread, # exported over NBD (on `nbd_sock`, export name "node0") self.qsd = QemuStorageDaemon( '--object', 'iothread,id=iothread0', '--blockdev', 'null-co,node-name=node0,read-zeroes=true', '--nbd-server', f'addr.type=unix,addr.path={nbd_sock}', '--export', 'nbd,id=exp0,node-name=node0,iothread=iothread0,' + 'fixed-iothread=true,writable=true', qmp=True ) def tearDown(self) -> None: self.qsd.stop() def test_blockdev_add_while_io(self) -> None: # Run qemu-img bench in the background bench_thr = Thread(target=do_qemu_img_bench) bench_thr.start() # While qemu-img bench is running, repeatedly add and remove an # overlay to/from node0 while bench_thr.is_alive(): self.qsd.cmd('blockdev-add', { 'driver': imgfmt, 'node-name': 'overlay', 'backing': 'node0', 'file': { 'driver': 'file', 'filename': top } }) self.qsd.cmd('blockdev-del', { 'node-name': 'overlay' }) bench_thr.join() def test_commit_while_io(self) -> None: # Run qemu-img bench in the background bench_thr = Thread(target=do_qemu_img_bench, args=(200000, )) bench_thr.start() qemu_io('-c', 'write 0 64k', top) qemu_io('-c', 'write 128k 64k', top) self.qsd.cmd('blockdev-add', { 'driver': imgfmt, 'node-name': 'overlay', 'backing': None, 'file': { 'driver': 'file', 'filename': top } }) self.qsd.cmd('blockdev-snapshot', { 'node': 'node0', 'overlay': 'overlay', }) # While qemu-img bench is running, repeatedly commit overlay to node0 while bench_thr.is_alive(): self.qsd.cmd('block-commit', { 'job-id': 'job0', 'device': 'overlay', }) self.qsd.cmd('block-job-cancel', { 'device': 'job0', }) cancelled = False while not cancelled: for event in self.qsd.get_qmp().get_events(wait=10.0): if event['event'] != 'JOB_STATUS_CHANGE': continue if event['data']['status'] == 'null': cancelled = True bench_thr.join() if __name__ == '__main__': # Format must support raw backing files iotests.main(supported_fmts=['qcow', 'qcow2', 'qed'], supported_protocols=['file'])