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 25*7b5929c7SHanna Reitzimport time 26*7b5929c7SHanna 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 82e38da020SMax Reitz result = self.vm.qmp('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 self.assert_qmp(result, 'return', {}) 90e38da020SMax Reitz 91e38da020SMax Reitz # Start some more requests 929a3a9a63SMax Reitz for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): 93e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) 949a3a9a63SMax Reitz for offset in range(4 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): 95e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 96e38da020SMax Reitz 97e38da020SMax Reitz # Wait for the READY event 98e38da020SMax Reitz self.wait_ready(drive='mirror') 99e38da020SMax Reitz 100e38da020SMax Reitz # Now start some final requests; all of these (which land on 101e38da020SMax Reitz # the source) should be settled using the active mechanism. 102e38da020SMax Reitz # The mirror code itself asserts that the source BDS's dirty 103e38da020SMax Reitz # bitmap will stay clean between READY and COMPLETED. 1049a3a9a63SMax Reitz for offset in range(5 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): 105e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) 1069a3a9a63SMax Reitz for offset in range(6 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): 107e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 108e38da020SMax Reitz 109e38da020SMax Reitz if sync_source_and_target: 110e38da020SMax Reitz # If source and target should be in sync after the mirror, 111e38da020SMax Reitz # we have to flush before completion 112e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_flush') 113e38da020SMax Reitz self.potential_writes_in_flight = False 114e38da020SMax Reitz 115e38da020SMax Reitz self.complete_and_wait(drive='mirror', wait_ready=False) 116e38da020SMax Reitz 117e38da020SMax Reitz def testActiveIO(self): 118e38da020SMax Reitz self.doActiveIO(False) 119e38da020SMax Reitz 120e38da020SMax Reitz def testActiveIOFlushed(self): 121e38da020SMax Reitz self.doActiveIO(True) 122e38da020SMax Reitz 12319ba4651SMax Reitz def testUnalignedActiveIO(self): 12419ba4651SMax Reitz # Fill the source image 12519ba4651SMax Reitz result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') 12619ba4651SMax Reitz 12719ba4651SMax Reitz # Start the block job (very slowly) 12819ba4651SMax Reitz result = self.vm.qmp('blockdev-mirror', 12919ba4651SMax Reitz job_id='mirror', 13019ba4651SMax Reitz filter_node_name='mirror-node', 13119ba4651SMax Reitz device='source-node', 13219ba4651SMax Reitz target='target-node', 13319ba4651SMax Reitz sync='full', 13419ba4651SMax Reitz copy_mode='write-blocking', 13519ba4651SMax Reitz buf_size=(1048576 // 4), 13619ba4651SMax Reitz speed=1) 13719ba4651SMax Reitz self.assert_qmp(result, 'return', {}) 13819ba4651SMax Reitz 13919ba4651SMax Reitz # Start an unaligned request to a dirty area 14019ba4651SMax Reitz result = self.vm.hmp_qemu_io('source', 'write -P 2 %i 1' % (1048576 + 42)) 14119ba4651SMax Reitz 14219ba4651SMax Reitz # Let the job finish 14319ba4651SMax Reitz result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0) 14419ba4651SMax Reitz self.assert_qmp(result, 'return', {}) 14519ba4651SMax Reitz self.complete_and_wait(drive='mirror') 14619ba4651SMax Reitz 14719ba4651SMax Reitz self.potential_writes_in_flight = False 148e38da020SMax Reitz 149e0f69d83SVladimir Sementsov-Ogievskiy def testIntersectingActiveIO(self): 150e0f69d83SVladimir Sementsov-Ogievskiy # Fill the source image 151e0f69d83SVladimir Sementsov-Ogievskiy result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') 152e0f69d83SVladimir Sementsov-Ogievskiy 153e0f69d83SVladimir Sementsov-Ogievskiy # Start the block job (very slowly) 154e0f69d83SVladimir Sementsov-Ogievskiy result = self.vm.qmp('blockdev-mirror', 155e0f69d83SVladimir Sementsov-Ogievskiy job_id='mirror', 156e0f69d83SVladimir Sementsov-Ogievskiy filter_node_name='mirror-node', 157e0f69d83SVladimir Sementsov-Ogievskiy device='source-node', 158e0f69d83SVladimir Sementsov-Ogievskiy target='target-node', 159e0f69d83SVladimir Sementsov-Ogievskiy sync='full', 160e0f69d83SVladimir Sementsov-Ogievskiy copy_mode='write-blocking', 161e0f69d83SVladimir Sementsov-Ogievskiy speed=1) 162e0f69d83SVladimir Sementsov-Ogievskiy 163e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'break write_aio A') 164e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 0 1M') # 1 165e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'wait_break A') 166e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 0 2M') # 2 167e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 0 2M') # 3 168e0f69d83SVladimir Sementsov-Ogievskiy 169e0f69d83SVladimir Sementsov-Ogievskiy # Now 2 and 3 are in mirror_wait_on_conflicts, waiting for 1 170e0f69d83SVladimir Sementsov-Ogievskiy 171e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'break write_aio B') 172e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'aio_write 1M 2M') # 4 173e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'wait_break B') 174e0f69d83SVladimir Sementsov-Ogievskiy 175e0f69d83SVladimir Sementsov-Ogievskiy # 4 doesn't wait for 2 and 3, because they didn't yet set 176e0f69d83SVladimir Sementsov-Ogievskiy # in_flight_bitmap. So, nothing prevents 4 to go except for our 177e0f69d83SVladimir Sementsov-Ogievskiy # break-point B. 178e0f69d83SVladimir Sementsov-Ogievskiy 179e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'resume A') 180e0f69d83SVladimir Sementsov-Ogievskiy 181e0f69d83SVladimir Sementsov-Ogievskiy # Now we resumed 1, so 2 and 3 goes to the next iteration of while loop 182e0f69d83SVladimir Sementsov-Ogievskiy # in mirror_wait_on_conflicts(). They don't exit, as bitmap is dirty 183d44dae1aSVladimir Sementsov-Ogievskiy # due to request 4. 184d44dae1aSVladimir Sementsov-Ogievskiy # In the past at that point 2 and 3 would wait for each other producing 185d44dae1aSVladimir Sementsov-Ogievskiy # a dead-lock. Now this is fixed and they will wait for request 4. 186e0f69d83SVladimir Sementsov-Ogievskiy 187e0f69d83SVladimir Sementsov-Ogievskiy self.vm.hmp_qemu_io('source', 'resume B') 188e0f69d83SVladimir Sementsov-Ogievskiy 189d44dae1aSVladimir Sementsov-Ogievskiy # After resuming 4, one of 2 and 3 goes first and set in_flight_bitmap, 190d44dae1aSVladimir Sementsov-Ogievskiy # so the other will wait for it. 191e0f69d83SVladimir Sementsov-Ogievskiy 192e0f69d83SVladimir Sementsov-Ogievskiy result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0) 193e0f69d83SVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'return', {}) 194e0f69d83SVladimir Sementsov-Ogievskiy self.complete_and_wait(drive='mirror') 195e0f69d83SVladimir Sementsov-Ogievskiy 196e0f69d83SVladimir Sementsov-Ogievskiy self.potential_writes_in_flight = False 197e0f69d83SVladimir Sementsov-Ogievskiy 198e38da020SMax Reitz 199*7b5929c7SHanna Reitzclass TestThrottledWithNbdExportBase(iotests.QMPTestCase): 20038591290SHanna Reitz image_len = 128 * 1024 * 1024 # MB 201*7b5929c7SHanna Reitz iops: Optional[int] = None 20238591290SHanna Reitz background_processes: List['subprocess.Popen[str]'] = [] 20338591290SHanna Reitz 20438591290SHanna Reitz def setUp(self): 205*7b5929c7SHanna Reitz # Must be set by subclasses 206*7b5929c7SHanna Reitz self.assertIsNotNone(self.iops) 207*7b5929c7SHanna Reitz 20838591290SHanna Reitz qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') 20938591290SHanna Reitz qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') 21038591290SHanna Reitz 21138591290SHanna Reitz self.vm = iotests.VM() 21238591290SHanna Reitz self.vm.launch() 21338591290SHanna Reitz 21438591290SHanna Reitz result = self.vm.qmp('object-add', **{ 21538591290SHanna Reitz 'qom-type': 'throttle-group', 21638591290SHanna Reitz 'id': 'thrgr', 21738591290SHanna Reitz 'limits': { 21838591290SHanna Reitz 'iops-total': self.iops, 21938591290SHanna Reitz 'iops-total-max': self.iops 22038591290SHanna Reitz } 22138591290SHanna Reitz }) 22238591290SHanna Reitz self.assert_qmp(result, 'return', {}) 22338591290SHanna Reitz 22438591290SHanna Reitz result = self.vm.qmp('blockdev-add', **{ 22538591290SHanna Reitz 'node-name': 'source-node', 22638591290SHanna Reitz 'driver': 'throttle', 22738591290SHanna Reitz 'throttle-group': 'thrgr', 22838591290SHanna Reitz 'file': { 22938591290SHanna Reitz 'driver': iotests.imgfmt, 23038591290SHanna Reitz 'file': { 23138591290SHanna Reitz 'driver': 'file', 23238591290SHanna Reitz 'filename': source_img 23338591290SHanna Reitz } 23438591290SHanna Reitz } 23538591290SHanna Reitz }) 23638591290SHanna Reitz self.assert_qmp(result, 'return', {}) 23738591290SHanna Reitz 23838591290SHanna Reitz result = self.vm.qmp('blockdev-add', **{ 23938591290SHanna Reitz 'node-name': 'target-node', 24038591290SHanna Reitz 'driver': iotests.imgfmt, 24138591290SHanna Reitz 'file': { 24238591290SHanna Reitz 'driver': 'file', 24338591290SHanna Reitz 'filename': target_img 24438591290SHanna Reitz } 24538591290SHanna Reitz }) 24638591290SHanna Reitz self.assert_qmp(result, 'return', {}) 24738591290SHanna Reitz 24838591290SHanna Reitz self.nbd_sock = iotests.file_path('nbd.sock', 24938591290SHanna Reitz base_dir=iotests.sock_dir) 25038591290SHanna Reitz self.nbd_url = f'nbd+unix:///source-node?socket={self.nbd_sock}' 25138591290SHanna Reitz 25238591290SHanna Reitz result = self.vm.qmp('nbd-server-start', addr={ 25338591290SHanna Reitz 'type': 'unix', 25438591290SHanna Reitz 'data': { 25538591290SHanna Reitz 'path': self.nbd_sock 25638591290SHanna Reitz } 25738591290SHanna Reitz }) 25838591290SHanna Reitz self.assert_qmp(result, 'return', {}) 25938591290SHanna Reitz 26038591290SHanna Reitz result = self.vm.qmp('block-export-add', id='exp0', type='nbd', 26138591290SHanna Reitz node_name='source-node', writable=True) 26238591290SHanna Reitz self.assert_qmp(result, 'return', {}) 26338591290SHanna Reitz 26438591290SHanna Reitz def tearDown(self): 26538591290SHanna Reitz # Wait for background requests to settle 26638591290SHanna Reitz try: 26738591290SHanna Reitz while True: 26838591290SHanna Reitz p = self.background_processes.pop() 26938591290SHanna Reitz while True: 27038591290SHanna Reitz try: 27138591290SHanna Reitz p.wait(timeout=0.0) 27238591290SHanna Reitz break 27338591290SHanna Reitz except subprocess.TimeoutExpired: 27438591290SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 27538591290SHanna Reitz except IndexError: 27638591290SHanna Reitz pass 27738591290SHanna Reitz 27838591290SHanna Reitz # Cancel ongoing block jobs 27938591290SHanna Reitz for job in self.vm.qmp('query-jobs')['return']: 28038591290SHanna Reitz self.vm.qmp('block-job-cancel', device=job['id'], force=True) 28138591290SHanna Reitz 28238591290SHanna Reitz while True: 28338591290SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 28438591290SHanna Reitz if len(self.vm.qmp('query-jobs')['return']) == 0: 28538591290SHanna Reitz break 28638591290SHanna Reitz 28738591290SHanna Reitz self.vm.shutdown() 28838591290SHanna Reitz os.remove(source_img) 28938591290SHanna Reitz os.remove(target_img) 29038591290SHanna Reitz 291*7b5929c7SHanna Reitz 292*7b5929c7SHanna Reitzclass TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase): 293*7b5929c7SHanna Reitz iops = 16 294*7b5929c7SHanna Reitz 29538591290SHanna Reitz def testUnderLoad(self): 29638591290SHanna Reitz ''' 29738591290SHanna Reitz Throttle the source node, then issue a whole bunch of external requests 29838591290SHanna Reitz while the mirror job (in write-blocking mode) is running. We want to 29938591290SHanna Reitz see background requests being issued even while the source is under 30038591290SHanna Reitz full load by active writes, so that progress can be made towards READY. 30138591290SHanna Reitz ''' 30238591290SHanna Reitz 30338591290SHanna Reitz # Fill the first half of the source image; do not fill the second half, 30438591290SHanna Reitz # that is where we will have active requests occur. This ensures that 30538591290SHanna Reitz # active mirroring itself will not directly contribute to the job's 30638591290SHanna Reitz # progress (because when the job was started, those areas were not 30738591290SHanna Reitz # intended to be copied, so active mirroring will only lead to not 30838591290SHanna Reitz # losing progress, but also not making any). 30938591290SHanna Reitz self.vm.hmp_qemu_io('source-node', 31038591290SHanna Reitz f'aio_write -P 1 0 {self.image_len // 2}') 31138591290SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 31238591290SHanna Reitz 31338591290SHanna Reitz # Launch the mirror job 31438591290SHanna Reitz mirror_buf_size = 65536 31538591290SHanna Reitz result = self.vm.qmp('blockdev-mirror', 31638591290SHanna Reitz job_id='mirror', 31738591290SHanna Reitz filter_node_name='mirror-node', 31838591290SHanna Reitz device='source-node', 31938591290SHanna Reitz target='target-node', 32038591290SHanna Reitz sync='full', 32138591290SHanna Reitz copy_mode='write-blocking', 32238591290SHanna Reitz buf_size=mirror_buf_size) 32338591290SHanna Reitz self.assert_qmp(result, 'return', {}) 32438591290SHanna Reitz 32538591290SHanna Reitz # We create the external requests via qemu-io processes on the NBD 32638591290SHanna Reitz # server. Have their offset start in the middle of the image so they 32738591290SHanna Reitz # do not overlap with the background requests (which start from the 32838591290SHanna Reitz # beginning). 32938591290SHanna Reitz active_request_offset = self.image_len // 2 33038591290SHanna Reitz active_request_len = 4096 33138591290SHanna Reitz 33238591290SHanna Reitz # Create enough requests to saturate the node for 5 seconds 33338591290SHanna Reitz for _ in range(0, 5 * self.iops): 33438591290SHanna Reitz req = f'write -P 42 {active_request_offset} {active_request_len}' 33538591290SHanna Reitz active_request_offset += active_request_len 33638591290SHanna Reitz p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req) 33738591290SHanna Reitz self.background_processes += [p] 33838591290SHanna Reitz 33938591290SHanna Reitz # Now advance the clock one I/O operation at a time by the 4 seconds 34038591290SHanna Reitz # (i.e. one less than 5). We expect the mirror job to issue background 34138591290SHanna Reitz # operations here, even though active requests are still in flight. 34238591290SHanna Reitz # The active requests will take precedence, however, because they have 34338591290SHanna Reitz # been issued earlier than mirror's background requests. 34438591290SHanna Reitz # Once the active requests we have started above are done (i.e. after 5 34538591290SHanna Reitz # virtual seconds), we expect those background requests to be worked 34638591290SHanna Reitz # on. We only advance 4 seconds here to avoid race conditions. 34738591290SHanna Reitz for _ in range(0, 4 * self.iops): 34838591290SHanna Reitz step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops) 34938591290SHanna Reitz self.vm.qtest(f'clock_step {step}') 35038591290SHanna Reitz 35138591290SHanna Reitz # Note how much remains to be done until the mirror job is finished 35238591290SHanna Reitz job_status = self.vm.qmp('query-jobs')['return'][0] 35338591290SHanna Reitz start_remaining = job_status['total-progress'] - \ 35438591290SHanna Reitz job_status['current-progress'] 35538591290SHanna Reitz 35638591290SHanna Reitz # Create a whole bunch of more active requests 35738591290SHanna Reitz for _ in range(0, 10 * self.iops): 35838591290SHanna Reitz req = f'write -P 42 {active_request_offset} {active_request_len}' 35938591290SHanna Reitz active_request_offset += active_request_len 36038591290SHanna Reitz p = iotests.qemu_io_popen('-f', 'nbd', self.nbd_url, '-c', req) 36138591290SHanna Reitz self.background_processes += [p] 36238591290SHanna Reitz 36338591290SHanna Reitz # Let the clock advance more. After 1 second, as noted above, we 36438591290SHanna Reitz # expect the background requests to be worked on. Give them a couple 36538591290SHanna Reitz # of seconds (specifically 4) to see their impact. 36638591290SHanna Reitz for _ in range(0, 5 * self.iops): 36738591290SHanna Reitz step = math.ceil(1 * 1000 * 1000 * 1000 / self.iops) 36838591290SHanna Reitz self.vm.qtest(f'clock_step {step}') 36938591290SHanna Reitz 37038591290SHanna Reitz # Note how much remains to be done now. We expect this number to be 37138591290SHanna Reitz # reduced thanks to those background requests. 37238591290SHanna Reitz job_status = self.vm.qmp('query-jobs')['return'][0] 37338591290SHanna Reitz end_remaining = job_status['total-progress'] - \ 37438591290SHanna Reitz job_status['current-progress'] 37538591290SHanna Reitz 37638591290SHanna Reitz # See that indeed progress was being made on the job, even while the 37738591290SHanna Reitz # node was saturated with active requests 37838591290SHanna Reitz self.assertGreater(start_remaining - end_remaining, 0) 37938591290SHanna Reitz 38038591290SHanna Reitz 381*7b5929c7SHanna Reitzclass TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase): 382*7b5929c7SHanna Reitz iops = 1024 383*7b5929c7SHanna Reitz 384*7b5929c7SHanna Reitz def testActiveOnCreation(self): 385*7b5929c7SHanna Reitz ''' 386*7b5929c7SHanna Reitz Issue requests on the mirror source node right as the mirror is 387*7b5929c7SHanna Reitz instated. It's possible that requests occur before the actual job is 388*7b5929c7SHanna Reitz created, but after the node has been put into the graph. Write 389*7b5929c7SHanna Reitz requests across the node must in that case be forwarded to the source 390*7b5929c7SHanna Reitz node without attempting to mirror them (there is no job object yet, so 391*7b5929c7SHanna Reitz attempting to access it would cause a segfault). 392*7b5929c7SHanna Reitz We do this with a lightly throttled node (i.e. quite high IOPS limit). 393*7b5929c7SHanna Reitz Using throttling seems to increase reproductivity, but if the limit is 394*7b5929c7SHanna Reitz too low, all requests allowed per second will be submitted before 395*7b5929c7SHanna Reitz mirror_start_job() gets to the problematic point. 396*7b5929c7SHanna Reitz ''' 397*7b5929c7SHanna Reitz 398*7b5929c7SHanna Reitz # Let qemu-img bench create write requests (enough for two seconds on 399*7b5929c7SHanna Reitz # the virtual clock) 400*7b5929c7SHanna Reitz bench_args = ['bench', '-w', '-d', '1024', '-f', 'nbd', 401*7b5929c7SHanna Reitz '-c', str(self.iops * 2), self.nbd_url] 402*7b5929c7SHanna Reitz p = iotests.qemu_tool_popen(iotests.qemu_img_args + bench_args) 403*7b5929c7SHanna Reitz self.background_processes += [p] 404*7b5929c7SHanna Reitz 405*7b5929c7SHanna Reitz # Give qemu-img bench time to start up and issue requests 406*7b5929c7SHanna Reitz time.sleep(1.0) 407*7b5929c7SHanna Reitz # Flush the request queue, so new requests can come in right as we 408*7b5929c7SHanna Reitz # start blockdev-mirror 409*7b5929c7SHanna Reitz self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') 410*7b5929c7SHanna Reitz 411*7b5929c7SHanna Reitz result = self.vm.qmp('blockdev-mirror', 412*7b5929c7SHanna Reitz job_id='mirror', 413*7b5929c7SHanna Reitz device='source-node', 414*7b5929c7SHanna Reitz target='target-node', 415*7b5929c7SHanna Reitz sync='full', 416*7b5929c7SHanna Reitz copy_mode='write-blocking') 417*7b5929c7SHanna Reitz self.assert_qmp(result, 'return', {}) 418*7b5929c7SHanna Reitz 419*7b5929c7SHanna Reitz 420e38da020SMax Reitzif __name__ == '__main__': 421103cbc77SMax Reitz iotests.main(supported_fmts=['qcow2', 'raw'], 422103cbc77SMax Reitz supported_protocols=['file']) 423