1e38da020SMax Reitz#!/usr/bin/env python 2e38da020SMax Reitz# 3e38da020SMax Reitz# Tests for active mirroring 4e38da020SMax Reitz# 5e38da020SMax Reitz# Copyright (C) 2018 Red Hat, Inc. 6e38da020SMax Reitz# 7e38da020SMax Reitz# This program is free software; you can redistribute it and/or modify 8e38da020SMax Reitz# it under the terms of the GNU General Public License as published by 9e38da020SMax Reitz# the Free Software Foundation; either version 2 of the License, or 10e38da020SMax Reitz# (at your option) any later version. 11e38da020SMax Reitz# 12e38da020SMax Reitz# This program is distributed in the hope that it will be useful, 13e38da020SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 14e38da020SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15e38da020SMax Reitz# GNU General Public License for more details. 16e38da020SMax Reitz# 17e38da020SMax Reitz# You should have received a copy of the GNU General Public License 18e38da020SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 19e38da020SMax Reitz# 20e38da020SMax Reitz 21e38da020SMax Reitzimport os 22e38da020SMax Reitzimport iotests 23e38da020SMax Reitzfrom iotests import qemu_img 24e38da020SMax Reitz 25e38da020SMax Reitzsource_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) 26e38da020SMax Reitztarget_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) 27e38da020SMax Reitz 28e38da020SMax Reitzclass TestActiveMirror(iotests.QMPTestCase): 29e38da020SMax Reitz image_len = 128 * 1024 * 1024 # MB 30e38da020SMax Reitz potential_writes_in_flight = True 31e38da020SMax Reitz 32e38da020SMax Reitz def setUp(self): 33e38da020SMax Reitz qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') 34e38da020SMax Reitz qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') 35e38da020SMax Reitz 36e38da020SMax Reitz blk_source = {'id': 'source', 37e38da020SMax Reitz 'if': 'none', 38e38da020SMax Reitz 'node-name': 'source-node', 39e38da020SMax Reitz 'driver': iotests.imgfmt, 40e38da020SMax Reitz 'file': {'driver': 'file', 41e38da020SMax Reitz 'filename': source_img}} 42e38da020SMax Reitz 43e38da020SMax Reitz blk_target = {'node-name': 'target-node', 44e38da020SMax Reitz 'driver': iotests.imgfmt, 45e38da020SMax Reitz 'file': {'driver': 'file', 46e38da020SMax Reitz 'filename': target_img}} 47e38da020SMax Reitz 48e38da020SMax Reitz self.vm = iotests.VM() 49e38da020SMax Reitz self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source)) 50e38da020SMax Reitz self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target)) 51e38da020SMax Reitz self.vm.add_device('virtio-blk,drive=source') 52e38da020SMax Reitz self.vm.launch() 53e38da020SMax Reitz 54e38da020SMax Reitz def tearDown(self): 55e38da020SMax Reitz self.vm.shutdown() 56e38da020SMax Reitz 57e38da020SMax Reitz if not self.potential_writes_in_flight: 58e38da020SMax Reitz self.assertTrue(iotests.compare_images(source_img, target_img), 59e38da020SMax Reitz 'mirror target does not match source') 60e38da020SMax Reitz 61e38da020SMax Reitz os.remove(source_img) 62e38da020SMax Reitz os.remove(target_img) 63e38da020SMax Reitz 64e38da020SMax Reitz def doActiveIO(self, sync_source_and_target): 65e38da020SMax Reitz # Fill the source image 66e38da020SMax Reitz self.vm.hmp_qemu_io('source', 67e38da020SMax Reitz 'write -P 1 0 %i' % self.image_len); 68e38da020SMax Reitz 69e38da020SMax Reitz # Start some background requests 709a3a9a63SMax Reitz for offset in range(1 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024): 71e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset) 729a3a9a63SMax Reitz for offset in range(2 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024): 73e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 74e38da020SMax Reitz 75e38da020SMax Reitz # Start the block job 76e38da020SMax Reitz result = self.vm.qmp('blockdev-mirror', 77e38da020SMax Reitz job_id='mirror', 78e38da020SMax Reitz filter_node_name='mirror-node', 79e38da020SMax Reitz device='source-node', 80e38da020SMax Reitz target='target-node', 81e38da020SMax Reitz sync='full', 82e38da020SMax Reitz copy_mode='write-blocking') 83e38da020SMax Reitz self.assert_qmp(result, 'return', {}) 84e38da020SMax Reitz 85e38da020SMax Reitz # Start some more requests 869a3a9a63SMax Reitz for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): 87e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) 889a3a9a63SMax Reitz for offset in range(4 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): 89e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 90e38da020SMax Reitz 91e38da020SMax Reitz # Wait for the READY event 92e38da020SMax Reitz self.wait_ready(drive='mirror') 93e38da020SMax Reitz 94e38da020SMax Reitz # Now start some final requests; all of these (which land on 95e38da020SMax Reitz # the source) should be settled using the active mechanism. 96e38da020SMax Reitz # The mirror code itself asserts that the source BDS's dirty 97e38da020SMax Reitz # bitmap will stay clean between READY and COMPLETED. 989a3a9a63SMax Reitz for offset in range(5 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): 99e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) 1009a3a9a63SMax Reitz for offset in range(6 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): 101e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) 102e38da020SMax Reitz 103e38da020SMax Reitz if sync_source_and_target: 104e38da020SMax Reitz # If source and target should be in sync after the mirror, 105e38da020SMax Reitz # we have to flush before completion 106e38da020SMax Reitz self.vm.hmp_qemu_io('source', 'aio_flush') 107e38da020SMax Reitz self.potential_writes_in_flight = False 108e38da020SMax Reitz 109e38da020SMax Reitz self.complete_and_wait(drive='mirror', wait_ready=False) 110e38da020SMax Reitz 111e38da020SMax Reitz def testActiveIO(self): 112e38da020SMax Reitz self.doActiveIO(False) 113e38da020SMax Reitz 114e38da020SMax Reitz def testActiveIOFlushed(self): 115e38da020SMax Reitz self.doActiveIO(True) 116e38da020SMax Reitz 117*19ba4651SMax Reitz def testUnalignedActiveIO(self): 118*19ba4651SMax Reitz # Fill the source image 119*19ba4651SMax Reitz result = self.vm.hmp_qemu_io('source', 'write -P 1 0 2M') 120*19ba4651SMax Reitz 121*19ba4651SMax Reitz # Start the block job (very slowly) 122*19ba4651SMax Reitz result = self.vm.qmp('blockdev-mirror', 123*19ba4651SMax Reitz job_id='mirror', 124*19ba4651SMax Reitz filter_node_name='mirror-node', 125*19ba4651SMax Reitz device='source-node', 126*19ba4651SMax Reitz target='target-node', 127*19ba4651SMax Reitz sync='full', 128*19ba4651SMax Reitz copy_mode='write-blocking', 129*19ba4651SMax Reitz buf_size=(1048576 // 4), 130*19ba4651SMax Reitz speed=1) 131*19ba4651SMax Reitz self.assert_qmp(result, 'return', {}) 132*19ba4651SMax Reitz 133*19ba4651SMax Reitz # Start an unaligned request to a dirty area 134*19ba4651SMax Reitz result = self.vm.hmp_qemu_io('source', 'write -P 2 %i 1' % (1048576 + 42)) 135*19ba4651SMax Reitz 136*19ba4651SMax Reitz # Let the job finish 137*19ba4651SMax Reitz result = self.vm.qmp('block-job-set-speed', device='mirror', speed=0) 138*19ba4651SMax Reitz self.assert_qmp(result, 'return', {}) 139*19ba4651SMax Reitz self.complete_and_wait(drive='mirror') 140*19ba4651SMax Reitz 141*19ba4651SMax Reitz self.potential_writes_in_flight = False 142e38da020SMax Reitz 143e38da020SMax Reitz 144e38da020SMax Reitzif __name__ == '__main__': 145e38da020SMax Reitz iotests.main(supported_fmts=['qcow2', 'raw']) 146