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