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) 8990c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) 9090c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) 91d3c8c674SKevin Wolf self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none") 9222329f0dSLaurent Vivier self.vm.add_device('virtio-scsi') 93c3971b88SKevin Wolf self.vm.add_device("scsi-hd,id=scsi0,drive=drive0") 94747051cdSJeff Cody self.vm.launch() 95747051cdSJeff Cody 96747051cdSJeff Cody def tearDown(self): 97*b9420e4fSJohn Snow self.vm.shutdown() 98747051cdSJeff Cody os.remove(test_img) 99747051cdSJeff Cody os.remove(mid_img) 100747051cdSJeff Cody os.remove(backing_img) 101747051cdSJeff Cody 102747051cdSJeff Cody def test_commit(self): 1034de43470SFam Zheng self.run_commit_test(mid_img, backing_img) 10490c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 10590c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 106747051cdSJeff Cody 107d57177a4SKevin Wolf def test_commit_node(self): 108d57177a4SKevin Wolf self.run_commit_test("mid", "base", node_names=True) 109d57177a4SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 110d57177a4SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 111d57177a4SKevin Wolf 1129442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 11386472071SMax Reitz def test_commit_with_filter_and_quit(self): 11486472071SMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 11586472071SMax Reitz self.assert_qmp(result, 'return', {}) 11686472071SMax Reitz 11786472071SMax Reitz # Add a filter outside of the backing chain 11886472071SMax Reitz result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 11986472071SMax Reitz self.assert_qmp(result, 'return', {}) 12086472071SMax Reitz 12186472071SMax Reitz result = self.vm.qmp('block-commit', device='drive0') 12286472071SMax Reitz self.assert_qmp(result, 'return', {}) 12386472071SMax Reitz 12486472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 12586472071SMax Reitz # block job and a bdrv_drain_all() 12686472071SMax Reitz result = self.vm.qmp('quit') 12786472071SMax Reitz self.assert_qmp(result, 'return', {}) 12886472071SMax Reitz 12986472071SMax Reitz # Same as above, but this time we add the filter after starting the job 1309442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 13186472071SMax Reitz def test_commit_plus_filter_and_quit(self): 13286472071SMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 13386472071SMax Reitz self.assert_qmp(result, 'return', {}) 13486472071SMax Reitz 13586472071SMax Reitz result = self.vm.qmp('block-commit', device='drive0') 13686472071SMax Reitz self.assert_qmp(result, 'return', {}) 13786472071SMax Reitz 13886472071SMax Reitz # Add a filter outside of the backing chain 13986472071SMax Reitz result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 14086472071SMax Reitz self.assert_qmp(result, 'return', {}) 14186472071SMax Reitz 14286472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 14386472071SMax Reitz # block job and a bdrv_drain_all() 14486472071SMax Reitz result = self.vm.qmp('quit') 14586472071SMax Reitz self.assert_qmp(result, 'return', {}) 14686472071SMax Reitz 147747051cdSJeff Cody def test_device_not_found(self): 148747051cdSJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) 149747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 150747051cdSJeff Cody 151747051cdSJeff Cody def test_top_same_base(self): 152fb0a078fSFam Zheng self.assert_no_active_block_jobs() 153747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img) 154747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 155b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % backing_img) 156747051cdSJeff Cody 157747051cdSJeff Cody def test_top_invalid(self): 158fb0a078fSFam Zheng self.assert_no_active_block_jobs() 159747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img) 160747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 161747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 162747051cdSJeff Cody 163747051cdSJeff Cody def test_base_invalid(self): 164fb0a078fSFam Zheng self.assert_no_active_block_jobs() 165747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile') 166747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 167b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find 'badfile' in the backing chain") 168747051cdSJeff Cody 169d57177a4SKevin Wolf def test_top_node_invalid(self): 170d57177a4SKevin Wolf self.assert_no_active_block_jobs() 171d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base') 172d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 173785ec4b1SConnor Kuehl self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'") 174d57177a4SKevin Wolf 175d57177a4SKevin Wolf def test_base_node_invalid(self): 176d57177a4SKevin Wolf self.assert_no_active_block_jobs() 177d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile') 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_top_path_and_node(self): 182d57177a4SKevin Wolf self.assert_no_active_block_jobs() 183d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', top='%s' % mid_img) 184d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 185d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top-node' and 'top' are mutually exclusive") 186d57177a4SKevin Wolf 187d57177a4SKevin Wolf def test_base_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', base='%s' % backing_img) 190d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 191d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'base-node' and 'base' are mutually exclusive") 192d57177a4SKevin Wolf 193747051cdSJeff Cody def test_top_is_active(self): 1948b9a30caSFam Zheng self.run_commit_test(test_img, backing_img, need_ready=True) 19590c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 19690c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 197747051cdSJeff Cody 1987676e2c5SJeff Cody def test_top_is_default_active(self): 1997676e2c5SJeff Cody self.run_default_commit_test() 20090c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 20190c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 2027676e2c5SJeff Cody 203747051cdSJeff Cody def test_top_and_base_reversed(self): 204fb0a078fSFam Zheng self.assert_no_active_block_jobs() 205747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img) 206747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 207b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % mid_img) 208747051cdSJeff Cody 209d57177a4SKevin Wolf def test_top_and_base_node_reversed(self): 210d57177a4SKevin Wolf self.assert_no_active_block_jobs() 211d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='base', base_node='top') 212d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 213d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top' is not in this backing file chain") 214d57177a4SKevin Wolf 215d57177a4SKevin Wolf def test_top_node_in_wrong_chain(self): 216d57177a4SKevin Wolf self.assert_no_active_block_jobs() 217d57177a4SKevin Wolf 218d57177a4SKevin Wolf result = self.vm.qmp('blockdev-add', driver='null-co', node_name='null') 219d57177a4SKevin Wolf self.assert_qmp(result, 'return', {}) 220d57177a4SKevin Wolf 221d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') 222d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 223d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'null' is not in this backing file chain") 224d57177a4SKevin Wolf 225c3971b88SKevin Wolf # When the job is running on a BB that is automatically deleted on hot 226c3971b88SKevin Wolf # unplug, the job is cancelled when the device disappears 227c3971b88SKevin Wolf def test_hot_unplug(self): 228c3971b88SKevin Wolf if self.image_len == 0: 229c3971b88SKevin Wolf return 230c3971b88SKevin Wolf 231c3971b88SKevin Wolf self.assert_no_active_block_jobs() 232c3971b88SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top=mid_img, 2339a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 234c3971b88SKevin Wolf self.assert_qmp(result, 'return', {}) 235c3971b88SKevin Wolf result = self.vm.qmp('device_del', id='scsi0') 236c3971b88SKevin Wolf self.assert_qmp(result, 'return', {}) 237c3971b88SKevin Wolf 238c3971b88SKevin Wolf cancelled = False 239c3971b88SKevin Wolf deleted = False 240c3971b88SKevin Wolf while not cancelled or not deleted: 241c3971b88SKevin Wolf for event in self.vm.get_qmp_events(wait=True): 242c3971b88SKevin Wolf if event['event'] == 'DEVICE_DELETED': 243c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'scsi0') 244c3971b88SKevin Wolf deleted = True 245c3971b88SKevin Wolf elif event['event'] == 'BLOCK_JOB_CANCELLED': 246c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'drive0') 247c3971b88SKevin Wolf cancelled = True 2481dac83f1SKevin Wolf elif event['event'] == 'JOB_STATUS_CHANGE': 2491dac83f1SKevin Wolf self.assert_qmp(event, 'data/id', 'drive0') 250c3971b88SKevin Wolf else: 251c3971b88SKevin Wolf self.fail("Unexpected event %s" % (event['event'])) 252c3971b88SKevin Wolf 253c3971b88SKevin Wolf self.assert_no_active_block_jobs() 254747051cdSJeff Cody 255d3c8c674SKevin Wolf # Tests that the insertion of the commit_top filter node doesn't make a 256d3c8c674SKevin Wolf # difference to query-blockstat 257d3c8c674SKevin Wolf def test_implicit_node(self): 258d3c8c674SKevin Wolf if self.image_len == 0: 259d3c8c674SKevin Wolf return 260d3c8c674SKevin Wolf 261d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 262d3c8c674SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top=mid_img, 2639a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 264d3c8c674SKevin Wolf self.assert_qmp(result, 'return', {}) 265d3c8c674SKevin Wolf 266d3c8c674SKevin Wolf result = self.vm.qmp('query-block') 267d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/file', test_img) 268d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) 269d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file', mid_img) 270d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 2) 271d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) 272d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', mid_img) 273d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/backing-image/filename', backing_img) 274d3c8c674SKevin Wolf 275d3c8c674SKevin Wolf result = self.vm.qmp('query-blockstats') 276d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/node-name', 'top') 277d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/node-name', 'mid') 278d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/backing/node-name', 'base') 279d3c8c674SKevin Wolf 280d3c8c674SKevin Wolf self.cancel_and_wait() 281d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 282d3c8c674SKevin Wolf 2836bf0d1f4SJeff Codyclass TestRelativePaths(ImageCommitTestCase): 2846bf0d1f4SJeff Cody image_len = 1 * 1024 * 1024 2856bf0d1f4SJeff Cody test_len = 1 * 1024 * 256 2866bf0d1f4SJeff Cody 2876bf0d1f4SJeff Cody dir1 = "dir1" 2886bf0d1f4SJeff Cody dir2 = "dir2/" 2896bf0d1f4SJeff Cody dir3 = "dir2/dir3/" 2906bf0d1f4SJeff Cody 2916bf0d1f4SJeff Cody test_img = os.path.join(iotests.test_dir, dir3, 'test.img') 2926bf0d1f4SJeff Cody mid_img = "../mid.img" 2936bf0d1f4SJeff Cody backing_img = "../dir1/backing.img" 2946bf0d1f4SJeff Cody 2956bf0d1f4SJeff Cody backing_img_abs = os.path.join(iotests.test_dir, dir1, 'backing.img') 2966bf0d1f4SJeff Cody mid_img_abs = os.path.join(iotests.test_dir, dir2, 'mid.img') 2976bf0d1f4SJeff Cody 2986bf0d1f4SJeff Cody def setUp(self): 2996bf0d1f4SJeff Cody try: 3006bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir1)) 3016bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir2)) 3026bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir3)) 3036bf0d1f4SJeff Cody except OSError as exception: 3046bf0d1f4SJeff Cody if exception.errno != errno.EEXIST: 3056bf0d1f4SJeff Cody raise 306915365a9SFam Zheng iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len) 307b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 308b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.backing_img_abs, 309b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 310b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 311b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.mid_img_abs, 312b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 313b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.backing_img, 314b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 315b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.mid_img, 316b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 31790c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs) 31890c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs) 3196bf0d1f4SJeff Cody self.vm = iotests.VM().add_drive(self.test_img) 3206bf0d1f4SJeff Cody self.vm.launch() 3216bf0d1f4SJeff Cody 3226bf0d1f4SJeff Cody def tearDown(self): 3236bf0d1f4SJeff Cody self.vm.shutdown() 3246bf0d1f4SJeff Cody os.remove(self.test_img) 3256bf0d1f4SJeff Cody os.remove(self.mid_img_abs) 3266bf0d1f4SJeff Cody os.remove(self.backing_img_abs) 3276bf0d1f4SJeff Cody try: 3286bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir1)) 3296bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir3)) 3306bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir2)) 3316bf0d1f4SJeff Cody except OSError as exception: 3326bf0d1f4SJeff Cody if exception.errno != errno.EEXIST and exception.errno != errno.ENOTEMPTY: 3336bf0d1f4SJeff Cody raise 3346bf0d1f4SJeff Cody 3356bf0d1f4SJeff Cody def test_commit(self): 3364de43470SFam Zheng self.run_commit_test(self.mid_img, self.backing_img) 33790c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) 33890c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) 3396bf0d1f4SJeff Cody 3406bf0d1f4SJeff Cody def test_device_not_found(self): 3416bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img) 3426bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 3436bf0d1f4SJeff Cody 3446bf0d1f4SJeff Cody def test_top_same_base(self): 345fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3466bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img) 3476bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 348b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % self.mid_img) 3496bf0d1f4SJeff Cody 3506bf0d1f4SJeff Cody def test_top_invalid(self): 351fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3526bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img) 3536bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3546bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 3556bf0d1f4SJeff Cody 3566bf0d1f4SJeff Cody def test_base_invalid(self): 357fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3586bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile') 3596bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 360b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find 'badfile' in the backing chain") 3616bf0d1f4SJeff Cody 3626bf0d1f4SJeff Cody def test_top_is_active(self): 3634de43470SFam Zheng self.run_commit_test(self.test_img, self.backing_img) 36490c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) 36590c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) 3666bf0d1f4SJeff Cody 3676bf0d1f4SJeff Cody def test_top_and_base_reversed(self): 368fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3696bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img) 3706bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 371b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % self.mid_img) 3726bf0d1f4SJeff Cody 373747051cdSJeff Cody 374747051cdSJeff Codyclass TestSetSpeed(ImageCommitTestCase): 375747051cdSJeff Cody image_len = 80 * 1024 * 1024 # MB 376747051cdSJeff Cody 377747051cdSJeff Cody def setUp(self): 378747051cdSJeff Cody qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 379b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 380b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 381b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 382b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 383b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 38490c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img) 38590c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) 38651c493c5SMax Reitz self.vm = iotests.VM().add_drive('blkdebug::' + test_img) 387747051cdSJeff Cody self.vm.launch() 388747051cdSJeff Cody 389747051cdSJeff Cody def tearDown(self): 390747051cdSJeff Cody self.vm.shutdown() 391747051cdSJeff Cody os.remove(test_img) 392747051cdSJeff Cody os.remove(mid_img) 393747051cdSJeff Cody os.remove(backing_img) 394747051cdSJeff Cody 395747051cdSJeff Cody def test_set_speed(self): 396fb0a078fSFam Zheng self.assert_no_active_block_jobs() 397747051cdSJeff Cody 398b59b3d57SFam Zheng self.vm.pause_drive('drive0') 399747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) 400747051cdSJeff Cody self.assert_qmp(result, 'return', {}) 401747051cdSJeff Cody 402747051cdSJeff Cody # Ensure the speed we set was accepted 403747051cdSJeff Cody result = self.vm.qmp('query-block-jobs') 404747051cdSJeff Cody self.assert_qmp(result, 'return[0]/device', 'drive0') 405747051cdSJeff Cody self.assert_qmp(result, 'return[0]/speed', 1024 * 1024) 406747051cdSJeff Cody 407b59b3d57SFam Zheng self.cancel_and_wait(resume=True) 408747051cdSJeff Cody 4098b9a30caSFam Zhengclass TestActiveZeroLengthImage(TestSingleDrive): 4108b9a30caSFam Zheng image_len = 0 411747051cdSJeff Cody 412bcdce5a7SAlberto Garciaclass TestReopenOverlay(ImageCommitTestCase): 413bcdce5a7SAlberto Garcia image_len = 1024 * 1024 414bcdce5a7SAlberto Garcia img0 = os.path.join(iotests.test_dir, '0.img') 415bcdce5a7SAlberto Garcia img1 = os.path.join(iotests.test_dir, '1.img') 416bcdce5a7SAlberto Garcia img2 = os.path.join(iotests.test_dir, '2.img') 417bcdce5a7SAlberto Garcia img3 = os.path.join(iotests.test_dir, '3.img') 418bcdce5a7SAlberto Garcia 419bcdce5a7SAlberto Garcia def setUp(self): 420bcdce5a7SAlberto Garcia iotests.create_image(self.img0, self.image_len) 421b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 422b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img0, '-F', 'raw', self.img1) 423b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 424b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img1, 425b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img2) 426b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 427b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img2, 428b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img3) 429bcdce5a7SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1) 430bcdce5a7SAlberto Garcia self.vm = iotests.VM().add_drive(self.img3) 431bcdce5a7SAlberto Garcia self.vm.launch() 432bcdce5a7SAlberto Garcia 433bcdce5a7SAlberto Garcia def tearDown(self): 434bcdce5a7SAlberto Garcia self.vm.shutdown() 435bcdce5a7SAlberto Garcia os.remove(self.img0) 436bcdce5a7SAlberto Garcia os.remove(self.img1) 437bcdce5a7SAlberto Garcia os.remove(self.img2) 438bcdce5a7SAlberto Garcia os.remove(self.img3) 439bcdce5a7SAlberto Garcia 440bcdce5a7SAlberto Garcia # This tests what happens when the overlay image of the 'top' node 441bcdce5a7SAlberto Garcia # needs to be reopened in read-write mode in order to update the 442bcdce5a7SAlberto Garcia # backing image string. 443bcdce5a7SAlberto Garcia def test_reopen_overlay(self): 444bcdce5a7SAlberto Garcia self.run_commit_test(self.img1, self.img0) 445bcdce5a7SAlberto Garcia 446d4398489SKevin Wolfclass TestErrorHandling(iotests.QMPTestCase): 447d4398489SKevin Wolf image_len = 2 * 1024 * 1024 448d4398489SKevin Wolf 449d4398489SKevin Wolf def setUp(self): 450d4398489SKevin Wolf iotests.create_image(backing_img, self.image_len) 451b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 452b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, 453b66ff2c2SEric Blake '-F', 'raw', mid_img) 454b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 455b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 456b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 457d4398489SKevin Wolf 458d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img) 459d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img) 460d4398489SKevin Wolf 461d4398489SKevin Wolf self.vm = iotests.VM() 462d4398489SKevin Wolf self.vm.launch() 463d4398489SKevin Wolf 464d4398489SKevin Wolf self.blkdebug_file = iotests.file_path("blkdebug.conf") 465d4398489SKevin Wolf 466d4398489SKevin Wolf def tearDown(self): 467d4398489SKevin Wolf self.vm.shutdown() 468d4398489SKevin Wolf os.remove(test_img) 469d4398489SKevin Wolf os.remove(mid_img) 470d4398489SKevin Wolf os.remove(backing_img) 471d4398489SKevin Wolf 472d4398489SKevin Wolf def blockdev_add(self, **kwargs): 473d4398489SKevin Wolf result = self.vm.qmp('blockdev-add', **kwargs) 474d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 475d4398489SKevin Wolf 476d4398489SKevin Wolf def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None): 477d4398489SKevin Wolf self.blockdev_add(node_name='base-file', driver='file', 478d4398489SKevin Wolf filename=backing_img) 479d4398489SKevin Wolf self.blockdev_add(node_name='mid-file', driver='file', 480d4398489SKevin Wolf filename=mid_img) 481d4398489SKevin Wolf self.blockdev_add(node_name='top-file', driver='file', 482d4398489SKevin Wolf filename=test_img) 483d4398489SKevin Wolf 484d4398489SKevin Wolf if base_debug: 485d4398489SKevin Wolf self.blockdev_add(node_name='base-dbg', driver='blkdebug', 486d4398489SKevin Wolf image='base-file', inject_error=base_debug) 487d4398489SKevin Wolf if mid_debug: 488d4398489SKevin Wolf self.blockdev_add(node_name='mid-dbg', driver='blkdebug', 489d4398489SKevin Wolf image='mid-file', inject_error=mid_debug) 490d4398489SKevin Wolf if top_debug: 491d4398489SKevin Wolf self.blockdev_add(node_name='top-dbg', driver='blkdebug', 492d4398489SKevin Wolf image='top-file', inject_error=top_debug) 493d4398489SKevin Wolf 494d4398489SKevin Wolf self.blockdev_add(node_name='base-fmt', driver='raw', 495d4398489SKevin Wolf file=('base-dbg' if base_debug else 'base-file')) 496d4398489SKevin Wolf self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt, 497d4398489SKevin Wolf file=('mid-dbg' if mid_debug else 'mid-file'), 498d4398489SKevin Wolf backing='base-fmt') 499d4398489SKevin Wolf self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt, 500d4398489SKevin Wolf file=('top-dbg' if top_debug else 'top-file'), 501d4398489SKevin Wolf backing='mid-fmt') 502d4398489SKevin Wolf 503d4398489SKevin Wolf def run_job(self, expected_events, error_pauses_job=False): 504d4398489SKevin Wolf match_device = {'data': {'device': 'job0'}} 505d4398489SKevin Wolf events = [ 506d4398489SKevin Wolf ('BLOCK_JOB_COMPLETED', match_device), 507d4398489SKevin Wolf ('BLOCK_JOB_CANCELLED', match_device), 508d4398489SKevin Wolf ('BLOCK_JOB_ERROR', match_device), 509d4398489SKevin Wolf ('BLOCK_JOB_READY', match_device), 510d4398489SKevin Wolf ] 511d4398489SKevin Wolf 512d4398489SKevin Wolf completed = False 513d4398489SKevin Wolf log = [] 514d4398489SKevin Wolf while not completed: 515d4398489SKevin Wolf ev = self.vm.events_wait(events, timeout=5.0) 516d4398489SKevin Wolf if ev['event'] == 'BLOCK_JOB_COMPLETED': 517d4398489SKevin Wolf completed = True 518d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_ERROR': 519d4398489SKevin Wolf if error_pauses_job: 520d4398489SKevin Wolf result = self.vm.qmp('block-job-resume', device='job0') 521d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 522d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_READY': 523d4398489SKevin Wolf result = self.vm.qmp('block-job-complete', device='job0') 524d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 525d4398489SKevin Wolf else: 526d4398489SKevin Wolf self.fail("Unexpected event: %s" % ev) 527d4398489SKevin Wolf log.append(iotests.filter_qmp_event(ev)) 528d4398489SKevin Wolf 529d4398489SKevin Wolf self.maxDiff = None 530d4398489SKevin Wolf self.assertEqual(expected_events, log) 531d4398489SKevin Wolf 532d4398489SKevin Wolf def event_error(self, op, action): 533d4398489SKevin Wolf return { 534d4398489SKevin Wolf 'event': 'BLOCK_JOB_ERROR', 535d4398489SKevin Wolf 'data': {'action': action, 'device': 'job0', 'operation': op}, 536d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'} 537d4398489SKevin Wolf } 538d4398489SKevin Wolf 539d4398489SKevin Wolf def event_ready(self): 540d4398489SKevin Wolf return { 541d4398489SKevin Wolf 'event': 'BLOCK_JOB_READY', 542d4398489SKevin Wolf 'data': {'device': 'job0', 543d4398489SKevin Wolf 'len': 524288, 544d4398489SKevin Wolf 'offset': 524288, 545d4398489SKevin Wolf 'speed': 0, 546d4398489SKevin Wolf 'type': 'commit'}, 547d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 548d4398489SKevin Wolf } 549d4398489SKevin Wolf 550d4398489SKevin Wolf def event_completed(self, errmsg=None, active=True): 551d4398489SKevin Wolf max_len = 524288 if active else self.image_len 552d4398489SKevin Wolf data = { 553d4398489SKevin Wolf 'device': 'job0', 554d4398489SKevin Wolf 'len': max_len, 555d4398489SKevin Wolf 'offset': 0 if errmsg else max_len, 556d4398489SKevin Wolf 'speed': 0, 557d4398489SKevin Wolf 'type': 'commit' 558d4398489SKevin Wolf } 559d4398489SKevin Wolf if errmsg: 560d4398489SKevin Wolf data['error'] = errmsg 561d4398489SKevin Wolf 562d4398489SKevin Wolf return { 563d4398489SKevin Wolf 'event': 'BLOCK_JOB_COMPLETED', 564d4398489SKevin Wolf 'data': data, 565d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 566d4398489SKevin Wolf } 567d4398489SKevin Wolf 568d4398489SKevin Wolf def blkdebug_event(self, event, is_raw=False): 569d4398489SKevin Wolf if event: 570d4398489SKevin Wolf return [{ 571d4398489SKevin Wolf 'event': event, 572d4398489SKevin Wolf 'sector': 512 if is_raw else 1024, 573d4398489SKevin Wolf 'once': True, 574d4398489SKevin Wolf }] 575d4398489SKevin Wolf return None 576d4398489SKevin Wolf 577d4398489SKevin Wolf def prepare_and_start_job(self, on_error, active=True, 578d4398489SKevin Wolf top_event=None, mid_event=None, base_event=None): 579d4398489SKevin Wolf 580d4398489SKevin Wolf top_debug = self.blkdebug_event(top_event) 581d4398489SKevin Wolf mid_debug = self.blkdebug_event(mid_event) 582d4398489SKevin Wolf base_debug = self.blkdebug_event(base_event, True) 583d4398489SKevin Wolf 584d4398489SKevin Wolf self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug, 585d4398489SKevin Wolf base_debug=base_debug) 586d4398489SKevin Wolf 587d4398489SKevin Wolf result = self.vm.qmp('block-commit', job_id='job0', device='top-fmt', 588d4398489SKevin Wolf top_node='top-fmt' if active else 'mid-fmt', 589d4398489SKevin Wolf base_node='mid-fmt' if active else 'base-fmt', 590d4398489SKevin Wolf on_error=on_error) 591d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 592d4398489SKevin Wolf 593d4398489SKevin Wolf def testActiveReadErrorReport(self): 594d4398489SKevin Wolf self.prepare_and_start_job('report', top_event='read_aio') 595d4398489SKevin Wolf self.run_job([ 596d4398489SKevin Wolf self.event_error('read', 'report'), 597d4398489SKevin Wolf self.event_completed('Input/output error') 598d4398489SKevin Wolf ]) 599d4398489SKevin Wolf 600d4398489SKevin Wolf self.vm.shutdown() 601d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 602d4398489SKevin Wolf 'target image matches source after error') 603d4398489SKevin Wolf 604d4398489SKevin Wolf def testActiveReadErrorStop(self): 605d4398489SKevin Wolf self.prepare_and_start_job('stop', top_event='read_aio') 606d4398489SKevin Wolf self.run_job([ 607d4398489SKevin Wolf self.event_error('read', 'stop'), 608d4398489SKevin Wolf self.event_ready(), 609d4398489SKevin Wolf self.event_completed() 610d4398489SKevin Wolf ], error_pauses_job=True) 611d4398489SKevin Wolf 612d4398489SKevin Wolf self.vm.shutdown() 613d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 614d4398489SKevin Wolf 'target image does not match source after commit') 615d4398489SKevin Wolf 616d4398489SKevin Wolf def testActiveReadErrorIgnore(self): 617d4398489SKevin Wolf self.prepare_and_start_job('ignore', top_event='read_aio') 618d4398489SKevin Wolf self.run_job([ 619d4398489SKevin Wolf self.event_error('read', 'ignore'), 620d4398489SKevin Wolf self.event_ready(), 621d4398489SKevin Wolf self.event_completed() 622d4398489SKevin Wolf ]) 623d4398489SKevin Wolf 624d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 625d4398489SKevin Wolf self.vm.shutdown() 626d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 627d4398489SKevin Wolf 'target image does not match source after commit') 628d4398489SKevin Wolf 629d4398489SKevin Wolf def testActiveWriteErrorReport(self): 630d4398489SKevin Wolf self.prepare_and_start_job('report', mid_event='write_aio') 631d4398489SKevin Wolf self.run_job([ 632d4398489SKevin Wolf self.event_error('write', 'report'), 633d4398489SKevin Wolf self.event_completed('Input/output error') 634d4398489SKevin Wolf ]) 635d4398489SKevin Wolf 636d4398489SKevin Wolf self.vm.shutdown() 637d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 638d4398489SKevin Wolf 'target image matches source after error') 639d4398489SKevin Wolf 640d4398489SKevin Wolf def testActiveWriteErrorStop(self): 641d4398489SKevin Wolf self.prepare_and_start_job('stop', mid_event='write_aio') 642d4398489SKevin Wolf self.run_job([ 643d4398489SKevin Wolf self.event_error('write', 'stop'), 644d4398489SKevin Wolf self.event_ready(), 645d4398489SKevin Wolf self.event_completed() 646d4398489SKevin Wolf ], error_pauses_job=True) 647d4398489SKevin Wolf 648d4398489SKevin Wolf self.vm.shutdown() 649d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 650d4398489SKevin Wolf 'target image does not match source after commit') 651d4398489SKevin Wolf 652d4398489SKevin Wolf def testActiveWriteErrorIgnore(self): 653d4398489SKevin Wolf self.prepare_and_start_job('ignore', mid_event='write_aio') 654d4398489SKevin Wolf self.run_job([ 655d4398489SKevin Wolf self.event_error('write', 'ignore'), 656d4398489SKevin Wolf self.event_ready(), 657d4398489SKevin Wolf self.event_completed() 658d4398489SKevin Wolf ]) 659d4398489SKevin Wolf 660d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 661d4398489SKevin Wolf self.vm.shutdown() 662d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 663d4398489SKevin Wolf 'target image does not match source after commit') 664d4398489SKevin Wolf 665d4398489SKevin Wolf def testIntermediateReadErrorReport(self): 666d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, mid_event='read_aio') 667d4398489SKevin Wolf self.run_job([ 668d4398489SKevin Wolf self.event_error('read', 'report'), 669d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 670d4398489SKevin Wolf ]) 671d4398489SKevin Wolf 672d4398489SKevin Wolf self.vm.shutdown() 673d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 674d4398489SKevin Wolf 'target image matches source after error') 675d4398489SKevin Wolf 676d4398489SKevin Wolf def testIntermediateReadErrorStop(self): 677d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, mid_event='read_aio') 678d4398489SKevin Wolf self.run_job([ 679d4398489SKevin Wolf self.event_error('read', 'stop'), 680d4398489SKevin Wolf self.event_completed(active=False) 681d4398489SKevin Wolf ], error_pauses_job=True) 682d4398489SKevin Wolf 683d4398489SKevin Wolf self.vm.shutdown() 684d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 685d4398489SKevin Wolf 'target image does not match source after commit') 686d4398489SKevin Wolf 687d4398489SKevin Wolf def testIntermediateReadErrorIgnore(self): 688d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, mid_event='read_aio') 689d4398489SKevin Wolf self.run_job([ 690d4398489SKevin Wolf self.event_error('read', 'ignore'), 691d4398489SKevin Wolf self.event_completed(active=False) 692d4398489SKevin Wolf ]) 693d4398489SKevin Wolf 694d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 695d4398489SKevin Wolf self.vm.shutdown() 696d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 697d4398489SKevin Wolf 'target image does not match source after commit') 698d4398489SKevin Wolf 699d4398489SKevin Wolf def testIntermediateWriteErrorReport(self): 700d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, base_event='write_aio') 701d4398489SKevin Wolf self.run_job([ 702d4398489SKevin Wolf self.event_error('write', 'report'), 703d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 704d4398489SKevin Wolf ]) 705d4398489SKevin Wolf 706d4398489SKevin Wolf self.vm.shutdown() 707d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 708d4398489SKevin Wolf 'target image matches source after error') 709d4398489SKevin Wolf 710d4398489SKevin Wolf def testIntermediateWriteErrorStop(self): 711d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, base_event='write_aio') 712d4398489SKevin Wolf self.run_job([ 713d4398489SKevin Wolf self.event_error('write', 'stop'), 714d4398489SKevin Wolf self.event_completed(active=False) 715d4398489SKevin Wolf ], error_pauses_job=True) 716d4398489SKevin Wolf 717d4398489SKevin Wolf self.vm.shutdown() 718d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 719d4398489SKevin Wolf 'target image does not match source after commit') 720d4398489SKevin Wolf 721d4398489SKevin Wolf def testIntermediateWriteErrorIgnore(self): 722d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, base_event='write_aio') 723d4398489SKevin Wolf self.run_job([ 724d4398489SKevin Wolf self.event_error('write', 'ignore'), 725d4398489SKevin Wolf self.event_completed(active=False) 726d4398489SKevin Wolf ]) 727d4398489SKevin Wolf 728d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 729d4398489SKevin Wolf self.vm.shutdown() 730d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 731d4398489SKevin Wolf 'target image does not match source after commit') 732d4398489SKevin Wolf 733ffbba7fdSMax Reitzclass TestCommitWithFilters(iotests.QMPTestCase): 734ffbba7fdSMax Reitz img0 = os.path.join(iotests.test_dir, '0.img') 735ffbba7fdSMax Reitz img1 = os.path.join(iotests.test_dir, '1.img') 736ffbba7fdSMax Reitz img2 = os.path.join(iotests.test_dir, '2.img') 737ffbba7fdSMax Reitz img3 = os.path.join(iotests.test_dir, '3.img') 738ffbba7fdSMax Reitz 739ffbba7fdSMax Reitz def do_test_io(self, read_or_write): 740ffbba7fdSMax Reitz for index, pattern_file in enumerate(self.pattern_files): 741ffbba7fdSMax Reitz result = qemu_io('-f', iotests.imgfmt, 742ffbba7fdSMax Reitz '-c', 743ffbba7fdSMax Reitz f'{read_or_write} -P {index + 1} {index}M 1M', 744ffbba7fdSMax Reitz pattern_file) 745ffbba7fdSMax Reitz self.assertFalse('Pattern verification failed' in result) 746ffbba7fdSMax Reitz 747ffbba7fdSMax Reitz def setUp(self): 748ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M') 749ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M') 750ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img2, '64M') 751ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img3, '64M') 752ffbba7fdSMax Reitz 753ffbba7fdSMax Reitz # Distributions of the patterns in the files; this is checked 754ffbba7fdSMax Reitz # by tearDown() and should be changed by the test cases as is 755ffbba7fdSMax Reitz # necessary 756ffbba7fdSMax Reitz self.pattern_files = [self.img0, self.img1, self.img2, self.img3] 757ffbba7fdSMax Reitz 758ffbba7fdSMax Reitz self.do_test_io('write') 759ffbba7fdSMax Reitz 760ffbba7fdSMax Reitz self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi') 761ffbba7fdSMax Reitz self.vm.launch() 762ffbba7fdSMax Reitz 763ffbba7fdSMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 764ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 765ffbba7fdSMax Reitz 766ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-add', **{ 767ffbba7fdSMax Reitz 'node-name': 'top-filter', 768ffbba7fdSMax Reitz 'driver': 'throttle', 769ffbba7fdSMax Reitz 'throttle-group': 'tg', 770ffbba7fdSMax Reitz 'file': { 771ffbba7fdSMax Reitz 'node-name': 'cow-3', 772ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 773ffbba7fdSMax Reitz 'file': { 774ffbba7fdSMax Reitz 'driver': 'file', 775ffbba7fdSMax Reitz 'filename': self.img3 776ffbba7fdSMax Reitz }, 777ffbba7fdSMax Reitz 'backing': { 778ffbba7fdSMax Reitz 'node-name': 'cow-2', 779ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 780ffbba7fdSMax Reitz 'file': { 781ffbba7fdSMax Reitz 'driver': 'file', 782ffbba7fdSMax Reitz 'filename': self.img2 783ffbba7fdSMax Reitz }, 784ffbba7fdSMax Reitz 'backing': { 785ffbba7fdSMax Reitz 'node-name': 'cow-1', 786ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 787ffbba7fdSMax Reitz 'file': { 788ffbba7fdSMax Reitz 'driver': 'file', 789ffbba7fdSMax Reitz 'filename': self.img1 790ffbba7fdSMax Reitz }, 791ffbba7fdSMax Reitz 'backing': { 792ffbba7fdSMax Reitz 'node-name': 'bottom-filter', 793ffbba7fdSMax Reitz 'driver': 'throttle', 794ffbba7fdSMax Reitz 'throttle-group': 'tg', 795ffbba7fdSMax Reitz 'file': { 796ffbba7fdSMax Reitz 'node-name': 'cow-0', 797ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 798ffbba7fdSMax Reitz 'file': { 799ffbba7fdSMax Reitz 'driver': 'file', 800ffbba7fdSMax Reitz 'filename': self.img0 801ffbba7fdSMax Reitz } 802ffbba7fdSMax Reitz } 803ffbba7fdSMax Reitz } 804ffbba7fdSMax Reitz } 805ffbba7fdSMax Reitz } 806ffbba7fdSMax Reitz } 807ffbba7fdSMax Reitz }) 808ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 809ffbba7fdSMax Reitz 810ffbba7fdSMax Reitz def tearDown(self): 811ffbba7fdSMax Reitz self.vm.shutdown() 812ffbba7fdSMax Reitz self.do_test_io('read') 813ffbba7fdSMax Reitz 814ffbba7fdSMax Reitz os.remove(self.img3) 815ffbba7fdSMax Reitz os.remove(self.img2) 816ffbba7fdSMax Reitz os.remove(self.img1) 817ffbba7fdSMax Reitz os.remove(self.img0) 818ffbba7fdSMax Reitz 819ffbba7fdSMax Reitz # Filters make for funny filenames, so we cannot just use 820ffbba7fdSMax Reitz # self.imgX to get them 821ffbba7fdSMax Reitz def get_filename(self, node): 822ffbba7fdSMax Reitz return self.vm.node_info(node)['image']['filename'] 823ffbba7fdSMax Reitz 824ffbba7fdSMax Reitz def test_filterless_commit(self): 825ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 826ffbba7fdSMax Reitz job_id='commit', 827ffbba7fdSMax Reitz device='top-filter', 828ffbba7fdSMax Reitz top_node='cow-2', 829ffbba7fdSMax Reitz base_node='cow-1') 830ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 831ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 832ffbba7fdSMax Reitz 833ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-3')) 834ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-2')) 835ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-1')) 836ffbba7fdSMax Reitz 837ffbba7fdSMax Reitz # 2 has been comitted into 1 838ffbba7fdSMax Reitz self.pattern_files[2] = self.img1 839ffbba7fdSMax Reitz 840ffbba7fdSMax Reitz def test_commit_through_filter(self): 841ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 842ffbba7fdSMax Reitz job_id='commit', 843ffbba7fdSMax Reitz device='top-filter', 844ffbba7fdSMax Reitz top_node='cow-1', 845ffbba7fdSMax Reitz base_node='cow-0') 846ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 847ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 848ffbba7fdSMax Reitz 849ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 850ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-1')) 851ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('bottom-filter')) 852ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-0')) 853ffbba7fdSMax Reitz 854ffbba7fdSMax Reitz # 1 has been comitted into 0 855ffbba7fdSMax Reitz self.pattern_files[1] = self.img0 856ffbba7fdSMax Reitz 857ffbba7fdSMax Reitz def test_filtered_active_commit_with_filter(self): 858ffbba7fdSMax Reitz # Add a device, so the commit job finds a parent it can change 859ffbba7fdSMax Reitz # to point to the base node (so we can test that top-filter is 860ffbba7fdSMax Reitz # dropped from the graph) 861ffbba7fdSMax Reitz result = self.vm.qmp('device_add', id='drv0', driver='scsi-hd', 862ffbba7fdSMax Reitz bus='vio-scsi.0', drive='top-filter') 863ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 864ffbba7fdSMax Reitz 865ffbba7fdSMax Reitz # Try to release our reference to top-filter; that should not 866ffbba7fdSMax Reitz # work because drv0 uses it 867ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 868ffbba7fdSMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 869ffbba7fdSMax Reitz self.assert_qmp(result, 'error/desc', 'Node top-filter is in use') 870ffbba7fdSMax Reitz 871ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 872ffbba7fdSMax Reitz job_id='commit', 873ffbba7fdSMax Reitz device='top-filter', 874ffbba7fdSMax Reitz base_node='cow-2') 875ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 876ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 877ffbba7fdSMax Reitz 878ffbba7fdSMax Reitz # Try to release our reference to top-filter again 879ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 880ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 881ffbba7fdSMax Reitz 882ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('top-filter')) 883ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 884ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 885ffbba7fdSMax Reitz 886ffbba7fdSMax Reitz # Check that drv0 is now connected to cow-2 887ffbba7fdSMax Reitz blockdevs = self.vm.qmp('query-block')['return'] 888ffbba7fdSMax Reitz drv0 = next(dev for dev in blockdevs if dev['qdev'] == 'drv0') 889ffbba7fdSMax Reitz self.assertEqual(drv0['inserted']['node-name'], 'cow-2') 890ffbba7fdSMax Reitz 891ffbba7fdSMax Reitz # 3 has been comitted into 2 892ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 893ffbba7fdSMax Reitz 894ffbba7fdSMax Reitz def test_filtered_active_commit_without_filter(self): 895ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 896ffbba7fdSMax Reitz job_id='commit', 897ffbba7fdSMax Reitz device='top-filter', 898ffbba7fdSMax Reitz top_node='cow-3', 899ffbba7fdSMax Reitz base_node='cow-2') 900ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 901ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 902ffbba7fdSMax Reitz 903ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('top-filter')) 904ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 905ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 906ffbba7fdSMax Reitz 907ffbba7fdSMax Reitz # 3 has been comitted into 2 908ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 909ffbba7fdSMax Reitz 910374eedd1SMax Reitzclass TestCommitWithOverriddenBacking(iotests.QMPTestCase): 911374eedd1SMax Reitz img_base_a = os.path.join(iotests.test_dir, 'base_a.img') 912374eedd1SMax Reitz img_base_b = os.path.join(iotests.test_dir, 'base_b.img') 913374eedd1SMax Reitz img_top = os.path.join(iotests.test_dir, 'top.img') 914374eedd1SMax Reitz 915374eedd1SMax Reitz def setUp(self): 916374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_a, '1M') 917374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_b, '1M') 918497a30dbSEric Blake qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, 919497a30dbSEric Blake '-F', iotests.imgfmt, self.img_top) 920374eedd1SMax Reitz 921374eedd1SMax Reitz self.vm = iotests.VM() 922374eedd1SMax Reitz self.vm.launch() 923374eedd1SMax Reitz 924374eedd1SMax Reitz # Use base_b instead of base_a as the backing of top 925374eedd1SMax Reitz result = self.vm.qmp('blockdev-add', **{ 926374eedd1SMax Reitz 'node-name': 'top', 927374eedd1SMax Reitz 'driver': iotests.imgfmt, 928374eedd1SMax Reitz 'file': { 929374eedd1SMax Reitz 'driver': 'file', 930374eedd1SMax Reitz 'filename': self.img_top 931374eedd1SMax Reitz }, 932374eedd1SMax Reitz 'backing': { 933374eedd1SMax Reitz 'node-name': 'base', 934374eedd1SMax Reitz 'driver': iotests.imgfmt, 935374eedd1SMax Reitz 'file': { 936374eedd1SMax Reitz 'driver': 'file', 937374eedd1SMax Reitz 'filename': self.img_base_b 938374eedd1SMax Reitz } 939374eedd1SMax Reitz } 940374eedd1SMax Reitz }) 941374eedd1SMax Reitz self.assert_qmp(result, 'return', {}) 942374eedd1SMax Reitz 943374eedd1SMax Reitz def tearDown(self): 944374eedd1SMax Reitz self.vm.shutdown() 945374eedd1SMax Reitz os.remove(self.img_top) 946374eedd1SMax Reitz os.remove(self.img_base_a) 947374eedd1SMax Reitz os.remove(self.img_base_b) 948374eedd1SMax Reitz 949374eedd1SMax Reitz def test_commit_to_a(self): 950374eedd1SMax Reitz # Try committing to base_a (which should fail, as top's 951374eedd1SMax Reitz # backing image is base_b instead) 952374eedd1SMax Reitz result = self.vm.qmp('block-commit', 953374eedd1SMax Reitz job_id='commit', 954374eedd1SMax Reitz device='top', 955374eedd1SMax Reitz base=self.img_base_a) 956374eedd1SMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 957374eedd1SMax Reitz 958374eedd1SMax Reitz def test_commit_to_b(self): 959374eedd1SMax Reitz # Try committing to base_b (which should work, since that is 960374eedd1SMax Reitz # actually top's backing image) 961374eedd1SMax Reitz result = self.vm.qmp('block-commit', 962374eedd1SMax Reitz job_id='commit', 963374eedd1SMax Reitz device='top', 964374eedd1SMax Reitz base=self.img_base_b) 965374eedd1SMax Reitz self.assert_qmp(result, 'return', {}) 966374eedd1SMax Reitz 967374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_READY') 968374eedd1SMax Reitz self.vm.qmp('block-job-complete', device='commit') 969374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_COMPLETED') 970374eedd1SMax Reitz 971747051cdSJeff Codyif __name__ == '__main__': 972103cbc77SMax Reitz iotests.main(supported_fmts=['qcow2', 'qed'], 973103cbc77SMax Reitz supported_protocols=['file']) 974