1dd3e97dfSVladimir Sementsov-Ogievskiy#!/usr/bin/env python3 2dd3e97dfSVladimir Sementsov-Ogievskiy# group: auto backup 3dd3e97dfSVladimir Sementsov-Ogievskiy# 4dd3e97dfSVladimir Sementsov-Ogievskiy# Copyright (c) 2022 Virtuozzo International GmbH 5dd3e97dfSVladimir Sementsov-Ogievskiy# 6dd3e97dfSVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 7dd3e97dfSVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 8dd3e97dfSVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 9dd3e97dfSVladimir Sementsov-Ogievskiy# (at your option) any later version. 10dd3e97dfSVladimir Sementsov-Ogievskiy# 11dd3e97dfSVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 12dd3e97dfSVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 13dd3e97dfSVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14dd3e97dfSVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 15dd3e97dfSVladimir Sementsov-Ogievskiy# 16dd3e97dfSVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 17dd3e97dfSVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 18dd3e97dfSVladimir Sementsov-Ogievskiy# 19dd3e97dfSVladimir Sementsov-Ogievskiy 20dd3e97dfSVladimir Sementsov-Ogievskiyimport os 21dd3e97dfSVladimir Sementsov-Ogievskiyimport re 22dd3e97dfSVladimir Sementsov-Ogievskiy 23dd3e97dfSVladimir Sementsov-Ogievskiyfrom qemu.machine import QEMUMachine 24dd3e97dfSVladimir Sementsov-Ogievskiy 25dd3e97dfSVladimir Sementsov-Ogievskiyimport iotests 26dd3e97dfSVladimir Sementsov-Ogievskiyfrom iotests import qemu_img_create, qemu_io 27dd3e97dfSVladimir Sementsov-Ogievskiy 28dd3e97dfSVladimir Sementsov-Ogievskiy 29dd3e97dfSVladimir Sementsov-Ogievskiytemp_img = os.path.join(iotests.test_dir, 'temp') 30dd3e97dfSVladimir Sementsov-Ogievskiysource_img = os.path.join(iotests.test_dir, 'source') 31dd3e97dfSVladimir Sementsov-Ogievskiysize = '1M' 32dd3e97dfSVladimir Sementsov-Ogievskiy 33dd3e97dfSVladimir Sementsov-Ogievskiy 34dd3e97dfSVladimir Sementsov-Ogievskiyclass TestCbwError(iotests.QMPTestCase): 35dd3e97dfSVladimir Sementsov-Ogievskiy def tearDown(self): 36dd3e97dfSVladimir Sementsov-Ogievskiy self.vm.shutdown() 37dd3e97dfSVladimir Sementsov-Ogievskiy os.remove(temp_img) 38dd3e97dfSVladimir Sementsov-Ogievskiy os.remove(source_img) 39dd3e97dfSVladimir Sementsov-Ogievskiy 40dd3e97dfSVladimir Sementsov-Ogievskiy def setUp(self): 41dd3e97dfSVladimir Sementsov-Ogievskiy qemu_img_create('-f', iotests.imgfmt, source_img, size) 42dd3e97dfSVladimir Sementsov-Ogievskiy qemu_img_create('-f', iotests.imgfmt, temp_img, size) 43dd3e97dfSVladimir Sementsov-Ogievskiy qemu_io('-c', 'write 0 1M', source_img) 44dd3e97dfSVladimir Sementsov-Ogievskiy 45dd3e97dfSVladimir Sementsov-Ogievskiy opts = ['-nodefaults', '-display', 'none', '-machine', 'none'] 46dd3e97dfSVladimir Sementsov-Ogievskiy self.vm = QEMUMachine(iotests.qemu_prog, opts, 4746d4747aSJohn Snow base_temp_dir=iotests.test_dir) 48dd3e97dfSVladimir Sementsov-Ogievskiy self.vm.launch() 49dd3e97dfSVladimir Sementsov-Ogievskiy 50dd3e97dfSVladimir Sementsov-Ogievskiy def do_cbw_error(self, on_cbw_error): 51b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', { 52dd3e97dfSVladimir Sementsov-Ogievskiy 'node-name': 'cbw', 53dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': 'copy-before-write', 54dd3e97dfSVladimir Sementsov-Ogievskiy 'on-cbw-error': on_cbw_error, 55dd3e97dfSVladimir Sementsov-Ogievskiy 'file': { 56dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': iotests.imgfmt, 57dd3e97dfSVladimir Sementsov-Ogievskiy 'file': { 58dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': 'file', 59dd3e97dfSVladimir Sementsov-Ogievskiy 'filename': source_img, 60dd3e97dfSVladimir Sementsov-Ogievskiy } 61dd3e97dfSVladimir Sementsov-Ogievskiy }, 62dd3e97dfSVladimir Sementsov-Ogievskiy 'target': { 63dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': iotests.imgfmt, 64dd3e97dfSVladimir Sementsov-Ogievskiy 'file': { 65dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': 'blkdebug', 66dd3e97dfSVladimir Sementsov-Ogievskiy 'image': { 67dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': 'file', 68dd3e97dfSVladimir Sementsov-Ogievskiy 'filename': temp_img 69dd3e97dfSVladimir Sementsov-Ogievskiy }, 70dd3e97dfSVladimir Sementsov-Ogievskiy 'inject-error': [ 71dd3e97dfSVladimir Sementsov-Ogievskiy { 72dd3e97dfSVladimir Sementsov-Ogievskiy 'event': 'write_aio', 73dd3e97dfSVladimir Sementsov-Ogievskiy 'errno': 5, 74dd3e97dfSVladimir Sementsov-Ogievskiy 'immediately': False, 75dd3e97dfSVladimir Sementsov-Ogievskiy 'once': True 76dd3e97dfSVladimir Sementsov-Ogievskiy } 77dd3e97dfSVladimir Sementsov-Ogievskiy ] 78dd3e97dfSVladimir Sementsov-Ogievskiy } 79dd3e97dfSVladimir Sementsov-Ogievskiy } 80dd3e97dfSVladimir Sementsov-Ogievskiy }) 81dd3e97dfSVladimir Sementsov-Ogievskiy 82b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', { 83dd3e97dfSVladimir Sementsov-Ogievskiy 'node-name': 'access', 84dd3e97dfSVladimir Sementsov-Ogievskiy 'driver': 'snapshot-access', 85dd3e97dfSVladimir Sementsov-Ogievskiy 'file': 'cbw' 86dd3e97dfSVladimir Sementsov-Ogievskiy }) 87dd3e97dfSVladimir Sementsov-Ogievskiy 88dd3e97dfSVladimir Sementsov-Ogievskiy result = self.vm.qmp('human-monitor-command', 89dd3e97dfSVladimir Sementsov-Ogievskiy command_line='qemu-io cbw "write 0 1M"') 90dd3e97dfSVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'return', '') 91dd3e97dfSVladimir Sementsov-Ogievskiy 92dd3e97dfSVladimir Sementsov-Ogievskiy result = self.vm.qmp('human-monitor-command', 93dd3e97dfSVladimir Sementsov-Ogievskiy command_line='qemu-io access "read 0 1M"') 94dd3e97dfSVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'return', '') 95dd3e97dfSVladimir Sementsov-Ogievskiy 96dd3e97dfSVladimir Sementsov-Ogievskiy self.vm.shutdown() 97dd3e97dfSVladimir Sementsov-Ogievskiy log = self.vm.get_log() 98dd3e97dfSVladimir Sementsov-Ogievskiy log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) 99dd3e97dfSVladimir Sementsov-Ogievskiy log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) 100dd3e97dfSVladimir Sementsov-Ogievskiy log = iotests.filter_qemu_io(log) 101dd3e97dfSVladimir Sementsov-Ogievskiy return log 102dd3e97dfSVladimir Sementsov-Ogievskiy 103dd3e97dfSVladimir Sementsov-Ogievskiy def test_break_snapshot_on_cbw_error(self): 104dd3e97dfSVladimir Sementsov-Ogievskiy """break-snapshot behavior: 105dd3e97dfSVladimir Sementsov-Ogievskiy Guest write succeed, but further snapshot-read fails, as snapshot is 106dd3e97dfSVladimir Sementsov-Ogievskiy broken. 107dd3e97dfSVladimir Sementsov-Ogievskiy """ 108dd3e97dfSVladimir Sementsov-Ogievskiy log = self.do_cbw_error('break-snapshot') 109dd3e97dfSVladimir Sementsov-Ogievskiy 110dd3e97dfSVladimir Sementsov-Ogievskiy self.assertEqual(log, """\ 111dd3e97dfSVladimir Sementsov-Ogievskiywrote 1048576/1048576 bytes at offset 0 112dd3e97dfSVladimir Sementsov-Ogievskiy1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 113dd3e97dfSVladimir Sementsov-Ogievskiyread failed: Permission denied 114dd3e97dfSVladimir Sementsov-Ogievskiy""") 115dd3e97dfSVladimir Sementsov-Ogievskiy 116dd3e97dfSVladimir Sementsov-Ogievskiy def test_break_guest_write_on_cbw_error(self): 117dd3e97dfSVladimir Sementsov-Ogievskiy """break-guest-write behavior: 118dd3e97dfSVladimir Sementsov-Ogievskiy Guest write fails, but snapshot-access continues working and further 119dd3e97dfSVladimir Sementsov-Ogievskiy snapshot-read succeeds. 120dd3e97dfSVladimir Sementsov-Ogievskiy """ 121dd3e97dfSVladimir Sementsov-Ogievskiy log = self.do_cbw_error('break-guest-write') 122dd3e97dfSVladimir Sementsov-Ogievskiy 123dd3e97dfSVladimir Sementsov-Ogievskiy self.assertEqual(log, """\ 124dd3e97dfSVladimir Sementsov-Ogievskiywrite failed: Input/output error 125dd3e97dfSVladimir Sementsov-Ogievskiyread 1048576/1048576 bytes at offset 0 126dd3e97dfSVladimir Sementsov-Ogievskiy1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 127dd3e97dfSVladimir Sementsov-Ogievskiy""") 128dd3e97dfSVladimir Sementsov-Ogievskiy 1299d05a87bSVladimir Sementsov-Ogievskiy def do_cbw_timeout(self, on_cbw_error): 130b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('object-add', { 1319d05a87bSVladimir Sementsov-Ogievskiy 'qom-type': 'throttle-group', 1329d05a87bSVladimir Sementsov-Ogievskiy 'id': 'group0', 1339d05a87bSVladimir Sementsov-Ogievskiy 'limits': {'bps-write': 300 * 1024} 1349d05a87bSVladimir Sementsov-Ogievskiy }) 1359d05a87bSVladimir Sementsov-Ogievskiy 136b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', { 1379d05a87bSVladimir Sementsov-Ogievskiy 'node-name': 'cbw', 1389d05a87bSVladimir Sementsov-Ogievskiy 'driver': 'copy-before-write', 1399d05a87bSVladimir Sementsov-Ogievskiy 'on-cbw-error': on_cbw_error, 1409d05a87bSVladimir Sementsov-Ogievskiy 'cbw-timeout': 1, 1419d05a87bSVladimir Sementsov-Ogievskiy 'file': { 1429d05a87bSVladimir Sementsov-Ogievskiy 'driver': iotests.imgfmt, 1439d05a87bSVladimir Sementsov-Ogievskiy 'file': { 1449d05a87bSVladimir Sementsov-Ogievskiy 'driver': 'file', 1459d05a87bSVladimir Sementsov-Ogievskiy 'filename': source_img, 1469d05a87bSVladimir Sementsov-Ogievskiy } 1479d05a87bSVladimir Sementsov-Ogievskiy }, 1489d05a87bSVladimir Sementsov-Ogievskiy 'target': { 1499d05a87bSVladimir Sementsov-Ogievskiy 'driver': 'throttle', 1509d05a87bSVladimir Sementsov-Ogievskiy 'throttle-group': 'group0', 1519d05a87bSVladimir Sementsov-Ogievskiy 'file': { 1529d05a87bSVladimir Sementsov-Ogievskiy 'driver': 'qcow2', 1539d05a87bSVladimir Sementsov-Ogievskiy 'file': { 1549d05a87bSVladimir Sementsov-Ogievskiy 'driver': 'file', 1559d05a87bSVladimir Sementsov-Ogievskiy 'filename': temp_img 1569d05a87bSVladimir Sementsov-Ogievskiy } 1579d05a87bSVladimir Sementsov-Ogievskiy } 1589d05a87bSVladimir Sementsov-Ogievskiy } 1599d05a87bSVladimir Sementsov-Ogievskiy }) 1609d05a87bSVladimir Sementsov-Ogievskiy 161b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', { 1629d05a87bSVladimir Sementsov-Ogievskiy 'node-name': 'access', 1639d05a87bSVladimir Sementsov-Ogievskiy 'driver': 'snapshot-access', 1649d05a87bSVladimir Sementsov-Ogievskiy 'file': 'cbw' 1659d05a87bSVladimir Sementsov-Ogievskiy }) 1669d05a87bSVladimir Sementsov-Ogievskiy 1679d05a87bSVladimir Sementsov-Ogievskiy result = self.vm.qmp('human-monitor-command', 1689d05a87bSVladimir Sementsov-Ogievskiy command_line='qemu-io cbw "write 0 512K"') 1699d05a87bSVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'return', '') 1709d05a87bSVladimir Sementsov-Ogievskiy 1719d05a87bSVladimir Sementsov-Ogievskiy # We need second write to trigger throttling 1729d05a87bSVladimir Sementsov-Ogievskiy result = self.vm.qmp('human-monitor-command', 1739d05a87bSVladimir Sementsov-Ogievskiy command_line='qemu-io cbw "write 512K 512K"') 1749d05a87bSVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'return', '') 1759d05a87bSVladimir Sementsov-Ogievskiy 1769d05a87bSVladimir Sementsov-Ogievskiy result = self.vm.qmp('human-monitor-command', 1779d05a87bSVladimir Sementsov-Ogievskiy command_line='qemu-io access "read 0 1M"') 1789d05a87bSVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'return', '') 1799d05a87bSVladimir Sementsov-Ogievskiy 1809d05a87bSVladimir Sementsov-Ogievskiy self.vm.shutdown() 1819d05a87bSVladimir Sementsov-Ogievskiy log = self.vm.get_log() 1829d05a87bSVladimir Sementsov-Ogievskiy log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) 1839d05a87bSVladimir Sementsov-Ogievskiy log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) 1849d05a87bSVladimir Sementsov-Ogievskiy log = iotests.filter_qemu_io(log) 1859d05a87bSVladimir Sementsov-Ogievskiy return log 1869d05a87bSVladimir Sementsov-Ogievskiy 1879d05a87bSVladimir Sementsov-Ogievskiy def test_timeout_break_guest(self): 1889d05a87bSVladimir Sementsov-Ogievskiy log = self.do_cbw_timeout('break-guest-write') 18905b47eecSVladimir Sementsov-Ogievskiy # macOS and FreeBSD tend to represent ETIMEDOUT as 19005b47eecSVladimir Sementsov-Ogievskiy # "Operation timed out", when Linux prefer 19105b47eecSVladimir Sementsov-Ogievskiy # "Connection timed out" 19205b47eecSVladimir Sementsov-Ogievskiy log = log.replace('Operation timed out', 19305b47eecSVladimir Sementsov-Ogievskiy 'Connection timed out') 1949d05a87bSVladimir Sementsov-Ogievskiy self.assertEqual(log, """\ 1959d05a87bSVladimir Sementsov-Ogievskiywrote 524288/524288 bytes at offset 0 1969d05a87bSVladimir Sementsov-Ogievskiy512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 1979d05a87bSVladimir Sementsov-Ogievskiywrite failed: Connection timed out 1989d05a87bSVladimir Sementsov-Ogievskiyread 1048576/1048576 bytes at offset 0 1999d05a87bSVladimir Sementsov-Ogievskiy1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2009d05a87bSVladimir Sementsov-Ogievskiy""") 2019d05a87bSVladimir Sementsov-Ogievskiy 2029d05a87bSVladimir Sementsov-Ogievskiy def test_timeout_break_snapshot(self): 2039d05a87bSVladimir Sementsov-Ogievskiy log = self.do_cbw_timeout('break-snapshot') 2049d05a87bSVladimir Sementsov-Ogievskiy self.assertEqual(log, """\ 2059d05a87bSVladimir Sementsov-Ogievskiywrote 524288/524288 bytes at offset 0 2069d05a87bSVladimir Sementsov-Ogievskiy512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2079d05a87bSVladimir Sementsov-Ogievskiywrote 524288/524288 bytes at offset 524288 2089d05a87bSVladimir Sementsov-Ogievskiy512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 2099d05a87bSVladimir Sementsov-Ogievskiyread failed: Permission denied 2109d05a87bSVladimir Sementsov-Ogievskiy""") 2119d05a87bSVladimir Sementsov-Ogievskiy 212dd3e97dfSVladimir Sementsov-Ogievskiy 213dd3e97dfSVladimir Sementsov-Ogievskiyif __name__ == '__main__': 214dd3e97dfSVladimir Sementsov-Ogievskiy iotests.main(supported_fmts=['qcow2'], 2159548cbefSVladimir Sementsov-Ogievskiy supported_protocols=['file'], 2169548cbefSVladimir Sementsov-Ogievskiy required_fmts=['copy-before-write']) 217