1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: rw auto 3747051cdSJeff Cody# 4747051cdSJeff Cody# Tests for image block commit. 5747051cdSJeff Cody# 6747051cdSJeff Cody# Copyright (C) 2012 IBM, Corp. 7747051cdSJeff Cody# Copyright (C) 2012 Red Hat, Inc. 8747051cdSJeff Cody# 9747051cdSJeff Cody# This program is free software; you can redistribute it and/or modify 10747051cdSJeff Cody# it under the terms of the GNU General Public License as published by 11747051cdSJeff Cody# the Free Software Foundation; either version 2 of the License, or 12747051cdSJeff Cody# (at your option) any later version. 13747051cdSJeff Cody# 14747051cdSJeff Cody# This program is distributed in the hope that it will be useful, 15747051cdSJeff Cody# but WITHOUT ANY WARRANTY; without even the implied warranty of 16747051cdSJeff Cody# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17747051cdSJeff Cody# GNU General Public License for more details. 18747051cdSJeff Cody# 19747051cdSJeff Cody# You should have received a copy of the GNU General Public License 20747051cdSJeff Cody# along with this program. If not, see <http://www.gnu.org/licenses/>. 21747051cdSJeff Cody# 22747051cdSJeff Cody# Test for live block commit 23747051cdSJeff Cody# Derived from Image Streaming Test 030 24747051cdSJeff Cody 25747051cdSJeff Codyimport time 26747051cdSJeff Codyimport os 27747051cdSJeff Codyimport iotests 28747051cdSJeff Codyfrom iotests import qemu_img, qemu_io 29747051cdSJeff Codyimport struct 306bf0d1f4SJeff Codyimport errno 31747051cdSJeff Cody 32747051cdSJeff Codybacking_img = os.path.join(iotests.test_dir, 'backing.img') 33747051cdSJeff Codymid_img = os.path.join(iotests.test_dir, 'mid.img') 34747051cdSJeff Codytest_img = os.path.join(iotests.test_dir, 'test.img') 35747051cdSJeff Cody 36747051cdSJeff Codyclass ImageCommitTestCase(iotests.QMPTestCase): 37747051cdSJeff Cody '''Abstract base class for image commit test cases''' 38747051cdSJeff Cody 397676e2c5SJeff Cody def wait_for_complete(self, need_ready=False): 404de43470SFam Zheng completed = False 418b9a30caSFam Zheng ready = False 424de43470SFam Zheng while not completed: 434de43470SFam Zheng for event in self.vm.get_qmp_events(wait=True): 444de43470SFam Zheng if event['event'] == 'BLOCK_JOB_COMPLETED': 45bcdce5a7SAlberto Garcia self.assert_qmp_absent(event, 'data/error') 464de43470SFam Zheng self.assert_qmp(event, 'data/type', 'commit') 474de43470SFam Zheng self.assert_qmp(event, 'data/device', 'drive0') 481d3ba15aSMax Reitz self.assert_qmp(event, 'data/offset', event['data']['len']) 498b9a30caSFam Zheng if need_ready: 508b9a30caSFam Zheng self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event") 514de43470SFam Zheng completed = True 524de43470SFam Zheng elif event['event'] == 'BLOCK_JOB_READY': 538b9a30caSFam Zheng ready = True 544de43470SFam Zheng self.assert_qmp(event, 'data/type', 'commit') 554de43470SFam Zheng self.assert_qmp(event, 'data/device', 'drive0') 564de43470SFam Zheng self.vm.qmp('block-job-complete', device='drive0') 574de43470SFam Zheng 58fb0a078fSFam Zheng self.assert_no_active_block_jobs() 594de43470SFam Zheng self.vm.shutdown() 604de43470SFam Zheng 61d57177a4SKevin Wolf def run_commit_test(self, top, base, need_ready=False, node_names=False): 627676e2c5SJeff Cody self.assert_no_active_block_jobs() 63d57177a4SKevin Wolf if node_names: 64d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node=top, base_node=base) 65d57177a4SKevin Wolf else: 667676e2c5SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) 677676e2c5SJeff Cody self.assert_qmp(result, 'return', {}) 687676e2c5SJeff Cody self.wait_for_complete(need_ready) 697676e2c5SJeff Cody 707676e2c5SJeff Cody def run_default_commit_test(self): 717676e2c5SJeff Cody self.assert_no_active_block_jobs() 727676e2c5SJeff Cody result = self.vm.qmp('block-commit', device='drive0') 737676e2c5SJeff Cody self.assert_qmp(result, 'return', {}) 747676e2c5SJeff Cody self.wait_for_complete() 757676e2c5SJeff Cody 76747051cdSJeff Codyclass TestSingleDrive(ImageCommitTestCase): 77c3971b88SKevin Wolf # Need some space after the copied data so that throttling is effective in 78c3971b88SKevin Wolf # tests that use it rather than just completing the job immediately 79c3971b88SKevin Wolf image_len = 2 * 1024 * 1024 80747051cdSJeff Cody test_len = 1 * 1024 * 256 81747051cdSJeff Cody 82747051cdSJeff Cody def setUp(self): 838b9a30caSFam Zheng iotests.create_image(backing_img, self.image_len) 84b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 85b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 86b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 87b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 88b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 89aaa0c0efSJohn Snow if self.image_len: 9090c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) 91aaa0c0efSJohn Snow qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', 92aaa0c0efSJohn Snow mid_img) 93d3c8c674SKevin Wolf self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none") 9422329f0dSLaurent Vivier self.vm.add_device('virtio-scsi') 95c3971b88SKevin Wolf self.vm.add_device("scsi-hd,id=scsi0,drive=drive0") 96747051cdSJeff Cody self.vm.launch() 97747051cdSJeff Cody 98747051cdSJeff Cody def tearDown(self): 99b9420e4fSJohn Snow self.vm.shutdown() 100747051cdSJeff Cody os.remove(test_img) 101747051cdSJeff Cody os.remove(mid_img) 102747051cdSJeff Cody os.remove(backing_img) 103747051cdSJeff Cody 104747051cdSJeff Cody def test_commit(self): 1054de43470SFam Zheng self.run_commit_test(mid_img, backing_img) 106aaa0c0efSJohn Snow if not self.image_len: 107aaa0c0efSJohn Snow return 108e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 109e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 110747051cdSJeff Cody 111d57177a4SKevin Wolf def test_commit_node(self): 112d57177a4SKevin Wolf self.run_commit_test("mid", "base", node_names=True) 113aaa0c0efSJohn Snow if not self.image_len: 114aaa0c0efSJohn Snow return 115e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 116e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 117d57177a4SKevin Wolf 1189442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 11986472071SMax Reitz def test_commit_with_filter_and_quit(self): 12086472071SMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 12186472071SMax Reitz self.assert_qmp(result, 'return', {}) 12286472071SMax Reitz 12386472071SMax Reitz # Add a filter outside of the backing chain 12486472071SMax Reitz result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 12586472071SMax Reitz self.assert_qmp(result, 'return', {}) 12686472071SMax Reitz 12786472071SMax Reitz result = self.vm.qmp('block-commit', device='drive0') 12886472071SMax Reitz self.assert_qmp(result, 'return', {}) 12986472071SMax Reitz 13086472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 13186472071SMax Reitz # block job and a bdrv_drain_all() 13286472071SMax Reitz result = self.vm.qmp('quit') 13386472071SMax Reitz self.assert_qmp(result, 'return', {}) 13486472071SMax Reitz 13586472071SMax Reitz # Same as above, but this time we add the filter after starting the job 1369442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 13786472071SMax Reitz def test_commit_plus_filter_and_quit(self): 13886472071SMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 13986472071SMax Reitz self.assert_qmp(result, 'return', {}) 14086472071SMax Reitz 14186472071SMax Reitz result = self.vm.qmp('block-commit', device='drive0') 14286472071SMax Reitz self.assert_qmp(result, 'return', {}) 14386472071SMax Reitz 14486472071SMax Reitz # Add a filter outside of the backing chain 14586472071SMax Reitz result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 14686472071SMax Reitz self.assert_qmp(result, 'return', {}) 14786472071SMax Reitz 14886472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 14986472071SMax Reitz # block job and a bdrv_drain_all() 15086472071SMax Reitz result = self.vm.qmp('quit') 15186472071SMax Reitz self.assert_qmp(result, 'return', {}) 15286472071SMax Reitz 153747051cdSJeff Cody def test_device_not_found(self): 154747051cdSJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) 155747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 156747051cdSJeff Cody 157747051cdSJeff Cody def test_top_same_base(self): 158fb0a078fSFam Zheng self.assert_no_active_block_jobs() 159747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img) 160747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 161b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % backing_img) 162747051cdSJeff Cody 163747051cdSJeff Cody def test_top_invalid(self): 164fb0a078fSFam Zheng self.assert_no_active_block_jobs() 165747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img) 166747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 167747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 168747051cdSJeff Cody 169747051cdSJeff Cody def test_base_invalid(self): 170fb0a078fSFam Zheng self.assert_no_active_block_jobs() 171747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile') 172747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 173b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find 'badfile' in the backing chain") 174747051cdSJeff Cody 175d57177a4SKevin Wolf def test_top_node_invalid(self): 176d57177a4SKevin Wolf self.assert_no_active_block_jobs() 177d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base') 178d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 179785ec4b1SConnor Kuehl self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'") 180d57177a4SKevin Wolf 181d57177a4SKevin Wolf def test_base_node_invalid(self): 182d57177a4SKevin Wolf self.assert_no_active_block_jobs() 183d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile') 184d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 185785ec4b1SConnor Kuehl self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'") 186d57177a4SKevin Wolf 187d57177a4SKevin Wolf def test_top_path_and_node(self): 188d57177a4SKevin Wolf self.assert_no_active_block_jobs() 189d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', top='%s' % mid_img) 190d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 191d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top-node' and 'top' are mutually exclusive") 192d57177a4SKevin Wolf 193d57177a4SKevin Wolf def test_base_path_and_node(self): 194d57177a4SKevin Wolf self.assert_no_active_block_jobs() 195d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', base='%s' % backing_img) 196d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 197d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'base-node' and 'base' are mutually exclusive") 198d57177a4SKevin Wolf 199747051cdSJeff Cody def test_top_is_active(self): 2008b9a30caSFam Zheng self.run_commit_test(test_img, backing_img, need_ready=True) 201aaa0c0efSJohn Snow if not self.image_len: 202aaa0c0efSJohn Snow return 203e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 204e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 205747051cdSJeff Cody 2067676e2c5SJeff Cody def test_top_is_default_active(self): 2077676e2c5SJeff Cody self.run_default_commit_test() 208aaa0c0efSJohn Snow if not self.image_len: 209aaa0c0efSJohn Snow return 210e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 211e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 2127676e2c5SJeff Cody 213747051cdSJeff Cody def test_top_and_base_reversed(self): 214fb0a078fSFam Zheng self.assert_no_active_block_jobs() 215747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img) 216747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 217b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % mid_img) 218747051cdSJeff Cody 219d57177a4SKevin Wolf def test_top_and_base_node_reversed(self): 220d57177a4SKevin Wolf self.assert_no_active_block_jobs() 221d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='base', base_node='top') 222d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 223d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top' is not in this backing file chain") 224d57177a4SKevin Wolf 225d57177a4SKevin Wolf def test_top_node_in_wrong_chain(self): 226d57177a4SKevin Wolf self.assert_no_active_block_jobs() 227d57177a4SKevin Wolf 228d57177a4SKevin Wolf result = self.vm.qmp('blockdev-add', driver='null-co', node_name='null') 229d57177a4SKevin Wolf self.assert_qmp(result, 'return', {}) 230d57177a4SKevin Wolf 231d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') 232d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 233d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'null' is not in this backing file chain") 234d57177a4SKevin Wolf 235c3971b88SKevin Wolf # When the job is running on a BB that is automatically deleted on hot 236c3971b88SKevin Wolf # unplug, the job is cancelled when the device disappears 237c3971b88SKevin Wolf def test_hot_unplug(self): 238c3971b88SKevin Wolf if self.image_len == 0: 239c3971b88SKevin Wolf return 240c3971b88SKevin Wolf 241c3971b88SKevin Wolf self.assert_no_active_block_jobs() 242c3971b88SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top=mid_img, 2439a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 244c3971b88SKevin Wolf self.assert_qmp(result, 'return', {}) 245c3971b88SKevin Wolf result = self.vm.qmp('device_del', id='scsi0') 246c3971b88SKevin Wolf self.assert_qmp(result, 'return', {}) 247c3971b88SKevin Wolf 248c3971b88SKevin Wolf cancelled = False 249c3971b88SKevin Wolf deleted = False 250c3971b88SKevin Wolf while not cancelled or not deleted: 251c3971b88SKevin Wolf for event in self.vm.get_qmp_events(wait=True): 252c3971b88SKevin Wolf if event['event'] == 'DEVICE_DELETED': 253c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'scsi0') 254c3971b88SKevin Wolf deleted = True 255c3971b88SKevin Wolf elif event['event'] == 'BLOCK_JOB_CANCELLED': 256c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'drive0') 257c3971b88SKevin Wolf cancelled = True 2581dac83f1SKevin Wolf elif event['event'] == 'JOB_STATUS_CHANGE': 2591dac83f1SKevin Wolf self.assert_qmp(event, 'data/id', 'drive0') 260c3971b88SKevin Wolf else: 261c3971b88SKevin Wolf self.fail("Unexpected event %s" % (event['event'])) 262c3971b88SKevin Wolf 263c3971b88SKevin Wolf self.assert_no_active_block_jobs() 264747051cdSJeff Cody 265d3c8c674SKevin Wolf # Tests that the insertion of the commit_top filter node doesn't make a 266d3c8c674SKevin Wolf # difference to query-blockstat 267d3c8c674SKevin Wolf def test_implicit_node(self): 268d3c8c674SKevin Wolf if self.image_len == 0: 269d3c8c674SKevin Wolf return 270d3c8c674SKevin Wolf 271d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 272d3c8c674SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top=mid_img, 2739a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 274d3c8c674SKevin Wolf self.assert_qmp(result, 'return', {}) 275d3c8c674SKevin Wolf 276d3c8c674SKevin Wolf result = self.vm.qmp('query-block') 277d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/file', test_img) 278d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) 279d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file', mid_img) 280d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 2) 281d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) 282d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', mid_img) 283d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/backing-image/filename', backing_img) 284d3c8c674SKevin Wolf 285d3c8c674SKevin Wolf result = self.vm.qmp('query-blockstats') 286d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/node-name', 'top') 287d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/node-name', 'mid') 288d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/backing/node-name', 'base') 289d3c8c674SKevin Wolf 290d3c8c674SKevin Wolf self.cancel_and_wait() 291d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 292d3c8c674SKevin Wolf 2936bf0d1f4SJeff Codyclass TestRelativePaths(ImageCommitTestCase): 2946bf0d1f4SJeff Cody image_len = 1 * 1024 * 1024 2956bf0d1f4SJeff Cody test_len = 1 * 1024 * 256 2966bf0d1f4SJeff Cody 2976bf0d1f4SJeff Cody dir1 = "dir1" 2986bf0d1f4SJeff Cody dir2 = "dir2/" 2996bf0d1f4SJeff Cody dir3 = "dir2/dir3/" 3006bf0d1f4SJeff Cody 3016bf0d1f4SJeff Cody test_img = os.path.join(iotests.test_dir, dir3, 'test.img') 3026bf0d1f4SJeff Cody mid_img = "../mid.img" 3036bf0d1f4SJeff Cody backing_img = "../dir1/backing.img" 3046bf0d1f4SJeff Cody 3056bf0d1f4SJeff Cody backing_img_abs = os.path.join(iotests.test_dir, dir1, 'backing.img') 3066bf0d1f4SJeff Cody mid_img_abs = os.path.join(iotests.test_dir, dir2, 'mid.img') 3076bf0d1f4SJeff Cody 3086bf0d1f4SJeff Cody def setUp(self): 3096bf0d1f4SJeff Cody try: 3106bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir1)) 3116bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir2)) 3126bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir3)) 3136bf0d1f4SJeff Cody except OSError as exception: 3146bf0d1f4SJeff Cody if exception.errno != errno.EEXIST: 3156bf0d1f4SJeff Cody raise 316915365a9SFam Zheng iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len) 317b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 318b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.backing_img_abs, 319b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 320b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 321b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.mid_img_abs, 322b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 323b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.backing_img, 324b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 325b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.mid_img, 326b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 32790c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs) 32890c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs) 3296bf0d1f4SJeff Cody self.vm = iotests.VM().add_drive(self.test_img) 3306bf0d1f4SJeff Cody self.vm.launch() 3316bf0d1f4SJeff Cody 3326bf0d1f4SJeff Cody def tearDown(self): 3336bf0d1f4SJeff Cody self.vm.shutdown() 3346bf0d1f4SJeff Cody os.remove(self.test_img) 3356bf0d1f4SJeff Cody os.remove(self.mid_img_abs) 3366bf0d1f4SJeff Cody os.remove(self.backing_img_abs) 3376bf0d1f4SJeff Cody try: 3386bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir1)) 3396bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir3)) 3406bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir2)) 3416bf0d1f4SJeff Cody except OSError as exception: 3426bf0d1f4SJeff Cody if exception.errno != errno.EEXIST and exception.errno != errno.ENOTEMPTY: 3436bf0d1f4SJeff Cody raise 3446bf0d1f4SJeff Cody 3456bf0d1f4SJeff Cody def test_commit(self): 3464de43470SFam Zheng self.run_commit_test(self.mid_img, self.backing_img) 347e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs) 348e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs) 3496bf0d1f4SJeff Cody 3506bf0d1f4SJeff Cody def test_device_not_found(self): 3516bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img) 3526bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 3536bf0d1f4SJeff Cody 3546bf0d1f4SJeff Cody def test_top_same_base(self): 355fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3566bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img) 3576bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 358b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % self.mid_img) 3596bf0d1f4SJeff Cody 3606bf0d1f4SJeff Cody def test_top_invalid(self): 361fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3626bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img) 3636bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3646bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 3656bf0d1f4SJeff Cody 3666bf0d1f4SJeff Cody def test_base_invalid(self): 367fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3686bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile') 3696bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 370b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find 'badfile' in the backing chain") 3716bf0d1f4SJeff Cody 3726bf0d1f4SJeff Cody def test_top_is_active(self): 3734de43470SFam Zheng self.run_commit_test(self.test_img, self.backing_img) 374e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs) 375e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs) 3766bf0d1f4SJeff Cody 3776bf0d1f4SJeff Cody def test_top_and_base_reversed(self): 378fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3796bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img) 3806bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 381b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % self.mid_img) 3826bf0d1f4SJeff Cody 383747051cdSJeff Cody 384747051cdSJeff Codyclass TestSetSpeed(ImageCommitTestCase): 385747051cdSJeff Cody image_len = 80 * 1024 * 1024 # MB 386747051cdSJeff Cody 387747051cdSJeff Cody def setUp(self): 388747051cdSJeff Cody qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 389b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 390b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 391b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 392b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 393b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 39490c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img) 39590c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) 39651c493c5SMax Reitz self.vm = iotests.VM().add_drive('blkdebug::' + test_img) 397747051cdSJeff Cody self.vm.launch() 398747051cdSJeff Cody 399747051cdSJeff Cody def tearDown(self): 400747051cdSJeff Cody self.vm.shutdown() 401747051cdSJeff Cody os.remove(test_img) 402747051cdSJeff Cody os.remove(mid_img) 403747051cdSJeff Cody os.remove(backing_img) 404747051cdSJeff Cody 405747051cdSJeff Cody def test_set_speed(self): 406fb0a078fSFam Zheng self.assert_no_active_block_jobs() 407747051cdSJeff Cody 408b59b3d57SFam Zheng self.vm.pause_drive('drive0') 409747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) 410747051cdSJeff Cody self.assert_qmp(result, 'return', {}) 411747051cdSJeff Cody 412747051cdSJeff Cody # Ensure the speed we set was accepted 413747051cdSJeff Cody result = self.vm.qmp('query-block-jobs') 414747051cdSJeff Cody self.assert_qmp(result, 'return[0]/device', 'drive0') 415747051cdSJeff Cody self.assert_qmp(result, 'return[0]/speed', 1024 * 1024) 416747051cdSJeff Cody 417b59b3d57SFam Zheng self.cancel_and_wait(resume=True) 418747051cdSJeff Cody 4198b9a30caSFam Zhengclass TestActiveZeroLengthImage(TestSingleDrive): 4208b9a30caSFam Zheng image_len = 0 421747051cdSJeff Cody 422bcdce5a7SAlberto Garciaclass TestReopenOverlay(ImageCommitTestCase): 423bcdce5a7SAlberto Garcia image_len = 1024 * 1024 424bcdce5a7SAlberto Garcia img0 = os.path.join(iotests.test_dir, '0.img') 425bcdce5a7SAlberto Garcia img1 = os.path.join(iotests.test_dir, '1.img') 426bcdce5a7SAlberto Garcia img2 = os.path.join(iotests.test_dir, '2.img') 427bcdce5a7SAlberto Garcia img3 = os.path.join(iotests.test_dir, '3.img') 428bcdce5a7SAlberto Garcia 429bcdce5a7SAlberto Garcia def setUp(self): 430bcdce5a7SAlberto Garcia iotests.create_image(self.img0, self.image_len) 431b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 432b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img0, '-F', 'raw', self.img1) 433b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 434b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img1, 435b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img2) 436b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 437b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img2, 438b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img3) 439bcdce5a7SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1) 440bcdce5a7SAlberto Garcia self.vm = iotests.VM().add_drive(self.img3) 441bcdce5a7SAlberto Garcia self.vm.launch() 442bcdce5a7SAlberto Garcia 443bcdce5a7SAlberto Garcia def tearDown(self): 444bcdce5a7SAlberto Garcia self.vm.shutdown() 445bcdce5a7SAlberto Garcia os.remove(self.img0) 446bcdce5a7SAlberto Garcia os.remove(self.img1) 447bcdce5a7SAlberto Garcia os.remove(self.img2) 448bcdce5a7SAlberto Garcia os.remove(self.img3) 449bcdce5a7SAlberto Garcia 450bcdce5a7SAlberto Garcia # This tests what happens when the overlay image of the 'top' node 451bcdce5a7SAlberto Garcia # needs to be reopened in read-write mode in order to update the 452bcdce5a7SAlberto Garcia # backing image string. 453bcdce5a7SAlberto Garcia def test_reopen_overlay(self): 454bcdce5a7SAlberto Garcia self.run_commit_test(self.img1, self.img0) 455bcdce5a7SAlberto Garcia 456d4398489SKevin Wolfclass TestErrorHandling(iotests.QMPTestCase): 457d4398489SKevin Wolf image_len = 2 * 1024 * 1024 458d4398489SKevin Wolf 459d4398489SKevin Wolf def setUp(self): 460d4398489SKevin Wolf iotests.create_image(backing_img, self.image_len) 461b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 462b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, 463b66ff2c2SEric Blake '-F', 'raw', mid_img) 464b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 465b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 466b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 467d4398489SKevin Wolf 468d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img) 469d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img) 470d4398489SKevin Wolf 471d4398489SKevin Wolf self.vm = iotests.VM() 472d4398489SKevin Wolf self.vm.launch() 473d4398489SKevin Wolf 474d4398489SKevin Wolf self.blkdebug_file = iotests.file_path("blkdebug.conf") 475d4398489SKevin Wolf 476d4398489SKevin Wolf def tearDown(self): 477d4398489SKevin Wolf self.vm.shutdown() 478d4398489SKevin Wolf os.remove(test_img) 479d4398489SKevin Wolf os.remove(mid_img) 480d4398489SKevin Wolf os.remove(backing_img) 481d4398489SKevin Wolf 482d4398489SKevin Wolf def blockdev_add(self, **kwargs): 483d4398489SKevin Wolf result = self.vm.qmp('blockdev-add', **kwargs) 484d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 485d4398489SKevin Wolf 486d4398489SKevin Wolf def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None): 487d4398489SKevin Wolf self.blockdev_add(node_name='base-file', driver='file', 488d4398489SKevin Wolf filename=backing_img) 489d4398489SKevin Wolf self.blockdev_add(node_name='mid-file', driver='file', 490d4398489SKevin Wolf filename=mid_img) 491d4398489SKevin Wolf self.blockdev_add(node_name='top-file', driver='file', 492d4398489SKevin Wolf filename=test_img) 493d4398489SKevin Wolf 494d4398489SKevin Wolf if base_debug: 495d4398489SKevin Wolf self.blockdev_add(node_name='base-dbg', driver='blkdebug', 496d4398489SKevin Wolf image='base-file', inject_error=base_debug) 497d4398489SKevin Wolf if mid_debug: 498d4398489SKevin Wolf self.blockdev_add(node_name='mid-dbg', driver='blkdebug', 499d4398489SKevin Wolf image='mid-file', inject_error=mid_debug) 500d4398489SKevin Wolf if top_debug: 501d4398489SKevin Wolf self.blockdev_add(node_name='top-dbg', driver='blkdebug', 502d4398489SKevin Wolf image='top-file', inject_error=top_debug) 503d4398489SKevin Wolf 504d4398489SKevin Wolf self.blockdev_add(node_name='base-fmt', driver='raw', 505d4398489SKevin Wolf file=('base-dbg' if base_debug else 'base-file')) 506d4398489SKevin Wolf self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt, 507d4398489SKevin Wolf file=('mid-dbg' if mid_debug else 'mid-file'), 508d4398489SKevin Wolf backing='base-fmt') 509d4398489SKevin Wolf self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt, 510d4398489SKevin Wolf file=('top-dbg' if top_debug else 'top-file'), 511d4398489SKevin Wolf backing='mid-fmt') 512d4398489SKevin Wolf 513d4398489SKevin Wolf def run_job(self, expected_events, error_pauses_job=False): 514d4398489SKevin Wolf match_device = {'data': {'device': 'job0'}} 515d4398489SKevin Wolf events = [ 516d4398489SKevin Wolf ('BLOCK_JOB_COMPLETED', match_device), 517d4398489SKevin Wolf ('BLOCK_JOB_CANCELLED', match_device), 518d4398489SKevin Wolf ('BLOCK_JOB_ERROR', match_device), 519d4398489SKevin Wolf ('BLOCK_JOB_READY', match_device), 520d4398489SKevin Wolf ] 521d4398489SKevin Wolf 522d4398489SKevin Wolf completed = False 523d4398489SKevin Wolf log = [] 524d4398489SKevin Wolf while not completed: 525d4398489SKevin Wolf ev = self.vm.events_wait(events, timeout=5.0) 526d4398489SKevin Wolf if ev['event'] == 'BLOCK_JOB_COMPLETED': 527d4398489SKevin Wolf completed = True 528d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_ERROR': 529d4398489SKevin Wolf if error_pauses_job: 530d4398489SKevin Wolf result = self.vm.qmp('block-job-resume', device='job0') 531d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 532d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_READY': 533d4398489SKevin Wolf result = self.vm.qmp('block-job-complete', device='job0') 534d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 535d4398489SKevin Wolf else: 536d4398489SKevin Wolf self.fail("Unexpected event: %s" % ev) 537d4398489SKevin Wolf log.append(iotests.filter_qmp_event(ev)) 538d4398489SKevin Wolf 539d4398489SKevin Wolf self.maxDiff = None 540d4398489SKevin Wolf self.assertEqual(expected_events, log) 541d4398489SKevin Wolf 542d4398489SKevin Wolf def event_error(self, op, action): 543d4398489SKevin Wolf return { 544d4398489SKevin Wolf 'event': 'BLOCK_JOB_ERROR', 545d4398489SKevin Wolf 'data': {'action': action, 'device': 'job0', 'operation': op}, 546d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'} 547d4398489SKevin Wolf } 548d4398489SKevin Wolf 549d4398489SKevin Wolf def event_ready(self): 550d4398489SKevin Wolf return { 551d4398489SKevin Wolf 'event': 'BLOCK_JOB_READY', 552d4398489SKevin Wolf 'data': {'device': 'job0', 553d4398489SKevin Wolf 'len': 524288, 554d4398489SKevin Wolf 'offset': 524288, 555d4398489SKevin Wolf 'speed': 0, 556d4398489SKevin Wolf 'type': 'commit'}, 557d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 558d4398489SKevin Wolf } 559d4398489SKevin Wolf 560d4398489SKevin Wolf def event_completed(self, errmsg=None, active=True): 561d4398489SKevin Wolf max_len = 524288 if active else self.image_len 562d4398489SKevin Wolf data = { 563d4398489SKevin Wolf 'device': 'job0', 564d4398489SKevin Wolf 'len': max_len, 565d4398489SKevin Wolf 'offset': 0 if errmsg else max_len, 566d4398489SKevin Wolf 'speed': 0, 567d4398489SKevin Wolf 'type': 'commit' 568d4398489SKevin Wolf } 569d4398489SKevin Wolf if errmsg: 570d4398489SKevin Wolf data['error'] = errmsg 571d4398489SKevin Wolf 572d4398489SKevin Wolf return { 573d4398489SKevin Wolf 'event': 'BLOCK_JOB_COMPLETED', 574d4398489SKevin Wolf 'data': data, 575d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 576d4398489SKevin Wolf } 577d4398489SKevin Wolf 578d4398489SKevin Wolf def blkdebug_event(self, event, is_raw=False): 579d4398489SKevin Wolf if event: 580d4398489SKevin Wolf return [{ 581d4398489SKevin Wolf 'event': event, 582d4398489SKevin Wolf 'sector': 512 if is_raw else 1024, 583d4398489SKevin Wolf 'once': True, 584d4398489SKevin Wolf }] 585d4398489SKevin Wolf return None 586d4398489SKevin Wolf 587d4398489SKevin Wolf def prepare_and_start_job(self, on_error, active=True, 588d4398489SKevin Wolf top_event=None, mid_event=None, base_event=None): 589d4398489SKevin Wolf 590d4398489SKevin Wolf top_debug = self.blkdebug_event(top_event) 591d4398489SKevin Wolf mid_debug = self.blkdebug_event(mid_event) 592d4398489SKevin Wolf base_debug = self.blkdebug_event(base_event, True) 593d4398489SKevin Wolf 594d4398489SKevin Wolf self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug, 595d4398489SKevin Wolf base_debug=base_debug) 596d4398489SKevin Wolf 597d4398489SKevin Wolf result = self.vm.qmp('block-commit', job_id='job0', device='top-fmt', 598d4398489SKevin Wolf top_node='top-fmt' if active else 'mid-fmt', 599d4398489SKevin Wolf base_node='mid-fmt' if active else 'base-fmt', 600d4398489SKevin Wolf on_error=on_error) 601d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 602d4398489SKevin Wolf 603d4398489SKevin Wolf def testActiveReadErrorReport(self): 604d4398489SKevin Wolf self.prepare_and_start_job('report', top_event='read_aio') 605d4398489SKevin Wolf self.run_job([ 606d4398489SKevin Wolf self.event_error('read', 'report'), 607d4398489SKevin Wolf self.event_completed('Input/output error') 608d4398489SKevin Wolf ]) 609d4398489SKevin Wolf 610d4398489SKevin Wolf self.vm.shutdown() 611d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 612d4398489SKevin Wolf 'target image matches source after error') 613d4398489SKevin Wolf 614d4398489SKevin Wolf def testActiveReadErrorStop(self): 615d4398489SKevin Wolf self.prepare_and_start_job('stop', top_event='read_aio') 616d4398489SKevin Wolf self.run_job([ 617d4398489SKevin Wolf self.event_error('read', 'stop'), 618d4398489SKevin Wolf self.event_ready(), 619d4398489SKevin Wolf self.event_completed() 620d4398489SKevin Wolf ], error_pauses_job=True) 621d4398489SKevin Wolf 622d4398489SKevin Wolf self.vm.shutdown() 623d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 624d4398489SKevin Wolf 'target image does not match source after commit') 625d4398489SKevin Wolf 626d4398489SKevin Wolf def testActiveReadErrorIgnore(self): 627d4398489SKevin Wolf self.prepare_and_start_job('ignore', top_event='read_aio') 628d4398489SKevin Wolf self.run_job([ 629d4398489SKevin Wolf self.event_error('read', 'ignore'), 630d4398489SKevin Wolf self.event_ready(), 631d4398489SKevin Wolf self.event_completed() 632d4398489SKevin Wolf ]) 633d4398489SKevin Wolf 634d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 635d4398489SKevin Wolf self.vm.shutdown() 636d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 637d4398489SKevin Wolf 'target image does not match source after commit') 638d4398489SKevin Wolf 639d4398489SKevin Wolf def testActiveWriteErrorReport(self): 640d4398489SKevin Wolf self.prepare_and_start_job('report', mid_event='write_aio') 641d4398489SKevin Wolf self.run_job([ 642d4398489SKevin Wolf self.event_error('write', 'report'), 643d4398489SKevin Wolf self.event_completed('Input/output error') 644d4398489SKevin Wolf ]) 645d4398489SKevin Wolf 646d4398489SKevin Wolf self.vm.shutdown() 647d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 648d4398489SKevin Wolf 'target image matches source after error') 649d4398489SKevin Wolf 650d4398489SKevin Wolf def testActiveWriteErrorStop(self): 651d4398489SKevin Wolf self.prepare_and_start_job('stop', mid_event='write_aio') 652d4398489SKevin Wolf self.run_job([ 653d4398489SKevin Wolf self.event_error('write', 'stop'), 654d4398489SKevin Wolf self.event_ready(), 655d4398489SKevin Wolf self.event_completed() 656d4398489SKevin Wolf ], error_pauses_job=True) 657d4398489SKevin Wolf 658d4398489SKevin Wolf self.vm.shutdown() 659d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 660d4398489SKevin Wolf 'target image does not match source after commit') 661d4398489SKevin Wolf 662d4398489SKevin Wolf def testActiveWriteErrorIgnore(self): 663d4398489SKevin Wolf self.prepare_and_start_job('ignore', mid_event='write_aio') 664d4398489SKevin Wolf self.run_job([ 665d4398489SKevin Wolf self.event_error('write', 'ignore'), 666d4398489SKevin Wolf self.event_ready(), 667d4398489SKevin Wolf self.event_completed() 668d4398489SKevin Wolf ]) 669d4398489SKevin Wolf 670d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 671d4398489SKevin Wolf self.vm.shutdown() 672d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 673d4398489SKevin Wolf 'target image does not match source after commit') 674d4398489SKevin Wolf 675d4398489SKevin Wolf def testIntermediateReadErrorReport(self): 676d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, mid_event='read_aio') 677d4398489SKevin Wolf self.run_job([ 678d4398489SKevin Wolf self.event_error('read', 'report'), 679d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 680d4398489SKevin Wolf ]) 681d4398489SKevin Wolf 682d4398489SKevin Wolf self.vm.shutdown() 683d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 684d4398489SKevin Wolf 'target image matches source after error') 685d4398489SKevin Wolf 686d4398489SKevin Wolf def testIntermediateReadErrorStop(self): 687d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, mid_event='read_aio') 688d4398489SKevin Wolf self.run_job([ 689d4398489SKevin Wolf self.event_error('read', 'stop'), 690d4398489SKevin Wolf self.event_completed(active=False) 691d4398489SKevin Wolf ], error_pauses_job=True) 692d4398489SKevin Wolf 693d4398489SKevin Wolf self.vm.shutdown() 694d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 695d4398489SKevin Wolf 'target image does not match source after commit') 696d4398489SKevin Wolf 697d4398489SKevin Wolf def testIntermediateReadErrorIgnore(self): 698d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, mid_event='read_aio') 699d4398489SKevin Wolf self.run_job([ 700d4398489SKevin Wolf self.event_error('read', 'ignore'), 701d4398489SKevin Wolf self.event_completed(active=False) 702d4398489SKevin Wolf ]) 703d4398489SKevin Wolf 704d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 705d4398489SKevin Wolf self.vm.shutdown() 706d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 707d4398489SKevin Wolf 'target image does not match source after commit') 708d4398489SKevin Wolf 709d4398489SKevin Wolf def testIntermediateWriteErrorReport(self): 710d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, base_event='write_aio') 711d4398489SKevin Wolf self.run_job([ 712d4398489SKevin Wolf self.event_error('write', 'report'), 713d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 714d4398489SKevin Wolf ]) 715d4398489SKevin Wolf 716d4398489SKevin Wolf self.vm.shutdown() 717d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 718d4398489SKevin Wolf 'target image matches source after error') 719d4398489SKevin Wolf 720d4398489SKevin Wolf def testIntermediateWriteErrorStop(self): 721d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, base_event='write_aio') 722d4398489SKevin Wolf self.run_job([ 723d4398489SKevin Wolf self.event_error('write', 'stop'), 724d4398489SKevin Wolf self.event_completed(active=False) 725d4398489SKevin Wolf ], error_pauses_job=True) 726d4398489SKevin Wolf 727d4398489SKevin Wolf self.vm.shutdown() 728d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 729d4398489SKevin Wolf 'target image does not match source after commit') 730d4398489SKevin Wolf 731d4398489SKevin Wolf def testIntermediateWriteErrorIgnore(self): 732d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, base_event='write_aio') 733d4398489SKevin Wolf self.run_job([ 734d4398489SKevin Wolf self.event_error('write', 'ignore'), 735d4398489SKevin Wolf self.event_completed(active=False) 736d4398489SKevin Wolf ]) 737d4398489SKevin Wolf 738d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 739d4398489SKevin Wolf self.vm.shutdown() 740d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 741d4398489SKevin Wolf 'target image does not match source after commit') 742d4398489SKevin Wolf 743ffbba7fdSMax Reitzclass TestCommitWithFilters(iotests.QMPTestCase): 744ffbba7fdSMax Reitz img0 = os.path.join(iotests.test_dir, '0.img') 745ffbba7fdSMax Reitz img1 = os.path.join(iotests.test_dir, '1.img') 746ffbba7fdSMax Reitz img2 = os.path.join(iotests.test_dir, '2.img') 747ffbba7fdSMax Reitz img3 = os.path.join(iotests.test_dir, '3.img') 748ffbba7fdSMax Reitz 749ffbba7fdSMax Reitz def do_test_io(self, read_or_write): 750ffbba7fdSMax Reitz for index, pattern_file in enumerate(self.pattern_files): 751e9039c04SJohn Snow qemu_io('-f', iotests.imgfmt, 752ffbba7fdSMax Reitz '-c', 753ffbba7fdSMax Reitz f'{read_or_write} -P {index + 1} {index}M 1M', 754ffbba7fdSMax Reitz pattern_file) 755ffbba7fdSMax Reitz 756024354eaSThomas Huth @iotests.skip_if_unsupported(['throttle']) 757ffbba7fdSMax Reitz def setUp(self): 758ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M') 759ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M') 760ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img2, '64M') 761ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img3, '64M') 762ffbba7fdSMax Reitz 763ffbba7fdSMax Reitz # Distributions of the patterns in the files; this is checked 764ffbba7fdSMax Reitz # by tearDown() and should be changed by the test cases as is 765ffbba7fdSMax Reitz # necessary 766ffbba7fdSMax Reitz self.pattern_files = [self.img0, self.img1, self.img2, self.img3] 767ffbba7fdSMax Reitz 768ffbba7fdSMax Reitz self.do_test_io('write') 769ffbba7fdSMax Reitz 770ffbba7fdSMax Reitz self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi') 771ffbba7fdSMax Reitz self.vm.launch() 772ffbba7fdSMax Reitz 773ffbba7fdSMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 774ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 775ffbba7fdSMax Reitz 776ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-add', **{ 777ffbba7fdSMax Reitz 'node-name': 'top-filter', 778ffbba7fdSMax Reitz 'driver': 'throttle', 779ffbba7fdSMax Reitz 'throttle-group': 'tg', 780ffbba7fdSMax Reitz 'file': { 781ffbba7fdSMax Reitz 'node-name': 'cow-3', 782ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 783ffbba7fdSMax Reitz 'file': { 784ffbba7fdSMax Reitz 'driver': 'file', 785ffbba7fdSMax Reitz 'filename': self.img3 786ffbba7fdSMax Reitz }, 787ffbba7fdSMax Reitz 'backing': { 788ffbba7fdSMax Reitz 'node-name': 'cow-2', 789ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 790ffbba7fdSMax Reitz 'file': { 791ffbba7fdSMax Reitz 'driver': 'file', 792ffbba7fdSMax Reitz 'filename': self.img2 793ffbba7fdSMax Reitz }, 794ffbba7fdSMax Reitz 'backing': { 795ffbba7fdSMax Reitz 'node-name': 'cow-1', 796ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 797ffbba7fdSMax Reitz 'file': { 798ffbba7fdSMax Reitz 'driver': 'file', 799ffbba7fdSMax Reitz 'filename': self.img1 800ffbba7fdSMax Reitz }, 801ffbba7fdSMax Reitz 'backing': { 802ffbba7fdSMax Reitz 'node-name': 'bottom-filter', 803ffbba7fdSMax Reitz 'driver': 'throttle', 804ffbba7fdSMax Reitz 'throttle-group': 'tg', 805ffbba7fdSMax Reitz 'file': { 806ffbba7fdSMax Reitz 'node-name': 'cow-0', 807ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 808ffbba7fdSMax Reitz 'file': { 809ffbba7fdSMax Reitz 'driver': 'file', 810ffbba7fdSMax Reitz 'filename': self.img0 811ffbba7fdSMax Reitz } 812ffbba7fdSMax Reitz } 813ffbba7fdSMax Reitz } 814ffbba7fdSMax Reitz } 815ffbba7fdSMax Reitz } 816ffbba7fdSMax Reitz } 817ffbba7fdSMax Reitz }) 818ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 819ffbba7fdSMax Reitz 820ffbba7fdSMax Reitz def tearDown(self): 821ffbba7fdSMax Reitz self.vm.shutdown() 822ffbba7fdSMax Reitz self.do_test_io('read') 823ffbba7fdSMax Reitz 824ffbba7fdSMax Reitz os.remove(self.img3) 825ffbba7fdSMax Reitz os.remove(self.img2) 826ffbba7fdSMax Reitz os.remove(self.img1) 827ffbba7fdSMax Reitz os.remove(self.img0) 828ffbba7fdSMax Reitz 829ffbba7fdSMax Reitz # Filters make for funny filenames, so we cannot just use 830ffbba7fdSMax Reitz # self.imgX to get them 831ffbba7fdSMax Reitz def get_filename(self, node): 832ffbba7fdSMax Reitz return self.vm.node_info(node)['image']['filename'] 833ffbba7fdSMax Reitz 834ffbba7fdSMax Reitz def test_filterless_commit(self): 835ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 836ffbba7fdSMax Reitz job_id='commit', 837ffbba7fdSMax Reitz device='top-filter', 838ffbba7fdSMax Reitz top_node='cow-2', 839*48976291SJohn Snow base_node='cow-1', 840*48976291SJohn Snow backing_file=self.img1) 841ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 842ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 843ffbba7fdSMax Reitz 844ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-3')) 845ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-2')) 846ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-1')) 847ffbba7fdSMax Reitz 848ffbba7fdSMax Reitz # 2 has been comitted into 1 849ffbba7fdSMax Reitz self.pattern_files[2] = self.img1 850ffbba7fdSMax Reitz 851ffbba7fdSMax Reitz def test_commit_through_filter(self): 852ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 853ffbba7fdSMax Reitz job_id='commit', 854ffbba7fdSMax Reitz device='top-filter', 855ffbba7fdSMax Reitz top_node='cow-1', 856*48976291SJohn Snow base_node='cow-0', 857*48976291SJohn Snow backing_file=self.img0) 858ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 859ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 860ffbba7fdSMax Reitz 861ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 862ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-1')) 863ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('bottom-filter')) 864ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-0')) 865ffbba7fdSMax Reitz 866ffbba7fdSMax Reitz # 1 has been comitted into 0 867ffbba7fdSMax Reitz self.pattern_files[1] = self.img0 868ffbba7fdSMax Reitz 869ffbba7fdSMax Reitz def test_filtered_active_commit_with_filter(self): 870ffbba7fdSMax Reitz # Add a device, so the commit job finds a parent it can change 871ffbba7fdSMax Reitz # to point to the base node (so we can test that top-filter is 872ffbba7fdSMax Reitz # dropped from the graph) 873ffbba7fdSMax Reitz result = self.vm.qmp('device_add', id='drv0', driver='scsi-hd', 874ffbba7fdSMax Reitz bus='vio-scsi.0', drive='top-filter') 875ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 876ffbba7fdSMax Reitz 877ffbba7fdSMax Reitz # Try to release our reference to top-filter; that should not 878ffbba7fdSMax Reitz # work because drv0 uses it 879ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 880ffbba7fdSMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 881ffbba7fdSMax Reitz self.assert_qmp(result, 'error/desc', 'Node top-filter is in use') 882ffbba7fdSMax Reitz 883ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 884ffbba7fdSMax Reitz job_id='commit', 885ffbba7fdSMax Reitz device='top-filter', 886ffbba7fdSMax Reitz base_node='cow-2') 887ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 888ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 889ffbba7fdSMax Reitz 890ffbba7fdSMax Reitz # Try to release our reference to top-filter again 891ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 892ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 893ffbba7fdSMax Reitz 894ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('top-filter')) 895ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 896ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 897ffbba7fdSMax Reitz 898ffbba7fdSMax Reitz # Check that drv0 is now connected to cow-2 899ffbba7fdSMax Reitz blockdevs = self.vm.qmp('query-block')['return'] 900ffbba7fdSMax Reitz drv0 = next(dev for dev in blockdevs if dev['qdev'] == 'drv0') 901ffbba7fdSMax Reitz self.assertEqual(drv0['inserted']['node-name'], 'cow-2') 902ffbba7fdSMax Reitz 903ffbba7fdSMax Reitz # 3 has been comitted into 2 904ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 905ffbba7fdSMax Reitz 906ffbba7fdSMax Reitz def test_filtered_active_commit_without_filter(self): 907ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 908ffbba7fdSMax Reitz job_id='commit', 909ffbba7fdSMax Reitz device='top-filter', 910ffbba7fdSMax Reitz top_node='cow-3', 911ffbba7fdSMax Reitz base_node='cow-2') 912ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 913ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 914ffbba7fdSMax Reitz 915ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('top-filter')) 916ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 917ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 918ffbba7fdSMax Reitz 919ffbba7fdSMax Reitz # 3 has been comitted into 2 920ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 921ffbba7fdSMax Reitz 922374eedd1SMax Reitzclass TestCommitWithOverriddenBacking(iotests.QMPTestCase): 923374eedd1SMax Reitz img_base_a = os.path.join(iotests.test_dir, 'base_a.img') 924374eedd1SMax Reitz img_base_b = os.path.join(iotests.test_dir, 'base_b.img') 925374eedd1SMax Reitz img_top = os.path.join(iotests.test_dir, 'top.img') 926374eedd1SMax Reitz 927374eedd1SMax Reitz def setUp(self): 928374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_a, '1M') 929374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_b, '1M') 930497a30dbSEric Blake qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, 931497a30dbSEric Blake '-F', iotests.imgfmt, self.img_top) 932374eedd1SMax Reitz 933374eedd1SMax Reitz self.vm = iotests.VM() 934374eedd1SMax Reitz self.vm.launch() 935374eedd1SMax Reitz 936374eedd1SMax Reitz # Use base_b instead of base_a as the backing of top 937374eedd1SMax Reitz result = self.vm.qmp('blockdev-add', **{ 938374eedd1SMax Reitz 'node-name': 'top', 939374eedd1SMax Reitz 'driver': iotests.imgfmt, 940374eedd1SMax Reitz 'file': { 941374eedd1SMax Reitz 'driver': 'file', 942374eedd1SMax Reitz 'filename': self.img_top 943374eedd1SMax Reitz }, 944374eedd1SMax Reitz 'backing': { 945374eedd1SMax Reitz 'node-name': 'base', 946374eedd1SMax Reitz 'driver': iotests.imgfmt, 947374eedd1SMax Reitz 'file': { 948374eedd1SMax Reitz 'driver': 'file', 949374eedd1SMax Reitz 'filename': self.img_base_b 950374eedd1SMax Reitz } 951374eedd1SMax Reitz } 952374eedd1SMax Reitz }) 953374eedd1SMax Reitz self.assert_qmp(result, 'return', {}) 954374eedd1SMax Reitz 955374eedd1SMax Reitz def tearDown(self): 956374eedd1SMax Reitz self.vm.shutdown() 957374eedd1SMax Reitz os.remove(self.img_top) 958374eedd1SMax Reitz os.remove(self.img_base_a) 959374eedd1SMax Reitz os.remove(self.img_base_b) 960374eedd1SMax Reitz 961374eedd1SMax Reitz def test_commit_to_a(self): 962374eedd1SMax Reitz # Try committing to base_a (which should fail, as top's 963374eedd1SMax Reitz # backing image is base_b instead) 964374eedd1SMax Reitz result = self.vm.qmp('block-commit', 965374eedd1SMax Reitz job_id='commit', 966374eedd1SMax Reitz device='top', 967374eedd1SMax Reitz base=self.img_base_a) 968374eedd1SMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 969374eedd1SMax Reitz 970374eedd1SMax Reitz def test_commit_to_b(self): 971374eedd1SMax Reitz # Try committing to base_b (which should work, since that is 972374eedd1SMax Reitz # actually top's backing image) 973374eedd1SMax Reitz result = self.vm.qmp('block-commit', 974374eedd1SMax Reitz job_id='commit', 975374eedd1SMax Reitz device='top', 976374eedd1SMax Reitz base=self.img_base_b) 977374eedd1SMax Reitz self.assert_qmp(result, 'return', {}) 978374eedd1SMax Reitz 979374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_READY') 980374eedd1SMax Reitz self.vm.qmp('block-job-complete', device='commit') 981374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_COMPLETED') 982374eedd1SMax Reitz 983747051cdSJeff Codyif __name__ == '__main__': 984103cbc77SMax Reitz iotests.main(supported_fmts=['qcow2', 'qed'], 985103cbc77SMax Reitz supported_protocols=['file']) 986