1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: rw auto 3747051cdSJeff Cody# 4747051cdSJeff Cody# Tests for image block commit. 5747051cdSJeff Cody# 6747051cdSJeff Cody# Copyright (C) 2012 IBM, Corp. 7747051cdSJeff Cody# Copyright (C) 2012 Red Hat, Inc. 8747051cdSJeff Cody# 9747051cdSJeff Cody# This program is free software; you can redistribute it and/or modify 10747051cdSJeff Cody# it under the terms of the GNU General Public License as published by 11747051cdSJeff Cody# the Free Software Foundation; either version 2 of the License, or 12747051cdSJeff Cody# (at your option) any later version. 13747051cdSJeff Cody# 14747051cdSJeff Cody# This program is distributed in the hope that it will be useful, 15747051cdSJeff Cody# but WITHOUT ANY WARRANTY; without even the implied warranty of 16747051cdSJeff Cody# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17747051cdSJeff Cody# GNU General Public License for more details. 18747051cdSJeff Cody# 19747051cdSJeff Cody# You should have received a copy of the GNU General Public License 20747051cdSJeff Cody# along with this program. If not, see <http://www.gnu.org/licenses/>. 21747051cdSJeff Cody# 22747051cdSJeff Cody# Test for live block commit 23747051cdSJeff Cody# Derived from Image Streaming Test 030 24747051cdSJeff Cody 25747051cdSJeff Codyimport time 26747051cdSJeff Codyimport os 27747051cdSJeff Codyimport iotests 28747051cdSJeff Codyfrom iotests import qemu_img, qemu_io 29747051cdSJeff Codyimport struct 306bf0d1f4SJeff Codyimport errno 31747051cdSJeff Cody 32747051cdSJeff Codybacking_img = os.path.join(iotests.test_dir, 'backing.img') 33747051cdSJeff Codymid_img = os.path.join(iotests.test_dir, 'mid.img') 34747051cdSJeff Codytest_img = os.path.join(iotests.test_dir, 'test.img') 35747051cdSJeff Cody 36747051cdSJeff Codyclass ImageCommitTestCase(iotests.QMPTestCase): 37747051cdSJeff Cody '''Abstract base class for image commit test cases''' 38747051cdSJeff Cody 397676e2c5SJeff Cody def wait_for_complete(self, need_ready=False): 404de43470SFam Zheng completed = False 418b9a30caSFam Zheng ready = False 424de43470SFam Zheng while not completed: 434de43470SFam Zheng for event in self.vm.get_qmp_events(wait=True): 444de43470SFam Zheng if event['event'] == 'BLOCK_JOB_COMPLETED': 45bcdce5a7SAlberto Garcia self.assert_qmp_absent(event, 'data/error') 464de43470SFam Zheng self.assert_qmp(event, 'data/type', 'commit') 474de43470SFam Zheng self.assert_qmp(event, 'data/device', 'drive0') 481d3ba15aSMax Reitz self.assert_qmp(event, 'data/offset', event['data']['len']) 498b9a30caSFam Zheng if need_ready: 508b9a30caSFam Zheng self.assertTrue(ready, "Expecting BLOCK_JOB_COMPLETED event") 514de43470SFam Zheng completed = True 524de43470SFam Zheng elif event['event'] == 'BLOCK_JOB_READY': 538b9a30caSFam Zheng ready = True 544de43470SFam Zheng self.assert_qmp(event, 'data/type', 'commit') 554de43470SFam Zheng self.assert_qmp(event, 'data/device', 'drive0') 564de43470SFam Zheng self.vm.qmp('block-job-complete', device='drive0') 574de43470SFam Zheng 58fb0a078fSFam Zheng self.assert_no_active_block_jobs() 594de43470SFam Zheng self.vm.shutdown() 604de43470SFam Zheng 61d57177a4SKevin Wolf def run_commit_test(self, top, base, need_ready=False, node_names=False): 627676e2c5SJeff Cody self.assert_no_active_block_jobs() 63d57177a4SKevin Wolf if node_names: 64*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0', top_node=top, base_node=base) 65d57177a4SKevin Wolf else: 66*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0', top=top, base=base) 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() 71*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0') 727676e2c5SJeff Cody self.wait_for_complete() 737676e2c5SJeff Cody 74747051cdSJeff Codyclass TestSingleDrive(ImageCommitTestCase): 75c3971b88SKevin Wolf # Need some space after the copied data so that throttling is effective in 76c3971b88SKevin Wolf # tests that use it rather than just completing the job immediately 77c3971b88SKevin Wolf image_len = 2 * 1024 * 1024 78747051cdSJeff Cody test_len = 1 * 1024 * 256 79747051cdSJeff Cody 80747051cdSJeff Cody def setUp(self): 818b9a30caSFam Zheng iotests.create_image(backing_img, self.image_len) 82b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 83b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 84b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 85b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 86b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 87aaa0c0efSJohn Snow if self.image_len: 8890c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img) 89aaa0c0efSJohn Snow qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', 90aaa0c0efSJohn Snow mid_img) 91d3c8c674SKevin Wolf self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none") 9222329f0dSLaurent Vivier self.vm.add_device('virtio-scsi') 93c3971b88SKevin Wolf self.vm.add_device("scsi-hd,id=scsi0,drive=drive0") 94747051cdSJeff Cody self.vm.launch() 95747051cdSJeff Cody 96747051cdSJeff Cody def tearDown(self): 97b9420e4fSJohn Snow self.vm.shutdown() 98747051cdSJeff Cody os.remove(test_img) 99747051cdSJeff Cody os.remove(mid_img) 100747051cdSJeff Cody os.remove(backing_img) 101747051cdSJeff Cody 102747051cdSJeff Cody def test_commit(self): 1034de43470SFam Zheng self.run_commit_test(mid_img, backing_img) 104aaa0c0efSJohn Snow if not self.image_len: 105aaa0c0efSJohn Snow return 106e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 107e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 108747051cdSJeff Cody 109d57177a4SKevin Wolf def test_commit_node(self): 110d57177a4SKevin Wolf self.run_commit_test("mid", "base", node_names=True) 111aaa0c0efSJohn Snow if not self.image_len: 112aaa0c0efSJohn Snow return 113e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 114e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 115d57177a4SKevin Wolf 1169442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 11786472071SMax Reitz def test_commit_with_filter_and_quit(self): 118*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('object-add', qom_type='throttle-group', id='tg') 11986472071SMax Reitz 12086472071SMax Reitz # Add a filter outside of the backing chain 121*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 12286472071SMax Reitz 123*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0') 12486472071SMax Reitz 12586472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 12686472071SMax Reitz # block job and a bdrv_drain_all() 127*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('quit') 12886472071SMax Reitz 12986472071SMax Reitz # Same as above, but this time we add the filter after starting the job 1309442bebeSThomas Huth @iotests.skip_if_unsupported(['throttle']) 13186472071SMax Reitz def test_commit_plus_filter_and_quit(self): 132*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('object-add', qom_type='throttle-group', id='tg') 13386472071SMax Reitz 134*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0') 13586472071SMax Reitz 13686472071SMax Reitz # Add a filter outside of the backing chain 137*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', driver='throttle', node_name='filter', throttle_group='tg', file='mid') 13886472071SMax Reitz 13986472071SMax Reitz # Quit immediately, thus forcing a simultaneous cancel of the 14086472071SMax Reitz # block job and a bdrv_drain_all() 141*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('quit') 14286472071SMax Reitz 143747051cdSJeff Cody def test_device_not_found(self): 144747051cdSJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) 145747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 146747051cdSJeff Cody 147747051cdSJeff Cody def test_top_same_base(self): 148fb0a078fSFam Zheng self.assert_no_active_block_jobs() 149747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img) 150747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 151b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % backing_img) 152747051cdSJeff Cody 153747051cdSJeff Cody def test_top_invalid(self): 154fb0a078fSFam Zheng self.assert_no_active_block_jobs() 155747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img) 156747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 157747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 158747051cdSJeff Cody 159747051cdSJeff Cody def test_base_invalid(self): 160fb0a078fSFam Zheng self.assert_no_active_block_jobs() 161747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile') 162747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 163b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find 'badfile' in the backing chain") 164747051cdSJeff Cody 165d57177a4SKevin Wolf def test_top_node_invalid(self): 166d57177a4SKevin Wolf self.assert_no_active_block_jobs() 167d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base') 168d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 169785ec4b1SConnor Kuehl self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'") 170d57177a4SKevin Wolf 171d57177a4SKevin Wolf def test_base_node_invalid(self): 172d57177a4SKevin Wolf self.assert_no_active_block_jobs() 173d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile') 174d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 175785ec4b1SConnor Kuehl self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'") 176d57177a4SKevin Wolf 177d57177a4SKevin Wolf def test_top_path_and_node(self): 178d57177a4SKevin Wolf self.assert_no_active_block_jobs() 179d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', top='%s' % mid_img) 180d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 181d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top-node' and 'top' are mutually exclusive") 182d57177a4SKevin Wolf 183d57177a4SKevin Wolf def test_base_path_and_node(self): 184d57177a4SKevin Wolf self.assert_no_active_block_jobs() 185d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', base='%s' % backing_img) 186d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 187d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'base-node' and 'base' are mutually exclusive") 188d57177a4SKevin Wolf 189747051cdSJeff Cody def test_top_is_active(self): 1908b9a30caSFam Zheng self.run_commit_test(test_img, backing_img, need_ready=True) 191aaa0c0efSJohn Snow if not self.image_len: 192aaa0c0efSJohn Snow return 193e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 194e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 195747051cdSJeff Cody 1967676e2c5SJeff Cody def test_top_is_default_active(self): 1977676e2c5SJeff Cody self.run_default_commit_test() 198aaa0c0efSJohn Snow if not self.image_len: 199aaa0c0efSJohn Snow return 200e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img) 201e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img) 2027676e2c5SJeff Cody 203747051cdSJeff Cody def test_top_and_base_reversed(self): 204fb0a078fSFam Zheng self.assert_no_active_block_jobs() 205747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img) 206747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 207b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % mid_img) 208747051cdSJeff Cody 209d57177a4SKevin Wolf def test_top_and_base_node_reversed(self): 210d57177a4SKevin Wolf self.assert_no_active_block_jobs() 211d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='base', base_node='top') 212d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 213d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'top' is not in this backing file chain") 214d57177a4SKevin Wolf 215d57177a4SKevin Wolf def test_top_node_in_wrong_chain(self): 216d57177a4SKevin Wolf self.assert_no_active_block_jobs() 217d57177a4SKevin Wolf 218*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', driver='null-co', node_name='null') 219d57177a4SKevin Wolf 220d57177a4SKevin Wolf result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') 221d57177a4SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 222d57177a4SKevin Wolf self.assert_qmp(result, 'error/desc', "'null' is not in this backing file chain") 223d57177a4SKevin Wolf 224c3971b88SKevin Wolf # When the job is running on a BB that is automatically deleted on hot 225c3971b88SKevin Wolf # unplug, the job is cancelled when the device disappears 226c3971b88SKevin Wolf def test_hot_unplug(self): 227c3971b88SKevin Wolf if self.image_len == 0: 228c3971b88SKevin Wolf return 229c3971b88SKevin Wolf 230c3971b88SKevin Wolf self.assert_no_active_block_jobs() 231*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0', top=mid_img, 2329a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 233*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('device_del', id='scsi0') 234c3971b88SKevin Wolf 235c3971b88SKevin Wolf cancelled = False 236c3971b88SKevin Wolf deleted = False 237c3971b88SKevin Wolf while not cancelled or not deleted: 238c3971b88SKevin Wolf for event in self.vm.get_qmp_events(wait=True): 239c3971b88SKevin Wolf if event['event'] == 'DEVICE_DELETED': 240c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'scsi0') 241c3971b88SKevin Wolf deleted = True 242c3971b88SKevin Wolf elif event['event'] == 'BLOCK_JOB_CANCELLED': 243c3971b88SKevin Wolf self.assert_qmp(event, 'data/device', 'drive0') 244c3971b88SKevin Wolf cancelled = True 2451dac83f1SKevin Wolf elif event['event'] == 'JOB_STATUS_CHANGE': 2461dac83f1SKevin Wolf self.assert_qmp(event, 'data/id', 'drive0') 247c3971b88SKevin Wolf else: 248c3971b88SKevin Wolf self.fail("Unexpected event %s" % (event['event'])) 249c3971b88SKevin Wolf 250c3971b88SKevin Wolf self.assert_no_active_block_jobs() 251747051cdSJeff Cody 252d3c8c674SKevin Wolf # Tests that the insertion of the commit_top filter node doesn't make a 253d3c8c674SKevin Wolf # difference to query-blockstat 254d3c8c674SKevin Wolf def test_implicit_node(self): 255d3c8c674SKevin Wolf if self.image_len == 0: 256d3c8c674SKevin Wolf return 257d3c8c674SKevin Wolf 258d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 259*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0', top=mid_img, 2609a3a9a63SMax Reitz base=backing_img, speed=(self.image_len // 4)) 261d3c8c674SKevin Wolf 262d3c8c674SKevin Wolf result = self.vm.qmp('query-block') 263d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/file', test_img) 264d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) 265d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file', mid_img) 266d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 2) 267d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) 268d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', mid_img) 269d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/inserted/image/backing-image/backing-image/filename', backing_img) 270d3c8c674SKevin Wolf 271d3c8c674SKevin Wolf result = self.vm.qmp('query-blockstats') 272d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/node-name', 'top') 273d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/node-name', 'mid') 274d3c8c674SKevin Wolf self.assert_qmp(result, 'return[0]/backing/backing/node-name', 'base') 275d3c8c674SKevin Wolf 276d3c8c674SKevin Wolf self.cancel_and_wait() 277d3c8c674SKevin Wolf self.assert_no_active_block_jobs() 278d3c8c674SKevin Wolf 2796bf0d1f4SJeff Codyclass TestRelativePaths(ImageCommitTestCase): 2806bf0d1f4SJeff Cody image_len = 1 * 1024 * 1024 2816bf0d1f4SJeff Cody test_len = 1 * 1024 * 256 2826bf0d1f4SJeff Cody 2836bf0d1f4SJeff Cody dir1 = "dir1" 2846bf0d1f4SJeff Cody dir2 = "dir2/" 2856bf0d1f4SJeff Cody dir3 = "dir2/dir3/" 2866bf0d1f4SJeff Cody 2876bf0d1f4SJeff Cody test_img = os.path.join(iotests.test_dir, dir3, 'test.img') 2886bf0d1f4SJeff Cody mid_img = "../mid.img" 2896bf0d1f4SJeff Cody backing_img = "../dir1/backing.img" 2906bf0d1f4SJeff Cody 2916bf0d1f4SJeff Cody backing_img_abs = os.path.join(iotests.test_dir, dir1, 'backing.img') 2926bf0d1f4SJeff Cody mid_img_abs = os.path.join(iotests.test_dir, dir2, 'mid.img') 2936bf0d1f4SJeff Cody 2946bf0d1f4SJeff Cody def setUp(self): 2956bf0d1f4SJeff Cody try: 2966bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir1)) 2976bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir2)) 2986bf0d1f4SJeff Cody os.mkdir(os.path.join(iotests.test_dir, self.dir3)) 2996bf0d1f4SJeff Cody except OSError as exception: 3006bf0d1f4SJeff Cody if exception.errno != errno.EEXIST: 3016bf0d1f4SJeff Cody raise 302915365a9SFam Zheng iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len) 303b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 304b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.backing_img_abs, 305b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 306b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 307b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.mid_img_abs, 308b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 309b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.backing_img, 310b66ff2c2SEric Blake '-F', 'raw', self.mid_img_abs) 311b66ff2c2SEric Blake qemu_img('rebase', '-u', '-b', self.mid_img, 312b66ff2c2SEric Blake '-F', iotests.imgfmt, self.test_img) 31390c9b167SKevin Wolf qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs) 31490c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs) 3156bf0d1f4SJeff Cody self.vm = iotests.VM().add_drive(self.test_img) 3166bf0d1f4SJeff Cody self.vm.launch() 3176bf0d1f4SJeff Cody 3186bf0d1f4SJeff Cody def tearDown(self): 3196bf0d1f4SJeff Cody self.vm.shutdown() 3206bf0d1f4SJeff Cody os.remove(self.test_img) 3216bf0d1f4SJeff Cody os.remove(self.mid_img_abs) 3226bf0d1f4SJeff Cody os.remove(self.backing_img_abs) 3236bf0d1f4SJeff Cody try: 3246bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir1)) 3256bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir3)) 3266bf0d1f4SJeff Cody os.rmdir(os.path.join(iotests.test_dir, self.dir2)) 3276bf0d1f4SJeff Cody except OSError as exception: 3286bf0d1f4SJeff Cody if exception.errno != errno.EEXIST and exception.errno != errno.ENOTEMPTY: 3296bf0d1f4SJeff Cody raise 3306bf0d1f4SJeff Cody 3316bf0d1f4SJeff Cody def test_commit(self): 3324de43470SFam Zheng self.run_commit_test(self.mid_img, self.backing_img) 333e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs) 334e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs) 3356bf0d1f4SJeff Cody 3366bf0d1f4SJeff Cody def test_device_not_found(self): 3376bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img) 3386bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 3396bf0d1f4SJeff Cody 3406bf0d1f4SJeff Cody def test_top_same_base(self): 341fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3426bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='%s' % self.mid_img) 3436bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 344b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % self.mid_img) 3456bf0d1f4SJeff Cody 3466bf0d1f4SJeff Cody def test_top_invalid(self): 347fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3486bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % self.backing_img) 3496bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 3506bf0d1f4SJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 3516bf0d1f4SJeff Cody 3526bf0d1f4SJeff Cody def test_base_invalid(self): 353fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3546bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img, base='badfile') 3556bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 356b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find 'badfile' in the backing chain") 3576bf0d1f4SJeff Cody 3586bf0d1f4SJeff Cody def test_top_is_active(self): 3594de43470SFam Zheng self.run_commit_test(self.test_img, self.backing_img) 360e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs) 361e9039c04SJohn Snow qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs) 3626bf0d1f4SJeff Cody 3636bf0d1f4SJeff Cody def test_top_and_base_reversed(self): 364fb0a078fSFam Zheng self.assert_no_active_block_jobs() 3656bf0d1f4SJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.backing_img, base='%s' % self.mid_img) 3666bf0d1f4SJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 367b0d7be2aSMarkus Armbruster self.assert_qmp(result, 'error/desc', "Can't find '%s' in the backing chain" % self.mid_img) 3686bf0d1f4SJeff Cody 369747051cdSJeff Cody 370747051cdSJeff Codyclass TestSetSpeed(ImageCommitTestCase): 371747051cdSJeff Cody image_len = 80 * 1024 * 1024 # MB 372747051cdSJeff Cody 373747051cdSJeff Cody def setUp(self): 374747051cdSJeff Cody qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 375b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 376b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img) 377b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 378b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 379b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 38090c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img) 38190c9b167SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img) 38251c493c5SMax Reitz self.vm = iotests.VM().add_drive('blkdebug::' + test_img) 383747051cdSJeff Cody self.vm.launch() 384747051cdSJeff Cody 385747051cdSJeff Cody def tearDown(self): 386747051cdSJeff Cody self.vm.shutdown() 387747051cdSJeff Cody os.remove(test_img) 388747051cdSJeff Cody os.remove(mid_img) 389747051cdSJeff Cody os.remove(backing_img) 390747051cdSJeff Cody 391747051cdSJeff Cody def test_set_speed(self): 392fb0a078fSFam Zheng self.assert_no_active_block_jobs() 393747051cdSJeff Cody 394b59b3d57SFam Zheng self.vm.pause_drive('drive0') 395*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) 396747051cdSJeff Cody 397747051cdSJeff Cody # Ensure the speed we set was accepted 398747051cdSJeff Cody result = self.vm.qmp('query-block-jobs') 399747051cdSJeff Cody self.assert_qmp(result, 'return[0]/device', 'drive0') 400747051cdSJeff Cody self.assert_qmp(result, 'return[0]/speed', 1024 * 1024) 401747051cdSJeff Cody 402b59b3d57SFam Zheng self.cancel_and_wait(resume=True) 403747051cdSJeff Cody 4048b9a30caSFam Zhengclass TestActiveZeroLengthImage(TestSingleDrive): 4058b9a30caSFam Zheng image_len = 0 406747051cdSJeff Cody 407bcdce5a7SAlberto Garciaclass TestReopenOverlay(ImageCommitTestCase): 408bcdce5a7SAlberto Garcia image_len = 1024 * 1024 409bcdce5a7SAlberto Garcia img0 = os.path.join(iotests.test_dir, '0.img') 410bcdce5a7SAlberto Garcia img1 = os.path.join(iotests.test_dir, '1.img') 411bcdce5a7SAlberto Garcia img2 = os.path.join(iotests.test_dir, '2.img') 412bcdce5a7SAlberto Garcia img3 = os.path.join(iotests.test_dir, '3.img') 413bcdce5a7SAlberto Garcia 414bcdce5a7SAlberto Garcia def setUp(self): 415bcdce5a7SAlberto Garcia iotests.create_image(self.img0, self.image_len) 416b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 417b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img0, '-F', 'raw', self.img1) 418b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 419b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img1, 420b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img2) 421b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 422b66ff2c2SEric Blake '-o', 'backing_file=%s' % self.img2, 423b66ff2c2SEric Blake '-F', iotests.imgfmt, self.img3) 424bcdce5a7SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1) 425bcdce5a7SAlberto Garcia self.vm = iotests.VM().add_drive(self.img3) 426bcdce5a7SAlberto Garcia self.vm.launch() 427bcdce5a7SAlberto Garcia 428bcdce5a7SAlberto Garcia def tearDown(self): 429bcdce5a7SAlberto Garcia self.vm.shutdown() 430bcdce5a7SAlberto Garcia os.remove(self.img0) 431bcdce5a7SAlberto Garcia os.remove(self.img1) 432bcdce5a7SAlberto Garcia os.remove(self.img2) 433bcdce5a7SAlberto Garcia os.remove(self.img3) 434bcdce5a7SAlberto Garcia 435bcdce5a7SAlberto Garcia # This tests what happens when the overlay image of the 'top' node 436bcdce5a7SAlberto Garcia # needs to be reopened in read-write mode in order to update the 437bcdce5a7SAlberto Garcia # backing image string. 438bcdce5a7SAlberto Garcia def test_reopen_overlay(self): 439bcdce5a7SAlberto Garcia self.run_commit_test(self.img1, self.img0) 440bcdce5a7SAlberto Garcia 441d4398489SKevin Wolfclass TestErrorHandling(iotests.QMPTestCase): 442d4398489SKevin Wolf image_len = 2 * 1024 * 1024 443d4398489SKevin Wolf 444d4398489SKevin Wolf def setUp(self): 445d4398489SKevin Wolf iotests.create_image(backing_img, self.image_len) 446b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 447b66ff2c2SEric Blake '-o', 'backing_file=%s' % backing_img, 448b66ff2c2SEric Blake '-F', 'raw', mid_img) 449b66ff2c2SEric Blake qemu_img('create', '-f', iotests.imgfmt, 450b66ff2c2SEric Blake '-o', 'backing_file=%s' % mid_img, 451b66ff2c2SEric Blake '-F', iotests.imgfmt, test_img) 452d4398489SKevin Wolf 453d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img) 454d4398489SKevin Wolf qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img) 455d4398489SKevin Wolf 456d4398489SKevin Wolf self.vm = iotests.VM() 457d4398489SKevin Wolf self.vm.launch() 458d4398489SKevin Wolf 459d4398489SKevin Wolf self.blkdebug_file = iotests.file_path("blkdebug.conf") 460d4398489SKevin Wolf 461d4398489SKevin Wolf def tearDown(self): 462d4398489SKevin Wolf self.vm.shutdown() 463d4398489SKevin Wolf os.remove(test_img) 464d4398489SKevin Wolf os.remove(mid_img) 465d4398489SKevin Wolf os.remove(backing_img) 466d4398489SKevin Wolf 467d4398489SKevin Wolf def blockdev_add(self, **kwargs): 468*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', **kwargs) 469d4398489SKevin Wolf 470d4398489SKevin Wolf def add_block_nodes(self, base_debug=None, mid_debug=None, top_debug=None): 471d4398489SKevin Wolf self.blockdev_add(node_name='base-file', driver='file', 472d4398489SKevin Wolf filename=backing_img) 473d4398489SKevin Wolf self.blockdev_add(node_name='mid-file', driver='file', 474d4398489SKevin Wolf filename=mid_img) 475d4398489SKevin Wolf self.blockdev_add(node_name='top-file', driver='file', 476d4398489SKevin Wolf filename=test_img) 477d4398489SKevin Wolf 478d4398489SKevin Wolf if base_debug: 479d4398489SKevin Wolf self.blockdev_add(node_name='base-dbg', driver='blkdebug', 480d4398489SKevin Wolf image='base-file', inject_error=base_debug) 481d4398489SKevin Wolf if mid_debug: 482d4398489SKevin Wolf self.blockdev_add(node_name='mid-dbg', driver='blkdebug', 483d4398489SKevin Wolf image='mid-file', inject_error=mid_debug) 484d4398489SKevin Wolf if top_debug: 485d4398489SKevin Wolf self.blockdev_add(node_name='top-dbg', driver='blkdebug', 486d4398489SKevin Wolf image='top-file', inject_error=top_debug) 487d4398489SKevin Wolf 488d4398489SKevin Wolf self.blockdev_add(node_name='base-fmt', driver='raw', 489d4398489SKevin Wolf file=('base-dbg' if base_debug else 'base-file')) 490d4398489SKevin Wolf self.blockdev_add(node_name='mid-fmt', driver=iotests.imgfmt, 491d4398489SKevin Wolf file=('mid-dbg' if mid_debug else 'mid-file'), 492d4398489SKevin Wolf backing='base-fmt') 493d4398489SKevin Wolf self.blockdev_add(node_name='top-fmt', driver=iotests.imgfmt, 494d4398489SKevin Wolf file=('top-dbg' if top_debug else 'top-file'), 495d4398489SKevin Wolf backing='mid-fmt') 496d4398489SKevin Wolf 497d4398489SKevin Wolf def run_job(self, expected_events, error_pauses_job=False): 498d4398489SKevin Wolf match_device = {'data': {'device': 'job0'}} 499d4398489SKevin Wolf events = [ 500d4398489SKevin Wolf ('BLOCK_JOB_COMPLETED', match_device), 501d4398489SKevin Wolf ('BLOCK_JOB_CANCELLED', match_device), 502d4398489SKevin Wolf ('BLOCK_JOB_ERROR', match_device), 503d4398489SKevin Wolf ('BLOCK_JOB_READY', match_device), 504d4398489SKevin Wolf ] 505d4398489SKevin Wolf 506d4398489SKevin Wolf completed = False 507d4398489SKevin Wolf log = [] 508d4398489SKevin Wolf while not completed: 509d4398489SKevin Wolf ev = self.vm.events_wait(events, timeout=5.0) 510d4398489SKevin Wolf if ev['event'] == 'BLOCK_JOB_COMPLETED': 511d4398489SKevin Wolf completed = True 512d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_ERROR': 513d4398489SKevin Wolf if error_pauses_job: 514*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-job-resume', device='job0') 515d4398489SKevin Wolf elif ev['event'] == 'BLOCK_JOB_READY': 516*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-job-complete', device='job0') 517d4398489SKevin Wolf else: 518d4398489SKevin Wolf self.fail("Unexpected event: %s" % ev) 519d4398489SKevin Wolf log.append(iotests.filter_qmp_event(ev)) 520d4398489SKevin Wolf 521d4398489SKevin Wolf self.maxDiff = None 522d4398489SKevin Wolf self.assertEqual(expected_events, log) 523d4398489SKevin Wolf 524d4398489SKevin Wolf def event_error(self, op, action): 525d4398489SKevin Wolf return { 526d4398489SKevin Wolf 'event': 'BLOCK_JOB_ERROR', 527d4398489SKevin Wolf 'data': {'action': action, 'device': 'job0', 'operation': op}, 528d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'} 529d4398489SKevin Wolf } 530d4398489SKevin Wolf 531d4398489SKevin Wolf def event_ready(self): 532d4398489SKevin Wolf return { 533d4398489SKevin Wolf 'event': 'BLOCK_JOB_READY', 534d4398489SKevin Wolf 'data': {'device': 'job0', 535d4398489SKevin Wolf 'len': 524288, 536d4398489SKevin Wolf 'offset': 524288, 537d4398489SKevin Wolf 'speed': 0, 538d4398489SKevin Wolf 'type': 'commit'}, 539d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 540d4398489SKevin Wolf } 541d4398489SKevin Wolf 542d4398489SKevin Wolf def event_completed(self, errmsg=None, active=True): 543d4398489SKevin Wolf max_len = 524288 if active else self.image_len 544d4398489SKevin Wolf data = { 545d4398489SKevin Wolf 'device': 'job0', 546d4398489SKevin Wolf 'len': max_len, 547d4398489SKevin Wolf 'offset': 0 if errmsg else max_len, 548d4398489SKevin Wolf 'speed': 0, 549d4398489SKevin Wolf 'type': 'commit' 550d4398489SKevin Wolf } 551d4398489SKevin Wolf if errmsg: 552d4398489SKevin Wolf data['error'] = errmsg 553d4398489SKevin Wolf 554d4398489SKevin Wolf return { 555d4398489SKevin Wolf 'event': 'BLOCK_JOB_COMPLETED', 556d4398489SKevin Wolf 'data': data, 557d4398489SKevin Wolf 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}, 558d4398489SKevin Wolf } 559d4398489SKevin Wolf 560d4398489SKevin Wolf def blkdebug_event(self, event, is_raw=False): 561d4398489SKevin Wolf if event: 562d4398489SKevin Wolf return [{ 563d4398489SKevin Wolf 'event': event, 564d4398489SKevin Wolf 'sector': 512 if is_raw else 1024, 565d4398489SKevin Wolf 'once': True, 566d4398489SKevin Wolf }] 567d4398489SKevin Wolf return None 568d4398489SKevin Wolf 569d4398489SKevin Wolf def prepare_and_start_job(self, on_error, active=True, 570d4398489SKevin Wolf top_event=None, mid_event=None, base_event=None): 571d4398489SKevin Wolf 572d4398489SKevin Wolf top_debug = self.blkdebug_event(top_event) 573d4398489SKevin Wolf mid_debug = self.blkdebug_event(mid_event) 574d4398489SKevin Wolf base_debug = self.blkdebug_event(base_event, True) 575d4398489SKevin Wolf 576d4398489SKevin Wolf self.add_block_nodes(top_debug=top_debug, mid_debug=mid_debug, 577d4398489SKevin Wolf base_debug=base_debug) 578d4398489SKevin Wolf 579*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', job_id='job0', device='top-fmt', 580d4398489SKevin Wolf top_node='top-fmt' if active else 'mid-fmt', 581d4398489SKevin Wolf base_node='mid-fmt' if active else 'base-fmt', 582d4398489SKevin Wolf on_error=on_error) 583d4398489SKevin Wolf 584d4398489SKevin Wolf def testActiveReadErrorReport(self): 585d4398489SKevin Wolf self.prepare_and_start_job('report', top_event='read_aio') 586d4398489SKevin Wolf self.run_job([ 587d4398489SKevin Wolf self.event_error('read', 'report'), 588d4398489SKevin Wolf self.event_completed('Input/output error') 589d4398489SKevin Wolf ]) 590d4398489SKevin Wolf 591d4398489SKevin Wolf self.vm.shutdown() 592d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 593d4398489SKevin Wolf 'target image matches source after error') 594d4398489SKevin Wolf 595d4398489SKevin Wolf def testActiveReadErrorStop(self): 596d4398489SKevin Wolf self.prepare_and_start_job('stop', top_event='read_aio') 597d4398489SKevin Wolf self.run_job([ 598d4398489SKevin Wolf self.event_error('read', 'stop'), 599d4398489SKevin Wolf self.event_ready(), 600d4398489SKevin Wolf self.event_completed() 601d4398489SKevin Wolf ], error_pauses_job=True) 602d4398489SKevin Wolf 603d4398489SKevin Wolf self.vm.shutdown() 604d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 605d4398489SKevin Wolf 'target image does not match source after commit') 606d4398489SKevin Wolf 607d4398489SKevin Wolf def testActiveReadErrorIgnore(self): 608d4398489SKevin Wolf self.prepare_and_start_job('ignore', top_event='read_aio') 609d4398489SKevin Wolf self.run_job([ 610d4398489SKevin Wolf self.event_error('read', 'ignore'), 611d4398489SKevin Wolf self.event_ready(), 612d4398489SKevin Wolf self.event_completed() 613d4398489SKevin Wolf ]) 614d4398489SKevin Wolf 615d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 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 testActiveWriteErrorReport(self): 621d4398489SKevin Wolf self.prepare_and_start_job('report', mid_event='write_aio') 622d4398489SKevin Wolf self.run_job([ 623d4398489SKevin Wolf self.event_error('write', 'report'), 624d4398489SKevin Wolf self.event_completed('Input/output error') 625d4398489SKevin Wolf ]) 626d4398489SKevin Wolf 627d4398489SKevin Wolf self.vm.shutdown() 628d4398489SKevin Wolf self.assertFalse(iotests.compare_images(test_img, mid_img), 629d4398489SKevin Wolf 'target image matches source after error') 630d4398489SKevin Wolf 631d4398489SKevin Wolf def testActiveWriteErrorStop(self): 632d4398489SKevin Wolf self.prepare_and_start_job('stop', mid_event='write_aio') 633d4398489SKevin Wolf self.run_job([ 634d4398489SKevin Wolf self.event_error('write', 'stop'), 635d4398489SKevin Wolf self.event_ready(), 636d4398489SKevin Wolf self.event_completed() 637d4398489SKevin Wolf ], error_pauses_job=True) 638d4398489SKevin Wolf 639d4398489SKevin Wolf self.vm.shutdown() 640d4398489SKevin Wolf self.assertTrue(iotests.compare_images(test_img, mid_img), 641d4398489SKevin Wolf 'target image does not match source after commit') 642d4398489SKevin Wolf 643d4398489SKevin Wolf def testActiveWriteErrorIgnore(self): 644d4398489SKevin Wolf self.prepare_and_start_job('ignore', mid_event='write_aio') 645d4398489SKevin Wolf self.run_job([ 646d4398489SKevin Wolf self.event_error('write', 'ignore'), 647d4398489SKevin Wolf self.event_ready(), 648d4398489SKevin Wolf self.event_completed() 649d4398489SKevin Wolf ]) 650d4398489SKevin Wolf 651d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 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 testIntermediateReadErrorReport(self): 657d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, mid_event='read_aio') 658d4398489SKevin Wolf self.run_job([ 659d4398489SKevin Wolf self.event_error('read', 'report'), 660d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 661d4398489SKevin Wolf ]) 662d4398489SKevin Wolf 663d4398489SKevin Wolf self.vm.shutdown() 664d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 665d4398489SKevin Wolf 'target image matches source after error') 666d4398489SKevin Wolf 667d4398489SKevin Wolf def testIntermediateReadErrorStop(self): 668d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, mid_event='read_aio') 669d4398489SKevin Wolf self.run_job([ 670d4398489SKevin Wolf self.event_error('read', 'stop'), 671d4398489SKevin Wolf self.event_completed(active=False) 672d4398489SKevin Wolf ], error_pauses_job=True) 673d4398489SKevin Wolf 674d4398489SKevin Wolf self.vm.shutdown() 675d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 676d4398489SKevin Wolf 'target image does not match source after commit') 677d4398489SKevin Wolf 678d4398489SKevin Wolf def testIntermediateReadErrorIgnore(self): 679d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, mid_event='read_aio') 680d4398489SKevin Wolf self.run_job([ 681d4398489SKevin Wolf self.event_error('read', 'ignore'), 682d4398489SKevin Wolf self.event_completed(active=False) 683d4398489SKevin Wolf ]) 684d4398489SKevin Wolf 685d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 686d4398489SKevin Wolf self.vm.shutdown() 687d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 688d4398489SKevin Wolf 'target image does not match source after commit') 689d4398489SKevin Wolf 690d4398489SKevin Wolf def testIntermediateWriteErrorReport(self): 691d4398489SKevin Wolf self.prepare_and_start_job('report', active=False, base_event='write_aio') 692d4398489SKevin Wolf self.run_job([ 693d4398489SKevin Wolf self.event_error('write', 'report'), 694d4398489SKevin Wolf self.event_completed('Input/output error', active=False) 695d4398489SKevin Wolf ]) 696d4398489SKevin Wolf 697d4398489SKevin Wolf self.vm.shutdown() 698d4398489SKevin Wolf self.assertFalse(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 699d4398489SKevin Wolf 'target image matches source after error') 700d4398489SKevin Wolf 701d4398489SKevin Wolf def testIntermediateWriteErrorStop(self): 702d4398489SKevin Wolf self.prepare_and_start_job('stop', active=False, base_event='write_aio') 703d4398489SKevin Wolf self.run_job([ 704d4398489SKevin Wolf self.event_error('write', 'stop'), 705d4398489SKevin Wolf self.event_completed(active=False) 706d4398489SKevin Wolf ], error_pauses_job=True) 707d4398489SKevin Wolf 708d4398489SKevin Wolf self.vm.shutdown() 709d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 710d4398489SKevin Wolf 'target image does not match source after commit') 711d4398489SKevin Wolf 712d4398489SKevin Wolf def testIntermediateWriteErrorIgnore(self): 713d4398489SKevin Wolf self.prepare_and_start_job('ignore', active=False, base_event='write_aio') 714d4398489SKevin Wolf self.run_job([ 715d4398489SKevin Wolf self.event_error('write', 'ignore'), 716d4398489SKevin Wolf self.event_completed(active=False) 717d4398489SKevin Wolf ]) 718d4398489SKevin Wolf 719d4398489SKevin Wolf # For commit, 'ignore' actually means retry, so this will succeed 720d4398489SKevin Wolf self.vm.shutdown() 721d4398489SKevin Wolf self.assertTrue(iotests.compare_images(mid_img, backing_img, fmt2='raw'), 722d4398489SKevin Wolf 'target image does not match source after commit') 723d4398489SKevin Wolf 724ffbba7fdSMax Reitzclass TestCommitWithFilters(iotests.QMPTestCase): 725ffbba7fdSMax Reitz img0 = os.path.join(iotests.test_dir, '0.img') 726ffbba7fdSMax Reitz img1 = os.path.join(iotests.test_dir, '1.img') 727ffbba7fdSMax Reitz img2 = os.path.join(iotests.test_dir, '2.img') 728ffbba7fdSMax Reitz img3 = os.path.join(iotests.test_dir, '3.img') 729ffbba7fdSMax Reitz 730ffbba7fdSMax Reitz def do_test_io(self, read_or_write): 731ffbba7fdSMax Reitz for index, pattern_file in enumerate(self.pattern_files): 732e9039c04SJohn Snow qemu_io('-f', iotests.imgfmt, 733ffbba7fdSMax Reitz '-c', 734ffbba7fdSMax Reitz f'{read_or_write} -P {index + 1} {index}M 1M', 735ffbba7fdSMax Reitz pattern_file) 736ffbba7fdSMax Reitz 737024354eaSThomas Huth @iotests.skip_if_unsupported(['throttle']) 738ffbba7fdSMax Reitz def setUp(self): 739ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M') 740ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M') 741ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img2, '64M') 742ffbba7fdSMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img3, '64M') 743ffbba7fdSMax Reitz 744ffbba7fdSMax Reitz # Distributions of the patterns in the files; this is checked 745ffbba7fdSMax Reitz # by tearDown() and should be changed by the test cases as is 746ffbba7fdSMax Reitz # necessary 747ffbba7fdSMax Reitz self.pattern_files = [self.img0, self.img1, self.img2, self.img3] 748ffbba7fdSMax Reitz 749ffbba7fdSMax Reitz self.do_test_io('write') 750ffbba7fdSMax Reitz 751ffbba7fdSMax Reitz self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi') 752ffbba7fdSMax Reitz self.vm.launch() 753ffbba7fdSMax Reitz 754*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('object-add', qom_type='throttle-group', id='tg') 755ffbba7fdSMax Reitz 756*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', { 757ffbba7fdSMax Reitz 'node-name': 'top-filter', 758ffbba7fdSMax Reitz 'driver': 'throttle', 759ffbba7fdSMax Reitz 'throttle-group': 'tg', 760ffbba7fdSMax Reitz 'file': { 761ffbba7fdSMax Reitz 'node-name': 'cow-3', 762ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 763ffbba7fdSMax Reitz 'file': { 764ffbba7fdSMax Reitz 'driver': 'file', 765ffbba7fdSMax Reitz 'filename': self.img3 766ffbba7fdSMax Reitz }, 767ffbba7fdSMax Reitz 'backing': { 768ffbba7fdSMax Reitz 'node-name': 'cow-2', 769ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 770ffbba7fdSMax Reitz 'file': { 771ffbba7fdSMax Reitz 'driver': 'file', 772ffbba7fdSMax Reitz 'filename': self.img2 773ffbba7fdSMax Reitz }, 774ffbba7fdSMax Reitz 'backing': { 775ffbba7fdSMax Reitz 'node-name': 'cow-1', 776ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 777ffbba7fdSMax Reitz 'file': { 778ffbba7fdSMax Reitz 'driver': 'file', 779ffbba7fdSMax Reitz 'filename': self.img1 780ffbba7fdSMax Reitz }, 781ffbba7fdSMax Reitz 'backing': { 782ffbba7fdSMax Reitz 'node-name': 'bottom-filter', 783ffbba7fdSMax Reitz 'driver': 'throttle', 784ffbba7fdSMax Reitz 'throttle-group': 'tg', 785ffbba7fdSMax Reitz 'file': { 786ffbba7fdSMax Reitz 'node-name': 'cow-0', 787ffbba7fdSMax Reitz 'driver': iotests.imgfmt, 788ffbba7fdSMax Reitz 'file': { 789ffbba7fdSMax Reitz 'driver': 'file', 790ffbba7fdSMax Reitz 'filename': self.img0 791ffbba7fdSMax Reitz } 792ffbba7fdSMax Reitz } 793ffbba7fdSMax Reitz } 794ffbba7fdSMax Reitz } 795ffbba7fdSMax Reitz } 796ffbba7fdSMax Reitz } 797ffbba7fdSMax Reitz }) 798ffbba7fdSMax Reitz 799ffbba7fdSMax Reitz def tearDown(self): 800ffbba7fdSMax Reitz self.vm.shutdown() 801ffbba7fdSMax Reitz self.do_test_io('read') 802ffbba7fdSMax Reitz 803ffbba7fdSMax Reitz os.remove(self.img3) 804ffbba7fdSMax Reitz os.remove(self.img2) 805ffbba7fdSMax Reitz os.remove(self.img1) 806ffbba7fdSMax Reitz os.remove(self.img0) 807ffbba7fdSMax Reitz 808ffbba7fdSMax Reitz # Filters make for funny filenames, so we cannot just use 809ffbba7fdSMax Reitz # self.imgX to get them 810ffbba7fdSMax Reitz def get_filename(self, node): 811ffbba7fdSMax Reitz return self.vm.node_info(node)['image']['filename'] 812ffbba7fdSMax Reitz 813ffbba7fdSMax Reitz def test_filterless_commit(self): 814*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', 815ffbba7fdSMax Reitz job_id='commit', 816ffbba7fdSMax Reitz device='top-filter', 817ffbba7fdSMax Reitz top_node='cow-2', 81848976291SJohn Snow base_node='cow-1', 81948976291SJohn Snow backing_file=self.img1) 820ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 821ffbba7fdSMax Reitz 822ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-3')) 823ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-2')) 824ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-1')) 825ffbba7fdSMax Reitz 82696420a30SMichael Tokarev # 2 has been committed into 1 827ffbba7fdSMax Reitz self.pattern_files[2] = self.img1 828ffbba7fdSMax Reitz 829ffbba7fdSMax Reitz def test_commit_through_filter(self): 830*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', 831ffbba7fdSMax Reitz job_id='commit', 832ffbba7fdSMax Reitz device='top-filter', 833ffbba7fdSMax Reitz top_node='cow-1', 83448976291SJohn Snow base_node='cow-0', 83548976291SJohn Snow backing_file=self.img0) 836ffbba7fdSMax Reitz self.wait_until_completed(drive='commit') 837ffbba7fdSMax Reitz 838ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 839ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-1')) 840ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('bottom-filter')) 841ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-0')) 842ffbba7fdSMax Reitz 84396420a30SMichael Tokarev # 1 has been committed into 0 844ffbba7fdSMax Reitz self.pattern_files[1] = self.img0 845ffbba7fdSMax Reitz 846ffbba7fdSMax Reitz def test_filtered_active_commit_with_filter(self): 847ffbba7fdSMax Reitz # Add a device, so the commit job finds a parent it can change 848ffbba7fdSMax Reitz # to point to the base node (so we can test that top-filter is 849ffbba7fdSMax Reitz # dropped from the graph) 850*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('device_add', id='drv0', driver='scsi-hd', 851ffbba7fdSMax Reitz bus='vio-scsi.0', drive='top-filter') 852ffbba7fdSMax Reitz 853ffbba7fdSMax Reitz # Try to release our reference to top-filter; that should not 854ffbba7fdSMax Reitz # work because drv0 uses it 855ffbba7fdSMax Reitz result = self.vm.qmp('blockdev-del', node_name='top-filter') 856ffbba7fdSMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 857ffbba7fdSMax Reitz self.assert_qmp(result, 'error/desc', 'Node top-filter is in use') 858ffbba7fdSMax Reitz 859*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', 860ffbba7fdSMax Reitz job_id='commit', 861ffbba7fdSMax Reitz device='top-filter', 862ffbba7fdSMax Reitz base_node='cow-2') 863ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 864ffbba7fdSMax Reitz 865ffbba7fdSMax Reitz # Try to release our reference to top-filter again 866*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-del', node_name='top-filter') 867ffbba7fdSMax Reitz 868ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('top-filter')) 869ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 870ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 871ffbba7fdSMax Reitz 872ffbba7fdSMax Reitz # Check that drv0 is now connected to cow-2 873ffbba7fdSMax Reitz blockdevs = self.vm.qmp('query-block')['return'] 874ffbba7fdSMax Reitz drv0 = next(dev for dev in blockdevs if dev['qdev'] == 'drv0') 875ffbba7fdSMax Reitz self.assertEqual(drv0['inserted']['node-name'], 'cow-2') 876ffbba7fdSMax Reitz 87796420a30SMichael Tokarev # 3 has been committed into 2 878ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 879ffbba7fdSMax Reitz 880ffbba7fdSMax Reitz def test_filtered_active_commit_without_filter(self): 881*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', 882ffbba7fdSMax Reitz job_id='commit', 883ffbba7fdSMax Reitz device='top-filter', 884ffbba7fdSMax Reitz top_node='cow-3', 885ffbba7fdSMax Reitz base_node='cow-2') 886ffbba7fdSMax Reitz self.complete_and_wait(drive='commit') 887ffbba7fdSMax Reitz 888ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('top-filter')) 889ffbba7fdSMax Reitz self.assertIsNone(self.vm.node_info('cow-3')) 890ffbba7fdSMax Reitz self.assertIsNotNone(self.vm.node_info('cow-2')) 891ffbba7fdSMax Reitz 89296420a30SMichael Tokarev # 3 has been committed into 2 893ffbba7fdSMax Reitz self.pattern_files[3] = self.img2 894ffbba7fdSMax Reitz 895374eedd1SMax Reitzclass TestCommitWithOverriddenBacking(iotests.QMPTestCase): 896374eedd1SMax Reitz img_base_a = os.path.join(iotests.test_dir, 'base_a.img') 897374eedd1SMax Reitz img_base_b = os.path.join(iotests.test_dir, 'base_b.img') 898374eedd1SMax Reitz img_top = os.path.join(iotests.test_dir, 'top.img') 899374eedd1SMax Reitz 900374eedd1SMax Reitz def setUp(self): 901374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_a, '1M') 902374eedd1SMax Reitz qemu_img('create', '-f', iotests.imgfmt, self.img_base_b, '1M') 903497a30dbSEric Blake qemu_img('create', '-f', iotests.imgfmt, '-b', self.img_base_a, 904497a30dbSEric Blake '-F', iotests.imgfmt, self.img_top) 905374eedd1SMax Reitz 906374eedd1SMax Reitz self.vm = iotests.VM() 907374eedd1SMax Reitz self.vm.launch() 908374eedd1SMax Reitz 909374eedd1SMax Reitz # Use base_b instead of base_a as the backing of top 910*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', { 911374eedd1SMax Reitz 'node-name': 'top', 912374eedd1SMax Reitz 'driver': iotests.imgfmt, 913374eedd1SMax Reitz 'file': { 914374eedd1SMax Reitz 'driver': 'file', 915374eedd1SMax Reitz 'filename': self.img_top 916374eedd1SMax Reitz }, 917374eedd1SMax Reitz 'backing': { 918374eedd1SMax Reitz 'node-name': 'base', 919374eedd1SMax Reitz 'driver': iotests.imgfmt, 920374eedd1SMax Reitz 'file': { 921374eedd1SMax Reitz 'driver': 'file', 922374eedd1SMax Reitz 'filename': self.img_base_b 923374eedd1SMax Reitz } 924374eedd1SMax Reitz } 925374eedd1SMax Reitz }) 926374eedd1SMax Reitz 927374eedd1SMax Reitz def tearDown(self): 928374eedd1SMax Reitz self.vm.shutdown() 929374eedd1SMax Reitz os.remove(self.img_top) 930374eedd1SMax Reitz os.remove(self.img_base_a) 931374eedd1SMax Reitz os.remove(self.img_base_b) 932374eedd1SMax Reitz 933374eedd1SMax Reitz def test_commit_to_a(self): 934374eedd1SMax Reitz # Try committing to base_a (which should fail, as top's 935374eedd1SMax Reitz # backing image is base_b instead) 936374eedd1SMax Reitz result = self.vm.qmp('block-commit', 937374eedd1SMax Reitz job_id='commit', 938374eedd1SMax Reitz device='top', 939374eedd1SMax Reitz base=self.img_base_a) 940374eedd1SMax Reitz self.assert_qmp(result, 'error/class', 'GenericError') 941374eedd1SMax Reitz 942374eedd1SMax Reitz def test_commit_to_b(self): 943374eedd1SMax Reitz # Try committing to base_b (which should work, since that is 944374eedd1SMax Reitz # actually top's backing image) 945*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('block-commit', 946374eedd1SMax Reitz job_id='commit', 947374eedd1SMax Reitz device='top', 948374eedd1SMax Reitz base=self.img_base_b) 949374eedd1SMax Reitz 950374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_READY') 951374eedd1SMax Reitz self.vm.qmp('block-job-complete', device='commit') 952374eedd1SMax Reitz self.vm.event_wait('BLOCK_JOB_COMPLETED') 953374eedd1SMax Reitz 954747051cdSJeff Codyif __name__ == '__main__': 955103cbc77SMax Reitz iotests.main(supported_fmts=['qcow2', 'qed'], 956103cbc77SMax Reitz supported_protocols=['file']) 957