1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 2747051cdSJeff Cody# 3747051cdSJeff Cody# Tests for image block commit. 4747051cdSJeff Cody# 5747051cdSJeff Cody# Copyright (C) 2012 IBM, Corp. 6747051cdSJeff Cody# Copyright (C) 2012 Red Hat, Inc. 7747051cdSJeff Cody# 8747051cdSJeff Cody# This program is free software; you can redistribute it and/or modify 9747051cdSJeff Cody# it under the terms of the GNU General Public License as published by 10747051cdSJeff Cody# the Free Software Foundation; either version 2 of the License, or 11747051cdSJeff Cody# (at your option) any later version. 12747051cdSJeff Cody# 13747051cdSJeff Cody# This program is distributed in the hope that it will be useful, 14747051cdSJeff Cody# but WITHOUT ANY WARRANTY; without even the implied warranty of 15747051cdSJeff Cody# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16747051cdSJeff Cody# GNU General Public License for more details. 17747051cdSJeff Cody# 18747051cdSJeff Cody# You should have received a copy of the GNU General Public License 19747051cdSJeff Cody# along with this program. If not, see <http://www.gnu.org/licenses/>. 20747051cdSJeff Cody# 21747051cdSJeff Cody# Test for live block commit 22747051cdSJeff Cody# Derived from Image Streaming Test 030 23747051cdSJeff Cody 24747051cdSJeff Codyimport time 25747051cdSJeff Codyimport os 26747051cdSJeff Codyimport iotests 27747051cdSJeff Codyfrom iotests import qemu_img, qemu_io 28747051cdSJeff Codyimport struct 296bf0d1f4SJeff Codyimport errno 30747051cdSJeff Cody 31747051cdSJeff Codybacking_img = os.path.join(iotests.test_dir, 'backing.img') 32747051cdSJeff Codymid_img = os.path.join(iotests.test_dir, 'mid.img') 33747051cdSJeff Codytest_img = os.path.join(iotests.test_dir, 'test.img') 34747051cdSJeff Cody 35747051cdSJeff Codyclass ImageCommitTestCase(iotests.QMPTestCase): 36747051cdSJeff Cody '''Abstract base class for image commit test cases''' 37747051cdSJeff Cody 387676e2c5SJeff Cody def wait_for_complete(self, need_ready=False): 394de43470SFam Zheng completed = False 408b9a30caSFam Zheng ready = False 414de43470SFam Zheng while not completed: 424de43470SFam Zheng for event in self.vm.get_qmp_events(wait=True): 434de43470SFam Zheng if event['event'] == 'BLOCK_JOB_COMPLETED': 44bcdce5a7SAlberto Garcia self.assert_qmp_absent(event, 'data/error') 454de43470SFam Zheng self.assert_qmp(event, 'data/type', 'commit') 464de43470SFam Zheng self.assert_qmp(event, 'data/device', 'drive0') 471d3ba15aSMax Reitz self.assert_qmp(event, 'data/offset', event['data']['len']) 488b9a30caSFam Zheng if need_ready: 498b9a30caSFam Zheng self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event") 504de43470SFam Zheng completed = True 514de43470SFam Zheng elif event['event'] == 'BLOCK_JOB_READY': 528b9a30caSFam Zheng ready = True 534de43470SFam Zheng self.assert_qmp(event, 'data/type', 'commit') 544de43470SFam Zheng self.assert_qmp(event, 'data/device', 'drive0') 554de43470SFam Zheng self.vm.qmp('block-job-complete', device='drive0') 564de43470SFam Zheng 57fb0a078fSFam Zheng self.assert_no_active_block_jobs() 584de43470SFam Zheng self.vm.shutdown() 594de43470SFam Zheng 60d57177a4SKevin Wolf def run_commit_test(self, top, base, need_ready=False, node_names=False): 617676e2c5SJeff Cody self.assert_no_active_block_jobs() 62d57177a4SKevin Wolf if node_names: 63d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node=top, base_node=base) 64d57177a4SKevin Wolf else: 657676e2c5SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) 667676e2c5SJeff Cody self.assert_qmp(result, 'return', {}) 677676e2c5SJeff Cody self.wait_for_complete(need_ready) 687676e2c5SJeff Cody 697676e2c5SJeff Cody def run_default_commit_test(self): 707676e2c5SJeff Cody self.assert_no_active_block_jobs() 717676e2c5SJeff Cody result = self.vm.qmp('block-commit', device='drive0') 727676e2c5SJeff Cody self.assert_qmp(result, 'return', {}) 737676e2c5SJeff Cody self.wait_for_complete() 747676e2c5SJeff Cody 75747051cdSJeff Codyclass TestSingleDrive(ImageCommitTestCase): 76c3971b88SKevin Wolf # Need some space after the copied data so that throttling is effective in 77c3971b88SKevin Wolf # tests that use it rather than just completing the job immediately 78c3971b88SKevin Wolf image_len = 2 * 1024 * 1024 79747051cdSJeff Cody test_len = 1 * 1024 * 256 80747051cdSJeff Cody 81747051cdSJeff Cody def setUp(self): 828b9a30caSFam Zheng iotests.create_image(backing_img, self.image_len) 83b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 84b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 85b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 86b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 87b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 8890c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) 8990c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) 90d3c8c674SKevin Wolf self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none") 91f357576fSJohn Snow self.vm.add_device(iotests.get_virtio_scsi_device()) 92c3971b88SKevin Wolf self.vm.add_device("scsi-hd,id=scsi0,drive=drive0") 93747051cdSJeff Cody self.vm.launch() 9486472071SMax Reitz self.has_quit = False 95747051cdSJeff Cody 96747051cdSJeff Cody def tearDown(self): 9786472071SMax Reitz self.vm.shutdown(has_quit=self.has_quit) 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 self.has_quit = True 13086472071SMax Reitz 13186472071SMax Reitz # Same as above, but this time we add the filter after starting the job 1329442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 13386472071SMax Reitz def test_commit_plus_filter_and_quit(self): 13486472071SMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 13586472071SMax Reitz self.assert_qmp(result, 'return', {}) 13686472071SMax Reitz 13786472071SMax Reitz result = self.vm.qmp('block-commit', device='drive0') 13886472071SMax Reitz self.assert_qmp(result, 'return', {}) 13986472071SMax Reitz 14086472071SMax Reitz # Add a filter outside of the backing chain 14186472071SMax Reitz result = self.vm.qmp('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 14286472071SMax Reitz self.assert_qmp(result, 'return', {}) 14386472071SMax Reitz 14486472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 14586472071SMax Reitz # block job and a bdrv_drain_all() 14686472071SMax Reitz result = self.vm.qmp('quit') 14786472071SMax Reitz self.assert_qmp(result, 'return', {}) 14886472071SMax Reitz 14986472071SMax Reitz self.has_quit = True 15086472071SMax Reitz 151747051cdSJeff Cody def test_device_not_found(self): 152747051cdSJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) 153747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 154747051cdSJeff Cody 155747051cdSJeff Cody def test_top_same_base(self): 156fb0a078fSFam Zheng self.assert_no_active_block_jobs() 157747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img) 158747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 159d5208c45SJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % backing_img) 160747051cdSJeff Cody 161747051cdSJeff Cody def test_top_invalid(self): 162fb0a078fSFam Zheng self.assert_no_active_block_jobs() 163747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img) 164747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 165747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 166747051cdSJeff Cody 167747051cdSJeff Cody def test_base_invalid(self): 168fb0a078fSFam Zheng self.assert_no_active_block_jobs() 169747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile') 170747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 171747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found') 172747051cdSJeff Cody 173d57177a4SKevin Wolf def test_top_node_invalid(self): 174d57177a4SKevin Wolf self.assert_no_active_block_jobs() 175d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base') 176d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 177d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile") 178d57177a4SKevin Wolf 179d57177a4SKevin Wolf def test_base_node_invalid(self): 180d57177a4SKevin Wolf self.assert_no_active_block_jobs() 181d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile') 182d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 183d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile") 184d57177a4SKevin Wolf 185d57177a4SKevin Wolf def test_top_path_and_node(self): 186d57177a4SKevin Wolf self.assert_no_active_block_jobs() 187d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', top='%s' % mid_img) 188d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 189d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top-node' and 'top' are mutually exclusive") 190d57177a4SKevin Wolf 191d57177a4SKevin Wolf def test_base_path_and_node(self): 192d57177a4SKevin Wolf self.assert_no_active_block_jobs() 193d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', base='%s' % backing_img) 194d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 195d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'base-node' and 'base' are mutually exclusive") 196d57177a4SKevin Wolf 197747051cdSJeff Cody def test_top_is_active(self): 1988b9a30caSFam Zheng self.run_commit_test(test_img, backing_img, need_ready=True) 19990c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 20090c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 201747051cdSJeff Cody 2027676e2c5SJeff Cody def test_top_is_default_active(self): 2037676e2c5SJeff Cody self.run_default_commit_test() 20490c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 20590c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 2067676e2c5SJeff Cody 207747051cdSJeff Cody def test_top_and_base_reversed(self): 208fb0a078fSFam Zheng self.assert_no_active_block_jobs() 209747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img) 210747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 211d5208c45SJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img) 212747051cdSJeff Cody 213d57177a4SKevin Wolf def test_top_and_base_node_reversed(self): 214d57177a4SKevin Wolf self.assert_no_active_block_jobs() 215d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='base', base_node='top') 216d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 217d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top' is not in this backing file chain") 218d57177a4SKevin Wolf 219d57177a4SKevin Wolf def test_top_node_in_wrong_chain(self): 220d57177a4SKevin Wolf self.assert_no_active_block_jobs() 221d57177a4SKevin Wolf 222d57177a4SKevin Wolf result = self.vm.qmp('blockdev-add', driver='null-co', node_name='null') 223d57177a4SKevin Wolf self.assert_qmp(result, 'return', {}) 224d57177a4SKevin Wolf 225d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') 226d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 227d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'null' is not in this backing file chain") 228d57177a4SKevin Wolf 229c3971b88SKevin Wolf # When the job is running on a BB that is automatically deleted on hot 230c3971b88SKevin Wolf # unplug, the job is cancelled when the device disappears 231c3971b88SKevin Wolf def test_hot_unplug(self): 232c3971b88SKevin Wolf if self.image_len == 0: 233c3971b88SKevin Wolf return 234c3971b88SKevin Wolf 235c3971b88SKevin Wolf self.assert_no_active_block_jobs() 236c3971b88SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top=mid_img, 2379a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 238c3971b88SKevin Wolf self.assert_qmp(result, 'return', {}) 239c3971b88SKevin Wolf result = self.vm.qmp('device_del', id='scsi0') 240c3971b88SKevin Wolf self.assert_qmp(result, 'return', {}) 241c3971b88SKevin Wolf 242c3971b88SKevin Wolf cancelled = False 243c3971b88SKevin Wolf deleted = False 244c3971b88SKevin Wolf while not cancelled or not deleted: 245c3971b88SKevin Wolf for event in self.vm.get_qmp_events(wait=True): 246c3971b88SKevin Wolf if event['event'] == 'DEVICE_DELETED': 247c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'scsi0') 248c3971b88SKevin Wolf deleted = True 249c3971b88SKevin Wolf elif event['event'] == 'BLOCK_JOB_CANCELLED': 250c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'drive0') 251c3971b88SKevin Wolf cancelled = True 2521dac83f1SKevin Wolf elif event['event'] == 'JOB_STATUS_CHANGE': 2531dac83f1SKevin Wolf self.assert_qmp(event, 'data/id', 'drive0') 254c3971b88SKevin Wolf else: 255c3971b88SKevin Wolf self.fail("Unexpected event %s" % (event['event'])) 256c3971b88SKevin Wolf 257c3971b88SKevin Wolf self.assert_no_active_block_jobs() 258747051cdSJeff Cody 259d3c8c674SKevin Wolf # Tests that the insertion of the commit_top filter node doesn't make a 260d3c8c674SKevin Wolf # difference to query-blockstat 261d3c8c674SKevin Wolf def test_implicit_node(self): 262d3c8c674SKevin Wolf if self.image_len == 0: 263d3c8c674SKevin Wolf return 264d3c8c674SKevin Wolf 265d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 266d3c8c674SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top=mid_img, 2679a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 268d3c8c674SKevin Wolf self.assert_qmp(result, 'return', {}) 269d3c8c674SKevin Wolf 270d3c8c674SKevin Wolf result = self.vm.qmp('query-block') 271d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/file', test_img) 272d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) 273d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file', mid_img) 274d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 2) 275d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) 276d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', mid_img) 277d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/backing-image/filename', backing_img) 278d3c8c674SKevin Wolf 279d3c8c674SKevin Wolf result = self.vm.qmp('query-blockstats') 280d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/node-name', 'top') 281d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/node-name', 'mid') 282d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/backing/node-name', 'base') 283d3c8c674SKevin Wolf 284d3c8c674SKevin Wolf self.cancel_and_wait() 285d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 286d3c8c674SKevin Wolf 2876bf0d1f4SJeff Codyclass TestRelativePaths(ImageCommitTestCase): 2886bf0d1f4SJeff Cody image_len = 1 * 1024 * 1024 2896bf0d1f4SJeff Cody test_len = 1 * 1024 * 256 2906bf0d1f4SJeff Cody 2916bf0d1f4SJeff Cody dir1 = "dir1" 2926bf0d1f4SJeff Cody dir2 = "dir2/" 2936bf0d1f4SJeff Cody dir3 = "dir2/dir3/" 2946bf0d1f4SJeff Cody 2956bf0d1f4SJeff Cody test_img = os.path.join(iotests.test_dir, dir3, 'test.img') 2966bf0d1f4SJeff Cody mid_img = "../mid.img" 2976bf0d1f4SJeff Cody backing_img = "../dir1/backing.img" 2986bf0d1f4SJeff Cody 2996bf0d1f4SJeff Cody backing_img_abs = os.path.join(iotests.test_dir, dir1, 'backing.img') 3006bf0d1f4SJeff Cody mid_img_abs = os.path.join(iotests.test_dir, dir2, 'mid.img') 3016bf0d1f4SJeff Cody 3026bf0d1f4SJeff Cody def setUp(self): 3036bf0d1f4SJeff Cody try: 3046bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir1)) 3056bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir2)) 3066bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir3)) 3076bf0d1f4SJeff Cody except OSError as exception: 3086bf0d1f4SJeff Cody if exception.errno != errno.EEXIST: 3096bf0d1f4SJeff Cody raise 310915365a9SFam Zheng iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len) 311b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 312b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.backing_img_abs, 313b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 314b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 315b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.mid_img_abs, 316b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 317b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.backing_img, 318b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 319b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.mid_img, 320b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 32190c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs) 32290c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs) 3236bf0d1f4SJeff Cody self.vm = iotests.VM().add_drive(self.test_img) 3246bf0d1f4SJeff Cody self.vm.launch() 3256bf0d1f4SJeff Cody 3266bf0d1f4SJeff Cody def tearDown(self): 3276bf0d1f4SJeff Cody self.vm.shutdown() 3286bf0d1f4SJeff Cody os.remove(self.test_img) 3296bf0d1f4SJeff Cody os.remove(self.mid_img_abs) 3306bf0d1f4SJeff Cody os.remove(self.backing_img_abs) 3316bf0d1f4SJeff Cody try: 3326bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir1)) 3336bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir3)) 3346bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir2)) 3356bf0d1f4SJeff Cody except OSError as exception: 3366bf0d1f4SJeff Cody if exception.errno != errno.EEXIST and exception.errno != errno.ENOTEMPTY: 3376bf0d1f4SJeff Cody raise 3386bf0d1f4SJeff Cody 3396bf0d1f4SJeff Cody def test_commit(self): 3404de43470SFam Zheng self.run_commit_test(self.mid_img, self.backing_img) 34190c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) 34290c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) 3436bf0d1f4SJeff Cody 3446bf0d1f4SJeff Cody def test_device_not_found(self): 3456bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img) 3466bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 3476bf0d1f4SJeff Cody 3486bf0d1f4SJeff Cody def test_top_same_base(self): 349fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3506bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img) 3516bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3526bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img) 3536bf0d1f4SJeff Cody 3546bf0d1f4SJeff Cody def test_top_invalid(self): 355fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3566bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img) 3576bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3586bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 3596bf0d1f4SJeff Cody 3606bf0d1f4SJeff Cody def test_base_invalid(self): 361fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3626bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile') 3636bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3646bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found') 3656bf0d1f4SJeff Cody 3666bf0d1f4SJeff Cody def test_top_is_active(self): 3674de43470SFam Zheng self.run_commit_test(self.test_img, self.backing_img) 36890c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed")) 36990c9b167SKevin Wolf self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed")) 3706bf0d1f4SJeff Cody 3716bf0d1f4SJeff Cody def test_top_and_base_reversed(self): 372fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3736bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img) 3746bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3756bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % self.mid_img) 3766bf0d1f4SJeff Cody 377747051cdSJeff Cody 378747051cdSJeff Codyclass TestSetSpeed(ImageCommitTestCase): 379747051cdSJeff Cody image_len = 80 * 1024 * 1024 # MB 380747051cdSJeff Cody 381747051cdSJeff Cody def setUp(self): 382747051cdSJeff Cody qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 383b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 384b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 385b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 386b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 387b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 38890c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img) 38990c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) 39051c493c5SMax Reitz self.vm = iotests.VM().add_drive('blkdebug::' + test_img) 391747051cdSJeff Cody self.vm.launch() 392747051cdSJeff Cody 393747051cdSJeff Cody def tearDown(self): 394747051cdSJeff Cody self.vm.shutdown() 395747051cdSJeff Cody os.remove(test_img) 396747051cdSJeff Cody os.remove(mid_img) 397747051cdSJeff Cody os.remove(backing_img) 398747051cdSJeff Cody 399747051cdSJeff Cody def test_set_speed(self): 400fb0a078fSFam Zheng self.assert_no_active_block_jobs() 401747051cdSJeff Cody 402b59b3d57SFam Zheng self.vm.pause_drive('drive0') 403747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) 404747051cdSJeff Cody self.assert_qmp(result, 'return', {}) 405747051cdSJeff Cody 406747051cdSJeff Cody # Ensure the speed we set was accepted 407747051cdSJeff Cody result = self.vm.qmp('query-block-jobs') 408747051cdSJeff Cody self.assert_qmp(result, 'return[0]/device', 'drive0') 409747051cdSJeff Cody self.assert_qmp(result, 'return[0]/speed', 1024 * 1024) 410747051cdSJeff Cody 411b59b3d57SFam Zheng self.cancel_and_wait(resume=True) 412747051cdSJeff Cody 4138b9a30caSFam Zhengclass TestActiveZeroLengthImage(TestSingleDrive): 4148b9a30caSFam Zheng image_len = 0 415747051cdSJeff Cody 416bcdce5a7SAlberto Garciaclass TestReopenOverlay(ImageCommitTestCase): 417bcdce5a7SAlberto Garcia image_len = 1024 * 1024 418bcdce5a7SAlberto Garcia img0 = os.path.join(iotests.test_dir, '0.img') 419bcdce5a7SAlberto Garcia img1 = os.path.join(iotests.test_dir, '1.img') 420bcdce5a7SAlberto Garcia img2 = os.path.join(iotests.test_dir, '2.img') 421bcdce5a7SAlberto Garcia img3 = os.path.join(iotests.test_dir, '3.img') 422bcdce5a7SAlberto Garcia 423bcdce5a7SAlberto Garcia def setUp(self): 424bcdce5a7SAlberto Garcia iotests.create_image(self.img0, self.image_len) 425b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 426b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img0, '-F', 'raw', self.img1) 427b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 428b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img1, 429b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img2) 430b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 431b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img2, 432b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img3) 433bcdce5a7SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1) 434bcdce5a7SAlberto Garcia self.vm = iotests.VM().add_drive(self.img3) 435bcdce5a7SAlberto Garcia self.vm.launch() 436bcdce5a7SAlberto Garcia 437bcdce5a7SAlberto Garcia def tearDown(self): 438bcdce5a7SAlberto Garcia self.vm.shutdown() 439bcdce5a7SAlberto Garcia os.remove(self.img0) 440bcdce5a7SAlberto Garcia os.remove(self.img1) 441bcdce5a7SAlberto Garcia os.remove(self.img2) 442bcdce5a7SAlberto Garcia os.remove(self.img3) 443bcdce5a7SAlberto Garcia 444bcdce5a7SAlberto Garcia # This tests what happens when the overlay image of the 'top' node 445bcdce5a7SAlberto Garcia # needs to be reopened in read-write mode in order to update the 446bcdce5a7SAlberto Garcia # backing image string. 447bcdce5a7SAlberto Garcia def test_reopen_overlay(self): 448bcdce5a7SAlberto Garcia self.run_commit_test(self.img1, self.img0) 449bcdce5a7SAlberto Garcia 450d4398489SKevin Wolfclass TestErrorHandling(iotests.QMPTestCase): 451d4398489SKevin Wolf image_len = 2 * 1024 * 1024 452d4398489SKevin Wolf 453d4398489SKevin Wolf def setUp(self): 454d4398489SKevin Wolf iotests.create_image(backing_img, self.image_len) 455b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 456b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, 457b66ff2c2SEric Blake '-F', 'raw', mid_img) 458b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 459b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 460b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 461d4398489SKevin Wolf 462d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img) 463d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img) 464d4398489SKevin Wolf 465d4398489SKevin Wolf self.vm = iotests.VM() 466d4398489SKevin Wolf self.vm.launch() 467d4398489SKevin Wolf 468d4398489SKevin Wolf self.blkdebug_file = iotests.file_path("blkdebug.conf") 469d4398489SKevin Wolf 470d4398489SKevin Wolf def tearDown(self): 471d4398489SKevin Wolf self.vm.shutdown() 472d4398489SKevin Wolf os.remove(test_img) 473d4398489SKevin Wolf os.remove(mid_img) 474d4398489SKevin Wolf os.remove(backing_img) 475d4398489SKevin Wolf 476d4398489SKevin Wolf def blockdev_add(self, **kwargs): 477d4398489SKevin Wolf result = self.vm.qmp('blockdev-add', **kwargs) 478d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 479d4398489SKevin Wolf 480d4398489SKevin Wolf def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None): 481d4398489SKevin Wolf self.blockdev_add(node_name='base-file', driver='file', 482d4398489SKevin Wolf filename=backing_img) 483d4398489SKevin Wolf self.blockdev_add(node_name='mid-file', driver='file', 484d4398489SKevin Wolf filename=mid_img) 485d4398489SKevin Wolf self.blockdev_add(node_name='top-file', driver='file', 486d4398489SKevin Wolf filename=test_img) 487d4398489SKevin Wolf 488d4398489SKevin Wolf if base_debug: 489d4398489SKevin Wolf self.blockdev_add(node_name='base-dbg', driver='blkdebug', 490d4398489SKevin Wolf image='base-file', inject_error=base_debug) 491d4398489SKevin Wolf if mid_debug: 492d4398489SKevin Wolf self.blockdev_add(node_name='mid-dbg', driver='blkdebug', 493d4398489SKevin Wolf image='mid-file', inject_error=mid_debug) 494d4398489SKevin Wolf if top_debug: 495d4398489SKevin Wolf self.blockdev_add(node_name='top-dbg', driver='blkdebug', 496d4398489SKevin Wolf image='top-file', inject_error=top_debug) 497d4398489SKevin Wolf 498d4398489SKevin Wolf self.blockdev_add(node_name='base-fmt', driver='raw', 499d4398489SKevin Wolf file=('base-dbg' if base_debug else 'base-file')) 500d4398489SKevin Wolf self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt, 501d4398489SKevin Wolf file=('mid-dbg' if mid_debug else 'mid-file'), 502d4398489SKevin Wolf backing='base-fmt') 503d4398489SKevin Wolf self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt, 504d4398489SKevin Wolf file=('top-dbg' if top_debug else 'top-file'), 505d4398489SKevin Wolf backing='mid-fmt') 506d4398489SKevin Wolf 507d4398489SKevin Wolf def run_job(self, expected_events, error_pauses_job=False): 508d4398489SKevin Wolf match_device = {'data': {'device': 'job0'}} 509d4398489SKevin Wolf events = [ 510d4398489SKevin Wolf ('BLOCK_JOB_COMPLETED', match_device), 511d4398489SKevin Wolf ('BLOCK_JOB_CANCELLED', match_device), 512d4398489SKevin Wolf ('BLOCK_JOB_ERROR', match_device), 513d4398489SKevin Wolf ('BLOCK_JOB_READY', match_device), 514d4398489SKevin Wolf ] 515d4398489SKevin Wolf 516d4398489SKevin Wolf completed = False 517d4398489SKevin Wolf log = [] 518d4398489SKevin Wolf while not completed: 519d4398489SKevin Wolf ev = self.vm.events_wait(events, timeout=5.0) 520d4398489SKevin Wolf if ev['event'] == 'BLOCK_JOB_COMPLETED': 521d4398489SKevin Wolf completed = True 522d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_ERROR': 523d4398489SKevin Wolf if error_pauses_job: 524d4398489SKevin Wolf result = self.vm.qmp('block-job-resume', device='job0') 525d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 526d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_READY': 527d4398489SKevin Wolf result = self.vm.qmp('block-job-complete', device='job0') 528d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 529d4398489SKevin Wolf else: 530d4398489SKevin Wolf self.fail("Unexpected event: %s" % ev) 531d4398489SKevin Wolf log.append(iotests.filter_qmp_event(ev)) 532d4398489SKevin Wolf 533d4398489SKevin Wolf self.maxDiff = None 534d4398489SKevin Wolf self.assertEqual(expected_events, log) 535d4398489SKevin Wolf 536d4398489SKevin Wolf def event_error(self, op, action): 537d4398489SKevin Wolf return { 538d4398489SKevin Wolf 'event': 'BLOCK_JOB_ERROR', 539d4398489SKevin Wolf 'data': {'action': action, 'device': 'job0', 'operation': op}, 540d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'} 541d4398489SKevin Wolf } 542d4398489SKevin Wolf 543d4398489SKevin Wolf def event_ready(self): 544d4398489SKevin Wolf return { 545d4398489SKevin Wolf 'event': 'BLOCK_JOB_READY', 546d4398489SKevin Wolf 'data': {'device': 'job0', 547d4398489SKevin Wolf 'len': 524288, 548d4398489SKevin Wolf 'offset': 524288, 549d4398489SKevin Wolf 'speed': 0, 550d4398489SKevin Wolf 'type': 'commit'}, 551d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 552d4398489SKevin Wolf } 553d4398489SKevin Wolf 554d4398489SKevin Wolf def event_completed(self, errmsg=None, active=True): 555d4398489SKevin Wolf max_len = 524288 if active else self.image_len 556d4398489SKevin Wolf data = { 557d4398489SKevin Wolf 'device': 'job0', 558d4398489SKevin Wolf 'len': max_len, 559d4398489SKevin Wolf 'offset': 0 if errmsg else max_len, 560d4398489SKevin Wolf 'speed': 0, 561d4398489SKevin Wolf 'type': 'commit' 562d4398489SKevin Wolf } 563d4398489SKevin Wolf if errmsg: 564d4398489SKevin Wolf data['error'] = errmsg 565d4398489SKevin Wolf 566d4398489SKevin Wolf return { 567d4398489SKevin Wolf 'event': 'BLOCK_JOB_COMPLETED', 568d4398489SKevin Wolf 'data': data, 569d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 570d4398489SKevin Wolf } 571d4398489SKevin Wolf 572d4398489SKevin Wolf def blkdebug_event(self, event, is_raw=False): 573d4398489SKevin Wolf if event: 574d4398489SKevin Wolf return [{ 575d4398489SKevin Wolf 'event': event, 576d4398489SKevin Wolf 'sector': 512 if is_raw else 1024, 577d4398489SKevin Wolf 'once': True, 578d4398489SKevin Wolf }] 579d4398489SKevin Wolf return None 580d4398489SKevin Wolf 581d4398489SKevin Wolf def prepare_and_start_job(self, on_error, active=True, 582d4398489SKevin Wolf top_event=None, mid_event=None, base_event=None): 583d4398489SKevin Wolf 584d4398489SKevin Wolf top_debug = self.blkdebug_event(top_event) 585d4398489SKevin Wolf mid_debug = self.blkdebug_event(mid_event) 586d4398489SKevin Wolf base_debug = self.blkdebug_event(base_event, True) 587d4398489SKevin Wolf 588d4398489SKevin Wolf self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug, 589d4398489SKevin Wolf base_debug=base_debug) 590d4398489SKevin Wolf 591d4398489SKevin Wolf result = self.vm.qmp('block-commit', job_id='job0', device='top-fmt', 592d4398489SKevin Wolf top_node='top-fmt' if active else 'mid-fmt', 593d4398489SKevin Wolf base_node='mid-fmt' if active else 'base-fmt', 594d4398489SKevin Wolf on_error=on_error) 595d4398489SKevin Wolf self.assert_qmp(result, 'return', {}) 596d4398489SKevin Wolf 597d4398489SKevin Wolf def testActiveReadErrorReport(self): 598d4398489SKevin Wolf self.prepare_and_start_job('report', top_event='read_aio') 599d4398489SKevin Wolf self.run_job([ 600d4398489SKevin Wolf self.event_error('read', 'report'), 601d4398489SKevin Wolf self.event_completed('Input/output error') 602d4398489SKevin Wolf ]) 603d4398489SKevin Wolf 604d4398489SKevin Wolf self.vm.shutdown() 605d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 606d4398489SKevin Wolf 'target image matches source after error') 607d4398489SKevin Wolf 608d4398489SKevin Wolf def testActiveReadErrorStop(self): 609d4398489SKevin Wolf self.prepare_and_start_job('stop', top_event='read_aio') 610d4398489SKevin Wolf self.run_job([ 611d4398489SKevin Wolf self.event_error('read', 'stop'), 612d4398489SKevin Wolf self.event_ready(), 613d4398489SKevin Wolf self.event_completed() 614d4398489SKevin Wolf ], error_pauses_job=True) 615d4398489SKevin Wolf 616d4398489SKevin Wolf self.vm.shutdown() 617d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 618d4398489SKevin Wolf 'target image does not match source after commit') 619d4398489SKevin Wolf 620d4398489SKevin Wolf def testActiveReadErrorIgnore(self): 621d4398489SKevin Wolf self.prepare_and_start_job('ignore', top_event='read_aio') 622d4398489SKevin Wolf self.run_job([ 623d4398489SKevin Wolf self.event_error('read', 'ignore'), 624d4398489SKevin Wolf self.event_ready(), 625d4398489SKevin Wolf self.event_completed() 626d4398489SKevin Wolf ]) 627d4398489SKevin Wolf 628d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 629d4398489SKevin Wolf self.vm.shutdown() 630d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 631d4398489SKevin Wolf 'target image does not match source after commit') 632d4398489SKevin Wolf 633d4398489SKevin Wolf def testActiveWriteErrorReport(self): 634d4398489SKevin Wolf self.prepare_and_start_job('report', mid_event='write_aio') 635d4398489SKevin Wolf self.run_job([ 636d4398489SKevin Wolf self.event_error('write', 'report'), 637d4398489SKevin Wolf self.event_completed('Input/output error') 638d4398489SKevin Wolf ]) 639d4398489SKevin Wolf 640d4398489SKevin Wolf self.vm.shutdown() 641d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 642d4398489SKevin Wolf 'target image matches source after error') 643d4398489SKevin Wolf 644d4398489SKevin Wolf def testActiveWriteErrorStop(self): 645d4398489SKevin Wolf self.prepare_and_start_job('stop', mid_event='write_aio') 646d4398489SKevin Wolf self.run_job([ 647d4398489SKevin Wolf self.event_error('write', 'stop'), 648d4398489SKevin Wolf self.event_ready(), 649d4398489SKevin Wolf self.event_completed() 650d4398489SKevin Wolf ], error_pauses_job=True) 651d4398489SKevin Wolf 652d4398489SKevin Wolf self.vm.shutdown() 653d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 654d4398489SKevin Wolf 'target image does not match source after commit') 655d4398489SKevin Wolf 656d4398489SKevin Wolf def testActiveWriteErrorIgnore(self): 657d4398489SKevin Wolf self.prepare_and_start_job('ignore', mid_event='write_aio') 658d4398489SKevin Wolf self.run_job([ 659d4398489SKevin Wolf self.event_error('write', 'ignore'), 660d4398489SKevin Wolf self.event_ready(), 661d4398489SKevin Wolf self.event_completed() 662d4398489SKevin Wolf ]) 663d4398489SKevin Wolf 664d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 665d4398489SKevin Wolf self.vm.shutdown() 666d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 667d4398489SKevin Wolf 'target image does not match source after commit') 668d4398489SKevin Wolf 669d4398489SKevin Wolf def testIntermediateReadErrorReport(self): 670d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, mid_event='read_aio') 671d4398489SKevin Wolf self.run_job([ 672d4398489SKevin Wolf self.event_error('read', 'report'), 673d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 674d4398489SKevin Wolf ]) 675d4398489SKevin Wolf 676d4398489SKevin Wolf self.vm.shutdown() 677d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 678d4398489SKevin Wolf 'target image matches source after error') 679d4398489SKevin Wolf 680d4398489SKevin Wolf def testIntermediateReadErrorStop(self): 681d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, mid_event='read_aio') 682d4398489SKevin Wolf self.run_job([ 683d4398489SKevin Wolf self.event_error('read', 'stop'), 684d4398489SKevin Wolf self.event_completed(active=False) 685d4398489SKevin Wolf ], error_pauses_job=True) 686d4398489SKevin Wolf 687d4398489SKevin Wolf self.vm.shutdown() 688d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 689d4398489SKevin Wolf 'target image does not match source after commit') 690d4398489SKevin Wolf 691d4398489SKevin Wolf def testIntermediateReadErrorIgnore(self): 692d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, mid_event='read_aio') 693d4398489SKevin Wolf self.run_job([ 694d4398489SKevin Wolf self.event_error('read', 'ignore'), 695d4398489SKevin Wolf self.event_completed(active=False) 696d4398489SKevin Wolf ]) 697d4398489SKevin Wolf 698d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 699d4398489SKevin Wolf self.vm.shutdown() 700d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 701d4398489SKevin Wolf 'target image does not match source after commit') 702d4398489SKevin Wolf 703d4398489SKevin Wolf def testIntermediateWriteErrorReport(self): 704d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, base_event='write_aio') 705d4398489SKevin Wolf self.run_job([ 706d4398489SKevin Wolf self.event_error('write', 'report'), 707d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 708d4398489SKevin Wolf ]) 709d4398489SKevin Wolf 710d4398489SKevin Wolf self.vm.shutdown() 711d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 712d4398489SKevin Wolf 'target image matches source after error') 713d4398489SKevin Wolf 714d4398489SKevin Wolf def testIntermediateWriteErrorStop(self): 715d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, base_event='write_aio') 716d4398489SKevin Wolf self.run_job([ 717d4398489SKevin Wolf self.event_error('write', 'stop'), 718d4398489SKevin Wolf self.event_completed(active=False) 719d4398489SKevin Wolf ], error_pauses_job=True) 720d4398489SKevin Wolf 721d4398489SKevin Wolf self.vm.shutdown() 722d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 723d4398489SKevin Wolf 'target image does not match source after commit') 724d4398489SKevin Wolf 725d4398489SKevin Wolf def testIntermediateWriteErrorIgnore(self): 726d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, base_event='write_aio') 727d4398489SKevin Wolf self.run_job([ 728d4398489SKevin Wolf self.event_error('write', 'ignore'), 729d4398489SKevin Wolf self.event_completed(active=False) 730d4398489SKevin Wolf ]) 731d4398489SKevin Wolf 732d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 733d4398489SKevin Wolf self.vm.shutdown() 734d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 735d4398489SKevin Wolf 'target image does not match source after commit') 736d4398489SKevin Wolf 737ffbba7fdSMax Reitzclass TestCommitWithFilters(iotests.QMPTestCase): 738ffbba7fdSMax Reitz img0 = os.path.join(iotests.test_dir, '0.img') 739ffbba7fdSMax Reitz img1 = os.path.join(iotests.test_dir, '1.img') 740ffbba7fdSMax Reitz img2 = os.path.join(iotests.test_dir, '2.img') 741ffbba7fdSMax Reitz img3 = os.path.join(iotests.test_dir, '3.img') 742ffbba7fdSMax Reitz 743ffbba7fdSMax Reitz def do_test_io(self, read_or_write): 744ffbba7fdSMax Reitz for index, pattern_file in enumerate(self.pattern_files): 745ffbba7fdSMax Reitz result = qemu_io('-f', iotests.imgfmt, 746ffbba7fdSMax Reitz '-c', 747ffbba7fdSMax Reitz f'{read_or_write} -P {index + 1} {index}M 1M', 748ffbba7fdSMax Reitz pattern_file) 749ffbba7fdSMax Reitz self.assertFalse('Pattern verification failed' in result) 750ffbba7fdSMax Reitz 751ffbba7fdSMax Reitz def setUp(self): 752ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M') 753ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M') 754ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img2, '64M') 755ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img3, '64M') 756ffbba7fdSMax Reitz 757ffbba7fdSMax Reitz # Distributions of the patterns in the files; this is checked 758ffbba7fdSMax Reitz # by tearDown() and should be changed by the test cases as is 759ffbba7fdSMax Reitz # necessary 760ffbba7fdSMax Reitz self.pattern_files = [self.img0, self.img1, self.img2, self.img3] 761ffbba7fdSMax Reitz 762ffbba7fdSMax Reitz self.do_test_io('write') 763ffbba7fdSMax Reitz 764ffbba7fdSMax Reitz self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi') 765ffbba7fdSMax Reitz self.vm.launch() 766ffbba7fdSMax Reitz 767ffbba7fdSMax Reitz result = self.vm.qmp('object-add', qom_type='throttle-group', id='tg') 768ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 769ffbba7fdSMax Reitz 770ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-add', **{ 771ffbba7fdSMax Reitz 'node-name': 'top-filter', 772ffbba7fdSMax Reitz 'driver': 'throttle', 773ffbba7fdSMax Reitz 'throttle-group': 'tg', 774ffbba7fdSMax Reitz 'file': { 775ffbba7fdSMax Reitz 'node-name': 'cow-3', 776ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 777ffbba7fdSMax Reitz 'file': { 778ffbba7fdSMax Reitz 'driver': 'file', 779ffbba7fdSMax Reitz 'filename': self.img3 780ffbba7fdSMax Reitz }, 781ffbba7fdSMax Reitz 'backing': { 782ffbba7fdSMax Reitz 'node-name': 'cow-2', 783ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 784ffbba7fdSMax Reitz 'file': { 785ffbba7fdSMax Reitz 'driver': 'file', 786ffbba7fdSMax Reitz 'filename': self.img2 787ffbba7fdSMax Reitz }, 788ffbba7fdSMax Reitz 'backing': { 789ffbba7fdSMax Reitz 'node-name': 'cow-1', 790ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 791ffbba7fdSMax Reitz 'file': { 792ffbba7fdSMax Reitz 'driver': 'file', 793ffbba7fdSMax Reitz 'filename': self.img1 794ffbba7fdSMax Reitz }, 795ffbba7fdSMax Reitz 'backing': { 796ffbba7fdSMax Reitz 'node-name': 'bottom-filter', 797ffbba7fdSMax Reitz 'driver': 'throttle', 798ffbba7fdSMax Reitz 'throttle-group': 'tg', 799ffbba7fdSMax Reitz 'file': { 800ffbba7fdSMax Reitz 'node-name': 'cow-0', 801ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 802ffbba7fdSMax Reitz 'file': { 803ffbba7fdSMax Reitz 'driver': 'file', 804ffbba7fdSMax Reitz 'filename': self.img0 805ffbba7fdSMax Reitz } 806ffbba7fdSMax Reitz } 807ffbba7fdSMax Reitz } 808ffbba7fdSMax Reitz } 809ffbba7fdSMax Reitz } 810ffbba7fdSMax Reitz } 811ffbba7fdSMax Reitz }) 812ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 813ffbba7fdSMax Reitz 814ffbba7fdSMax Reitz def tearDown(self): 815ffbba7fdSMax Reitz self.vm.shutdown() 816ffbba7fdSMax Reitz self.do_test_io('read') 817ffbba7fdSMax Reitz 818ffbba7fdSMax Reitz os.remove(self.img3) 819ffbba7fdSMax Reitz os.remove(self.img2) 820ffbba7fdSMax Reitz os.remove(self.img1) 821ffbba7fdSMax Reitz os.remove(self.img0) 822ffbba7fdSMax Reitz 823ffbba7fdSMax Reitz # Filters make for funny filenames, so we cannot just use 824ffbba7fdSMax Reitz # self.imgX to get them 825ffbba7fdSMax Reitz def get_filename(self, node): 826ffbba7fdSMax Reitz return self.vm.node_info(node)['image']['filename'] 827ffbba7fdSMax Reitz 828ffbba7fdSMax Reitz def test_filterless_commit(self): 829ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 830ffbba7fdSMax Reitz job_id='commit', 831ffbba7fdSMax Reitz device='top-filter', 832ffbba7fdSMax Reitz top_node='cow-2', 833ffbba7fdSMax Reitz base_node='cow-1') 834ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 835ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 836ffbba7fdSMax Reitz 837ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-3')) 838ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-2')) 839ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-1')) 840ffbba7fdSMax Reitz 841ffbba7fdSMax Reitz # 2 has been comitted into 1 842ffbba7fdSMax Reitz self.pattern_files[2] = self.img1 843ffbba7fdSMax Reitz 844ffbba7fdSMax Reitz def test_commit_through_filter(self): 845ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 846ffbba7fdSMax Reitz job_id='commit', 847ffbba7fdSMax Reitz device='top-filter', 848ffbba7fdSMax Reitz top_node='cow-1', 849ffbba7fdSMax Reitz base_node='cow-0') 850ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 851ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 852ffbba7fdSMax Reitz 853ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 854ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-1')) 855ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('bottom-filter')) 856ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-0')) 857ffbba7fdSMax Reitz 858ffbba7fdSMax Reitz # 1 has been comitted into 0 859ffbba7fdSMax Reitz self.pattern_files[1] = self.img0 860ffbba7fdSMax Reitz 861ffbba7fdSMax Reitz def test_filtered_active_commit_with_filter(self): 862ffbba7fdSMax Reitz # Add a device, so the commit job finds a parent it can change 863ffbba7fdSMax Reitz # to point to the base node (so we can test that top-filter is 864ffbba7fdSMax Reitz # dropped from the graph) 865ffbba7fdSMax Reitz result = self.vm.qmp('device_add', id='drv0', driver='scsi-hd', 866ffbba7fdSMax Reitz bus='vio-scsi.0', drive='top-filter') 867ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 868ffbba7fdSMax Reitz 869ffbba7fdSMax Reitz # Try to release our reference to top-filter; that should not 870ffbba7fdSMax Reitz # work because drv0 uses it 871ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 872ffbba7fdSMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 873ffbba7fdSMax Reitz self.assert_qmp(result, 'error/desc', 'Node top-filter is in use') 874ffbba7fdSMax Reitz 875ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 876ffbba7fdSMax Reitz job_id='commit', 877ffbba7fdSMax Reitz device='top-filter', 878ffbba7fdSMax Reitz base_node='cow-2') 879ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 880ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 881ffbba7fdSMax Reitz 882ffbba7fdSMax Reitz # Try to release our reference to top-filter again 883ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 884ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 885ffbba7fdSMax Reitz 886ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('top-filter')) 887ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 888ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 889ffbba7fdSMax Reitz 890ffbba7fdSMax Reitz # Check that drv0 is now connected to cow-2 891ffbba7fdSMax Reitz blockdevs = self.vm.qmp('query-block')['return'] 892ffbba7fdSMax Reitz drv0 = next(dev for dev in blockdevs if dev['qdev'] == 'drv0') 893ffbba7fdSMax Reitz self.assertEqual(drv0['inserted']['node-name'], 'cow-2') 894ffbba7fdSMax Reitz 895ffbba7fdSMax Reitz # 3 has been comitted into 2 896ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 897ffbba7fdSMax Reitz 898ffbba7fdSMax Reitz def test_filtered_active_commit_without_filter(self): 899ffbba7fdSMax Reitz result = self.vm.qmp('block-commit', 900ffbba7fdSMax Reitz job_id='commit', 901ffbba7fdSMax Reitz device='top-filter', 902ffbba7fdSMax Reitz top_node='cow-3', 903ffbba7fdSMax Reitz base_node='cow-2') 904ffbba7fdSMax Reitz self.assert_qmp(result, 'return', {}) 905ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 906ffbba7fdSMax Reitz 907ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('top-filter')) 908ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 909ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 910ffbba7fdSMax Reitz 911ffbba7fdSMax Reitz # 3 has been comitted into 2 912ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 913ffbba7fdSMax Reitz 914*374eedd1SMax Reitzclass TestCommitWithOverriddenBacking(iotests.QMPTestCase): 915*374eedd1SMax Reitz img_base_a = os.path.join(iotests.test_dir, 'base_a.img') 916*374eedd1SMax Reitz img_base_b = os.path.join(iotests.test_dir, 'base_b.img') 917*374eedd1SMax Reitz img_top = os.path.join(iotests.test_dir, 'top.img') 918*374eedd1SMax Reitz 919*374eedd1SMax Reitz def setUp(self): 920*374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_a, '1M') 921*374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_b, '1M') 922*374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, \ 923*374eedd1SMax Reitz self.img_top) 924*374eedd1SMax Reitz 925*374eedd1SMax Reitz self.vm = iotests.VM() 926*374eedd1SMax Reitz self.vm.launch() 927*374eedd1SMax Reitz 928*374eedd1SMax Reitz # Use base_b instead of base_a as the backing of top 929*374eedd1SMax Reitz result = self.vm.qmp('blockdev-add', **{ 930*374eedd1SMax Reitz 'node-name': 'top', 931*374eedd1SMax Reitz 'driver': iotests.imgfmt, 932*374eedd1SMax Reitz 'file': { 933*374eedd1SMax Reitz 'driver': 'file', 934*374eedd1SMax Reitz 'filename': self.img_top 935*374eedd1SMax Reitz }, 936*374eedd1SMax Reitz 'backing': { 937*374eedd1SMax Reitz 'node-name': 'base', 938*374eedd1SMax Reitz 'driver': iotests.imgfmt, 939*374eedd1SMax Reitz 'file': { 940*374eedd1SMax Reitz 'driver': 'file', 941*374eedd1SMax Reitz 'filename': self.img_base_b 942*374eedd1SMax Reitz } 943*374eedd1SMax Reitz } 944*374eedd1SMax Reitz }) 945*374eedd1SMax Reitz self.assert_qmp(result, 'return', {}) 946*374eedd1SMax Reitz 947*374eedd1SMax Reitz def tearDown(self): 948*374eedd1SMax Reitz self.vm.shutdown() 949*374eedd1SMax Reitz os.remove(self.img_top) 950*374eedd1SMax Reitz os.remove(self.img_base_a) 951*374eedd1SMax Reitz os.remove(self.img_base_b) 952*374eedd1SMax Reitz 953*374eedd1SMax Reitz def test_commit_to_a(self): 954*374eedd1SMax Reitz # Try committing to base_a (which should fail, as top's 955*374eedd1SMax Reitz # backing image is base_b instead) 956*374eedd1SMax Reitz result = self.vm.qmp('block-commit', 957*374eedd1SMax Reitz job_id='commit', 958*374eedd1SMax Reitz device='top', 959*374eedd1SMax Reitz base=self.img_base_a) 960*374eedd1SMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 961*374eedd1SMax Reitz 962*374eedd1SMax Reitz def test_commit_to_b(self): 963*374eedd1SMax Reitz # Try committing to base_b (which should work, since that is 964*374eedd1SMax Reitz # actually top's backing image) 965*374eedd1SMax Reitz result = self.vm.qmp('block-commit', 966*374eedd1SMax Reitz job_id='commit', 967*374eedd1SMax Reitz device='top', 968*374eedd1SMax Reitz base=self.img_base_b) 969*374eedd1SMax Reitz self.assert_qmp(result, 'return', {}) 970*374eedd1SMax Reitz 971*374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_READY') 972*374eedd1SMax Reitz self.vm.qmp('block-job-complete', device='commit') 973*374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_COMPLETED') 974*374eedd1SMax Reitz 975747051cdSJeff Codyif __name__ == '__main__': 976103cbc77SMax Reitz iotests.main(supported_fmts=['qcow2', 'qed'], 977103cbc77SMax Reitz supported_protocols=['file']) 978