1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: rw 3e38da020SMax Reitz# 4e38da020SMax Reitz# Tests for active mirroring 5e38da020SMax Reitz# 6e38da020SMax Reitz# Copyright (C) 2018 Red Hat, Inc. 7e38da020SMax Reitz# 8e38da020SMax Reitz# This program is free software; you can redistribute it and/or modify 9e38da020SMax Reitz# it under the terms of the GNU General Public License as published by 10e38da020SMax Reitz# the Free Software Foundation; either version 2 of the License, or 11e38da020SMax Reitz# (at your option) any later version. 12e38da020SMax Reitz# 13e38da020SMax Reitz# This program is distributed in the hope that it will be useful, 14e38da020SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 15e38da020SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16e38da020SMax Reitz# GNU General Public License for more details. 17e38da020SMax Reitz# 18e38da020SMax Reitz# You should have received a copy of the GNU General Public License 19e38da020SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 20e38da020SMax Reitz# 21e38da020SMax Reitz 2238591290SHanna Reitzimport math 23e38da020SMax Reitzimport os 2438591290SHanna Reitzimport subprocess 257b5929c7SHanna Reitzimport time 267b5929c7SHanna Reitzfrom typing import List, Optional 27e38da020SMax Reitzimport iotests 28e38da020SMax Reitzfrom iotests import qemu_img 29e38da020SMax Reitz 30e38da020SMax Reitzsource_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) 31e38da020SMax Reitztarget_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) 32e38da020SMax Reitz 33e38da020SMax Reitzclass TestActiveMirror(iotests.QMPTestCase): 34e38da020SMax Reitz image_len = 128 * 1024 * 1024 # MB 35e38da020SMax Reitz potential_writes_in_flight = True 36e38da020SMax Reitz 37e38da020SMax Reitz def setUp(self): 38e38da020SMax Reitz qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') 39e38da020SMax Reitz qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') 40e38da020SMax Reitz 41e38da020SMax Reitz blk_source = {'id': 'source', 42e38da020SMax Reitz 'if': 'none', 43e38da020SMax Reitz 'node-name': 'source-node', 44e38da020SMax Reitz 'driver': iotests.imgfmt, 45e0f69d83SVladimir Sementsov-Ogievskiy 'file': {'driver': 'blkdebug', 46e0f69d83SVladimir Sementsov-Ogievskiy 'image': {'driver': 'file', 47e0f69d83SVladimir Sementsov-Ogievskiy 'filename': source_img}}} 48e38da020SMax Reitz 49e38da020SMax Reitz blk_target = {'node-name': 'target-node', 50e38da020SMax Reitz 'driver': iotests.imgfmt, 51e38da020SMax Reitz 'file': {'driver': 'file', 52e38da020SMax Reitz 'filename': target_img}} 53e38da020SMax Reitz 54e38da020SMax Reitz self.vm = iotests.VM() 55e38da020SMax Reitz self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source)) 56e38da020SMax Reitz self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target)) 5738591290SHanna Reitz self.vm.add_device('virtio-blk,id=vblk,drive=source') 58e38da020SMax Reitz self.vm.launch() 59e38da020SMax Reitz 60e38da020SMax Reitz def tearDown(self): 61e38da020SMax Reitz self.vm.shutdown() 62e38da020SMax Reitz 63e38da020SMax Reitz if not self.potential_writes_in_flight: 64e38da020SMax Reitz self.assertTrue(iotests.compare_images(source_img, target_img), 65e38da020SMax Reitz 'mirror target does not match source') 66e38da020SMax Reitz 67e38da020SMax Reitz os.remove(source_img) 68e38da020SMax Reitz os.remove(target_img) 69e38da020SMax Reitz 70e38da020SMax Reitz def doActiveIO(self, sync_source_and_target): 71e38da020SMax Reitz # Fill the source image 72e38da020SMax Reitz self.vm.hmp_qemu_io('source', 73e38da020SMax Reitz 'write -P 1 0 %i' % self.image_len); 74e38da020SMax Reitz 75e38da020SMax Reitz # Start some background requests 769a3a9a63SMax Reitz for offset in range(1 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024): 77e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset) 789a3a9a63SMax Reitz for offset in range(2 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024): 79e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 80e38da020SMax Reitz 81e38da020SMax Reitz # Start the block job 82b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-mirror', 83e38da020SMax Reitz job_id='mirror', 84e38da020SMax Reitz filter_node_name='mirror-node', 85e38da020SMax Reitz device='source-node', 86e38da020SMax Reitz target='target-node', 87e38da020SMax Reitz sync='full', 88e38da020SMax Reitz copy_mode='write-blocking') 89e38da020SMax Reitz 90e38da020SMax Reitz # Start some more requests 919a3a9a63SMax Reitz for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): 92e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) 939a3a9a63SMax Reitz for offset in range(4 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): 94e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 95e38da020SMax Reitz 96e38da020SMax Reitz # Wait for the READY event 97e38da020SMax Reitz self.wait_ready(drive='mirror') 98e38da020SMax Reitz 99e38da020SMax Reitz # Now start some final requests; all of these (which land on 100e38da020SMax Reitz # the source) should be settled using the active mechanism. 101e38da020SMax Reitz # The mirror code itself asserts that the source BDS's dirty 102e38da020SMax Reitz # bitmap will stay clean between READY and COMPLETED. 1039a3a9a63SMax Reitz for offset in range(5 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): 104e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) 1059a3a9a63SMax Reitz for offset in range(6 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): 106e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 107e38da020SMax Reitz 108e38da020SMax Reitz if sync_source_and_target: 109e38da020SMax Reitz # If source and target should be in sync after the mirror, 110e38da020SMax Reitz # we have to flush before completion 111e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_flush') 112e38da020SMax Reitz self.potential_writes_in_flight = False 113e38da020SMax Reitz 114e38da020SMax Reitz self.complete_and_wait(drive='mirror', wait_ready=False) 115e38da020SMax Reitz 116e38da020SMax Reitz def testActiveIO(self): 117e38da020SMax Reitz self.doActiveIO(False) 118e38da020SMax Reitz 119e38da020SMax Reitz def testActiveIOFlushed(self): 120e38da020SMax Reitz self.doActiveIO(True) 121e38da020SMax Reitz 12219ba4651SMax Reitz def testUnalignedActiveIO(self): 12319ba4651SMax Reitz # Fill the source image 12419ba4651SMax Reitz result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') 12519ba4651SMax Reitz 12619ba4651SMax Reitz # Start the block job (very slowly) 127b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-mirror', 12819ba4651SMax Reitz job_id='mirror', 12919ba4651SMax Reitz filter_node_name='mirror-node', 13019ba4651SMax Reitz device='source-node', 13119ba4651SMax Reitz target='target-node', 13219ba4651SMax Reitz sync='full', 13319ba4651SMax Reitz copy_mode='write-blocking', 13419ba4651SMax Reitz buf_size=(1048576 // 4), 13519ba4651SMax Reitz speed=1) 13619ba4651SMax Reitz 13719ba4651SMax Reitz # Start an unaligned request to a dirty area 13819ba4651SMax Reitz result = self.vm.hmp_qemu_io('source', 'write -P 2 %i 1' % (1048576 + 42)) 13919ba4651SMax Reitz 14019ba4651SMax Reitz # Let the job finish 141b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-job-set-speed', device='mirror', speed=0) 14219ba4651SMax Reitz self.complete_and_wait(drive='mirror') 14319ba4651SMax Reitz 14419ba4651SMax Reitz self.potential_writes_in_flight = False 145e38da020SMax Reitz 146e0f69d83SVladimir Sementsov-Ogievskiy def testIntersectingActiveIO(self): 147e0f69d83SVladimir Sementsov-Ogievskiy # Fill the source image 148e0f69d83SVladimir Sementsov-Ogievskiy result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') 149e0f69d83SVladimir Sementsov-Ogievskiy 150e0f69d83SVladimir Sementsov-Ogievskiy # Start the block job (very slowly) 151b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-mirror', 152e0f69d83SVladimir Sementsov-Ogievskiy job_id='mirror', 153e0f69d83SVladimir Sementsov-Ogievskiy filter_node_name='mirror-node', 154e0f69d83SVladimir Sementsov-Ogievskiy device='source-node', 155e0f69d83SVladimir Sementsov-Ogievskiy target='target-node', 156e0f69d83SVladimir Sementsov-Ogievskiy sync='full', 157e0f69d83SVladimir Sementsov-Ogievskiy copy_mode='write-blocking', 158e0f69d83SVladimir Sementsov-Ogievskiy speed=1) 159e0f69d83SVladimir Sementsov-Ogievskiy 160e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'break write_aio A') 161e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 0 1M') # 1 162e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'wait_break A') 163e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 0 2M') # 2 164e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 0 2M') # 3 165e0f69d83SVladimir Sementsov-Ogievskiy 166e0f69d83SVladimir Sementsov-Ogievskiy # Now 2 and 3 are in mirror_wait_on_conflicts, waiting for 1 167e0f69d83SVladimir Sementsov-Ogievskiy 168e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'break write_aio B') 169e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 1M 2M') # 4 170e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'wait_break B') 171e0f69d83SVladimir Sementsov-Ogievskiy 172e0f69d83SVladimir Sementsov-Ogievskiy # 4 doesn't wait for 2 and 3, because they didn't yet set 173e0f69d83SVladimir Sementsov-Ogievskiy # in_flight_bitmap. So, nothing prevents 4 to go except for our 174e0f69d83SVladimir Sementsov-Ogievskiy # break-point B. 175e0f69d83SVladimir Sementsov-Ogievskiy 176e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'resume A') 177e0f69d83SVladimir Sementsov-Ogievskiy 178e0f69d83SVladimir Sementsov-Ogievskiy # Now we resumed 1, so 2 and 3 goes to the next iteration of while loop 179e0f69d83SVladimir Sementsov-Ogievskiy # in mirror_wait_on_conflicts(). They don't exit, as bitmap is dirty 180d44dae1aSVladimir Sementsov-Ogievskiy # due to request 4. 181d44dae1aSVladimir Sementsov-Ogievskiy # In the past at that point 2 and 3 would wait for each other producing 182d44dae1aSVladimir Sementsov-Ogievskiy # a dead-lock. Now this is fixed and they will wait for request 4. 183e0f69d83SVladimir Sementsov-Ogievskiy 184e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'resume B') 185e0f69d83SVladimir Sementsov-Ogievskiy 186d44dae1aSVladimir Sementsov-Ogievskiy # After resuming 4, one of 2 and 3 goes first and set in_flight_bitmap, 187d44dae1aSVladimir Sementsov-Ogievskiy # so the other will wait for it. 188e0f69d83SVladimir Sementsov-Ogievskiy 189b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-job-set-speed', device='mirror', speed=0) 190e0f69d83SVladimir Sementsov-Ogievskiy self.complete_and_wait(drive='mirror') 191e0f69d83SVladimir Sementsov-Ogievskiy 192e0f69d83SVladimir Sementsov-Ogievskiy self.potential_writes_in_flight = False 193e0f69d83SVladimir Sementsov-Ogievskiy 194e38da020SMax Reitz 1957b5929c7SHanna Reitzclass TestThrottledWithNbdExportBase(iotests.QMPTestCase): 19638591290SHanna Reitz image_len = 128 * 1024 * 1024 # MB 1977b5929c7SHanna Reitz iops: Optional[int] = None 19838591290SHanna Reitz background_processes: List['subprocess.Popen[str]'] = [] 19938591290SHanna Reitz 20038591290SHanna Reitz def setUp(self): 2017b5929c7SHanna Reitz # Must be set by subclasses 2027b5929c7SHanna Reitz self.assertIsNotNone(self.iops) 2037b5929c7SHanna Reitz 20438591290SHanna Reitz qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') 20538591290SHanna Reitz qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') 20638591290SHanna Reitz 20738591290SHanna Reitz self.vm = iotests.VM() 20838591290SHanna Reitz self.vm.launch() 20938591290SHanna Reitz 210b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('object-add', **{ 21138591290SHanna Reitz 'qom-type': 'throttle-group', 21238591290SHanna Reitz 'id': 'thrgr', 21338591290SHanna Reitz 'limits': { 21438591290SHanna Reitz 'iops-total': self.iops, 21538591290SHanna Reitz 'iops-total-max': self.iops 21638591290SHanna Reitz } 21738591290SHanna Reitz }) 21838591290SHanna Reitz 219b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', **{ 22038591290SHanna Reitz 'node-name': 'source-node', 22138591290SHanna Reitz 'driver': 'throttle', 22238591290SHanna Reitz 'throttle-group': 'thrgr', 22338591290SHanna Reitz 'file': { 22438591290SHanna Reitz 'driver': iotests.imgfmt, 22538591290SHanna Reitz 'file': { 22638591290SHanna Reitz 'driver': 'file', 22738591290SHanna Reitz 'filename': source_img 22838591290SHanna Reitz } 22938591290SHanna Reitz } 23038591290SHanna Reitz }) 23138591290SHanna Reitz 232b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', **{ 23338591290SHanna Reitz 'node-name': 'target-node', 23438591290SHanna Reitz 'driver': iotests.imgfmt, 23538591290SHanna Reitz 'file': { 23638591290SHanna Reitz 'driver': 'file', 23738591290SHanna Reitz 'filename': target_img 23838591290SHanna Reitz } 23938591290SHanna Reitz }) 24038591290SHanna Reitz 24138591290SHanna Reitz self.nbd_sock = iotests.file_path('nbd.sock', 24238591290SHanna Reitz base_dir=iotests.sock_dir) 24338591290SHanna Reitz self.nbd_url = f'nbd+unix:///source-node?socket={self.nbd_sock}' 24438591290SHanna Reitz 245b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('nbd-server-start', addr={ 24638591290SHanna Reitz 'type': 'unix', 24738591290SHanna Reitz 'data': { 24838591290SHanna Reitz 'path': self.nbd_sock 24938591290SHanna Reitz } 25038591290SHanna Reitz }) 25138591290SHanna Reitz 252b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-export-add', id='exp0', type='nbd', 25338591290SHanna Reitz node_name='source-node', writable=True) 25438591290SHanna Reitz 25538591290SHanna Reitz def tearDown(self): 25638591290SHanna Reitz # Wait for background requests to settle 25738591290SHanna Reitz try: 25838591290SHanna Reitz while True: 25938591290SHanna Reitz p = self.background_processes.pop() 26038591290SHanna Reitz while True: 26138591290SHanna Reitz try: 26238591290SHanna Reitz p.wait(timeout=0.0) 26338591290SHanna Reitz break 26438591290SHanna Reitz except subprocess.TimeoutExpired: 26538591290SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 266*a1e094dbSDaniel P. Berrangé try: 267*a1e094dbSDaniel P. Berrangé p.kill() 268*a1e094dbSDaniel P. Berrangé p.stdout.close() 269*a1e094dbSDaniel P. Berrangé except: 270*a1e094dbSDaniel P. Berrangé pass 27138591290SHanna Reitz except IndexError: 27238591290SHanna Reitz pass 27338591290SHanna Reitz 27438591290SHanna Reitz # Cancel ongoing block jobs 27538591290SHanna Reitz for job in self.vm.qmp('query-jobs')['return']: 27638591290SHanna Reitz self.vm.qmp('block-job-cancel', device=job['id'], force=True) 27738591290SHanna Reitz 27838591290SHanna Reitz while True: 27938591290SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 28038591290SHanna Reitz if len(self.vm.qmp('query-jobs')['return']) == 0: 28138591290SHanna Reitz break 28238591290SHanna Reitz 28338591290SHanna Reitz self.vm.shutdown() 28438591290SHanna Reitz os.remove(source_img) 28538591290SHanna Reitz os.remove(target_img) 28638591290SHanna Reitz 2877b5929c7SHanna Reitz 2887b5929c7SHanna Reitzclass TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase): 2897b5929c7SHanna Reitz iops = 16 2907b5929c7SHanna Reitz 29138591290SHanna Reitz def testUnderLoad(self): 29238591290SHanna Reitz ''' 29338591290SHanna Reitz Throttle the source node, then issue a whole bunch of external requests 29438591290SHanna Reitz while the mirror job (in write-blocking mode) is running. We want to 29538591290SHanna Reitz see background requests being issued even while the source is under 29638591290SHanna Reitz full load by active writes, so that progress can be made towards READY. 29738591290SHanna Reitz ''' 29838591290SHanna Reitz 29938591290SHanna Reitz # Fill the first half of the source image; do not fill the second half, 30038591290SHanna Reitz # that is where we will have active requests occur. This ensures that 30138591290SHanna Reitz # active mirroring itself will not directly contribute to the job's 30238591290SHanna Reitz # progress (because when the job was started, those areas were not 30338591290SHanna Reitz # intended to be copied, so active mirroring will only lead to not 30438591290SHanna Reitz # losing progress, but also not making any). 30538591290SHanna Reitz self.vm.hmp_qemu_io('source-node', 30638591290SHanna Reitz f'aio_write -P 1 0 {self.image_len // 2}') 30738591290SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 30838591290SHanna Reitz 30938591290SHanna Reitz # Launch the mirror job 31038591290SHanna Reitz mirror_buf_size = 65536 311b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-mirror', 31238591290SHanna Reitz job_id='mirror', 31338591290SHanna Reitz filter_node_name='mirror-node', 31438591290SHanna Reitz device='source-node', 31538591290SHanna Reitz target='target-node', 31638591290SHanna Reitz sync='full', 31738591290SHanna Reitz copy_mode='write-blocking', 31838591290SHanna Reitz buf_size=mirror_buf_size) 31938591290SHanna Reitz 32038591290SHanna Reitz # We create the external requests via qemu-io processes on the NBD 32138591290SHanna Reitz # server. Have their offset start in the middle of the image so they 32238591290SHanna Reitz # do not overlap with the background requests (which start from the 32338591290SHanna Reitz # beginning). 32438591290SHanna Reitz active_request_offset = self.image_len // 2 32538591290SHanna Reitz active_request_len = 4096 32638591290SHanna Reitz 32738591290SHanna Reitz # Create enough requests to saturate the node for 5 seconds 32838591290SHanna Reitz for _ in range(0, 5 * self.iops): 32938591290SHanna Reitz req = f'write -P 42 {active_request_offset} {active_request_len}' 33038591290SHanna Reitz active_request_offset += active_request_len 33138591290SHanna Reitz p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req) 33238591290SHanna Reitz self.background_processes += [p] 33338591290SHanna Reitz 33438591290SHanna Reitz # Now advance the clock one I/O operation at a time by the 4 seconds 33538591290SHanna Reitz # (i.e. one less than 5). We expect the mirror job to issue background 33638591290SHanna Reitz # operations here, even though active requests are still in flight. 33738591290SHanna Reitz # The active requests will take precedence, however, because they have 33838591290SHanna Reitz # been issued earlier than mirror's background requests. 33938591290SHanna Reitz # Once the active requests we have started above are done (i.e. after 5 34038591290SHanna Reitz # virtual seconds), we expect those background requests to be worked 34138591290SHanna Reitz # on. We only advance 4 seconds here to avoid race conditions. 34238591290SHanna Reitz for _ in range(0, 4 * self.iops): 34338591290SHanna Reitz step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops) 34438591290SHanna Reitz self.vm.qtest(f'clock_step {step}') 34538591290SHanna Reitz 34638591290SHanna Reitz # Note how much remains to be done until the mirror job is finished 34738591290SHanna Reitz job_status = self.vm.qmp('query-jobs')['return'][0] 34838591290SHanna Reitz start_remaining = job_status['total-progress'] - \ 34938591290SHanna Reitz job_status['current-progress'] 35038591290SHanna Reitz 35138591290SHanna Reitz # Create a whole bunch of more active requests 35238591290SHanna Reitz for _ in range(0, 10 * self.iops): 35338591290SHanna Reitz req = f'write -P 42 {active_request_offset} {active_request_len}' 35438591290SHanna Reitz active_request_offset += active_request_len 35538591290SHanna Reitz p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req) 35638591290SHanna Reitz self.background_processes += [p] 35738591290SHanna Reitz 35838591290SHanna Reitz # Let the clock advance more. After 1 second, as noted above, we 35938591290SHanna Reitz # expect the background requests to be worked on. Give them a couple 36038591290SHanna Reitz # of seconds (specifically 4) to see their impact. 36138591290SHanna Reitz for _ in range(0, 5 * self.iops): 36238591290SHanna Reitz step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops) 36338591290SHanna Reitz self.vm.qtest(f'clock_step {step}') 36438591290SHanna Reitz 36538591290SHanna Reitz # Note how much remains to be done now. We expect this number to be 36638591290SHanna Reitz # reduced thanks to those background requests. 36738591290SHanna Reitz job_status = self.vm.qmp('query-jobs')['return'][0] 36838591290SHanna Reitz end_remaining = job_status['total-progress'] - \ 36938591290SHanna Reitz job_status['current-progress'] 37038591290SHanna Reitz 37138591290SHanna Reitz # See that indeed progress was being made on the job, even while the 37238591290SHanna Reitz # node was saturated with active requests 37338591290SHanna Reitz self.assertGreater(start_remaining - end_remaining, 0) 37438591290SHanna Reitz 37538591290SHanna Reitz 3767b5929c7SHanna Reitzclass TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase): 3777b5929c7SHanna Reitz iops = 1024 3787b5929c7SHanna Reitz 3797b5929c7SHanna Reitz def testActiveOnCreation(self): 3807b5929c7SHanna Reitz ''' 3817b5929c7SHanna Reitz Issue requests on the mirror source node right as the mirror is 3827b5929c7SHanna Reitz instated. It's possible that requests occur before the actual job is 3837b5929c7SHanna Reitz created, but after the node has been put into the graph. Write 3847b5929c7SHanna Reitz requests across the node must in that case be forwarded to the source 3857b5929c7SHanna Reitz node without attempting to mirror them (there is no job object yet, so 3867b5929c7SHanna Reitz attempting to access it would cause a segfault). 3877b5929c7SHanna Reitz We do this with a lightly throttled node (i.e. quite high IOPS limit). 3887b5929c7SHanna Reitz Using throttling seems to increase reproductivity, but if the limit is 3897b5929c7SHanna Reitz too low, all requests allowed per second will be submitted before 3907b5929c7SHanna Reitz mirror_start_job() gets to the problematic point. 3917b5929c7SHanna Reitz ''' 3927b5929c7SHanna Reitz 3937b5929c7SHanna Reitz # Let qemu-img bench create write requests (enough for two seconds on 3947b5929c7SHanna Reitz # the virtual clock) 3957b5929c7SHanna Reitz bench_args = ['bench', '-w', '-d', '1024', '-f', 'nbd', 3967b5929c7SHanna Reitz '-c', str(self.iops * 2), self.nbd_url] 3977b5929c7SHanna Reitz p = iotests.qemu_tool_popen(iotests.qemu_img_args + bench_args) 3987b5929c7SHanna Reitz self.background_processes += [p] 3997b5929c7SHanna Reitz 4007b5929c7SHanna Reitz # Give qemu-img bench time to start up and issue requests 4017b5929c7SHanna Reitz time.sleep(1.0) 4027b5929c7SHanna Reitz # Flush the request queue, so new requests can come in right as we 4037b5929c7SHanna Reitz # start blockdev-mirror 4047b5929c7SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 4057b5929c7SHanna Reitz 406b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-mirror', 4077b5929c7SHanna Reitz job_id='mirror', 4087b5929c7SHanna Reitz device='source-node', 4097b5929c7SHanna Reitz target='target-node', 4107b5929c7SHanna Reitz sync='full', 4117b5929c7SHanna Reitz copy_mode='write-blocking') 4127b5929c7SHanna Reitz 4137b5929c7SHanna Reitz 414e38da020SMax Reitzif __name__ == '__main__': 415103cbc77SMax Reitz iotests.main(supported_fmts=['qcow2', 'raw'], 416103cbc77SMax Reitz supported_protocols=['file']) 417