1*747051cdSJeff Cody#!/usr/bin/env python 2*747051cdSJeff Cody# 3*747051cdSJeff Cody# Tests for image block commit. 4*747051cdSJeff Cody# 5*747051cdSJeff Cody# Copyright (C) 2012 IBM, Corp. 6*747051cdSJeff Cody# Copyright (C) 2012 Red Hat, Inc. 7*747051cdSJeff Cody# 8*747051cdSJeff Cody# This program is free software; you can redistribute it and/or modify 9*747051cdSJeff Cody# it under the terms of the GNU General Public License as published by 10*747051cdSJeff Cody# the Free Software Foundation; either version 2 of the License, or 11*747051cdSJeff Cody# (at your option) any later version. 12*747051cdSJeff Cody# 13*747051cdSJeff Cody# This program is distributed in the hope that it will be useful, 14*747051cdSJeff Cody# but WITHOUT ANY WARRANTY; without even the implied warranty of 15*747051cdSJeff Cody# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*747051cdSJeff Cody# GNU General Public License for more details. 17*747051cdSJeff Cody# 18*747051cdSJeff Cody# You should have received a copy of the GNU General Public License 19*747051cdSJeff Cody# along with this program. If not, see <http://www.gnu.org/licenses/>. 20*747051cdSJeff Cody# 21*747051cdSJeff Cody# Test for live block commit 22*747051cdSJeff Cody# Derived from Image Streaming Test 030 23*747051cdSJeff Cody 24*747051cdSJeff Codyimport time 25*747051cdSJeff Codyimport os 26*747051cdSJeff Codyimport iotests 27*747051cdSJeff Codyfrom iotests import qemu_img, qemu_io 28*747051cdSJeff Codyimport struct 29*747051cdSJeff Cody 30*747051cdSJeff Codybacking_img = os.path.join(iotests.test_dir, 'backing.img') 31*747051cdSJeff Codymid_img = os.path.join(iotests.test_dir, 'mid.img') 32*747051cdSJeff Codytest_img = os.path.join(iotests.test_dir, 'test.img') 33*747051cdSJeff Cody 34*747051cdSJeff Codyclass ImageCommitTestCase(iotests.QMPTestCase): 35*747051cdSJeff Cody '''Abstract base class for image commit test cases''' 36*747051cdSJeff Cody 37*747051cdSJeff Cody def assert_no_active_commit(self): 38*747051cdSJeff Cody result = self.vm.qmp('query-block-jobs') 39*747051cdSJeff Cody self.assert_qmp(result, 'return', []) 40*747051cdSJeff Cody 41*747051cdSJeff Cody def cancel_and_wait(self, drive='drive0'): 42*747051cdSJeff Cody '''Cancel a block job and wait for it to finish''' 43*747051cdSJeff Cody result = self.vm.qmp('block-job-cancel', device=drive) 44*747051cdSJeff Cody self.assert_qmp(result, 'return', {}) 45*747051cdSJeff Cody 46*747051cdSJeff Cody cancelled = False 47*747051cdSJeff Cody while not cancelled: 48*747051cdSJeff Cody for event in self.vm.get_qmp_events(wait=True): 49*747051cdSJeff Cody if event['event'] == 'BLOCK_JOB_CANCELLED': 50*747051cdSJeff Cody self.assert_qmp(event, 'data/type', 'commit') 51*747051cdSJeff Cody self.assert_qmp(event, 'data/device', drive) 52*747051cdSJeff Cody cancelled = True 53*747051cdSJeff Cody 54*747051cdSJeff Cody self.assert_no_active_commit() 55*747051cdSJeff Cody 56*747051cdSJeff Cody def create_image(self, name, size): 57*747051cdSJeff Cody file = open(name, 'w') 58*747051cdSJeff Cody i = 0 59*747051cdSJeff Cody while i < size: 60*747051cdSJeff Cody sector = struct.pack('>l504xl', i / 512, i / 512) 61*747051cdSJeff Cody file.write(sector) 62*747051cdSJeff Cody i = i + 512 63*747051cdSJeff Cody file.close() 64*747051cdSJeff Cody 65*747051cdSJeff Cody 66*747051cdSJeff Codyclass TestSingleDrive(ImageCommitTestCase): 67*747051cdSJeff Cody image_len = 1 * 1024 * 1024 68*747051cdSJeff Cody test_len = 1 * 1024 * 256 69*747051cdSJeff Cody 70*747051cdSJeff Cody def setUp(self): 71*747051cdSJeff Cody self.create_image(backing_img, TestSingleDrive.image_len) 72*747051cdSJeff Cody qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) 73*747051cdSJeff Cody qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) 74*747051cdSJeff Cody qemu_io('-c', 'write -P 0xab 0 524288', backing_img) 75*747051cdSJeff Cody qemu_io('-c', 'write -P 0xef 524288 524288', mid_img) 76*747051cdSJeff Cody self.vm = iotests.VM().add_drive(test_img) 77*747051cdSJeff Cody self.vm.launch() 78*747051cdSJeff Cody 79*747051cdSJeff Cody def tearDown(self): 80*747051cdSJeff Cody self.vm.shutdown() 81*747051cdSJeff Cody os.remove(test_img) 82*747051cdSJeff Cody os.remove(mid_img) 83*747051cdSJeff Cody os.remove(backing_img) 84*747051cdSJeff Cody 85*747051cdSJeff Cody def test_commit(self): 86*747051cdSJeff Cody self.assert_no_active_commit() 87*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img) 88*747051cdSJeff Cody self.assert_qmp(result, 'return', {}) 89*747051cdSJeff Cody 90*747051cdSJeff Cody completed = False 91*747051cdSJeff Cody while not completed: 92*747051cdSJeff Cody for event in self.vm.get_qmp_events(wait=True): 93*747051cdSJeff Cody if event['event'] == 'BLOCK_JOB_COMPLETED': 94*747051cdSJeff Cody self.assert_qmp(event, 'data/type', 'commit') 95*747051cdSJeff Cody self.assert_qmp(event, 'data/device', 'drive0') 96*747051cdSJeff Cody self.assert_qmp(event, 'data/offset', self.image_len) 97*747051cdSJeff Cody self.assert_qmp(event, 'data/len', self.image_len) 98*747051cdSJeff Cody completed = True 99*747051cdSJeff Cody 100*747051cdSJeff Cody self.assert_no_active_commit() 101*747051cdSJeff Cody self.vm.shutdown() 102*747051cdSJeff Cody 103*747051cdSJeff Cody self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) 104*747051cdSJeff Cody self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) 105*747051cdSJeff Cody 106*747051cdSJeff Cody def test_device_not_found(self): 107*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) 108*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'DeviceNotFound') 109*747051cdSJeff Cody 110*747051cdSJeff Cody def test_top_same_base(self): 111*747051cdSJeff Cody self.assert_no_active_commit() 112*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % backing_img) 113*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 114*747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Invalid files for merge: top and base are the same') 115*747051cdSJeff Cody 116*747051cdSJeff Cody def test_top_invalid(self): 117*747051cdSJeff Cody self.assert_no_active_commit() 118*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='badfile', base='%s' % backing_img) 119*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 120*747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Top image file badfile not found') 121*747051cdSJeff Cody 122*747051cdSJeff Cody def test_base_invalid(self): 123*747051cdSJeff Cody self.assert_no_active_commit() 124*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img, base='badfile') 125*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 126*747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found') 127*747051cdSJeff Cody 128*747051cdSJeff Cody def test_top_is_active(self): 129*747051cdSJeff Cody self.assert_no_active_commit() 130*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % test_img, base='%s' % backing_img) 131*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 132*747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported') 133*747051cdSJeff Cody 134*747051cdSJeff Cody def test_top_and_base_reversed(self): 135*747051cdSJeff Cody self.assert_no_active_commit() 136*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top='%s' % backing_img, base='%s' % mid_img) 137*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 138*747051cdSJeff Cody self.assert_qmp(result, 'error/desc', 'Base (%(1)s) is not reachable from top (%(2)s)' % {"1" : mid_img, "2" : backing_img}) 139*747051cdSJeff Cody 140*747051cdSJeff Cody def test_top_omitted(self): 141*747051cdSJeff Cody self.assert_no_active_commit() 142*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0') 143*747051cdSJeff Cody self.assert_qmp(result, 'error/class', 'GenericError') 144*747051cdSJeff Cody self.assert_qmp(result, 'error/desc', "Parameter 'top' is missing") 145*747051cdSJeff Cody 146*747051cdSJeff Cody 147*747051cdSJeff Codyclass TestSetSpeed(ImageCommitTestCase): 148*747051cdSJeff Cody image_len = 80 * 1024 * 1024 # MB 149*747051cdSJeff Cody 150*747051cdSJeff Cody def setUp(self): 151*747051cdSJeff Cody qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 152*747051cdSJeff Cody qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) 153*747051cdSJeff Cody qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) 154*747051cdSJeff Cody self.vm = iotests.VM().add_drive(test_img) 155*747051cdSJeff Cody self.vm.launch() 156*747051cdSJeff Cody 157*747051cdSJeff Cody def tearDown(self): 158*747051cdSJeff Cody self.vm.shutdown() 159*747051cdSJeff Cody os.remove(test_img) 160*747051cdSJeff Cody os.remove(mid_img) 161*747051cdSJeff Cody os.remove(backing_img) 162*747051cdSJeff Cody 163*747051cdSJeff Cody def test_set_speed(self): 164*747051cdSJeff Cody self.assert_no_active_commit() 165*747051cdSJeff Cody 166*747051cdSJeff Cody result = self.vm.qmp('block-commit', device='drive0', top=mid_img, speed=1024 * 1024) 167*747051cdSJeff Cody self.assert_qmp(result, 'return', {}) 168*747051cdSJeff Cody 169*747051cdSJeff Cody # Ensure the speed we set was accepted 170*747051cdSJeff Cody result = self.vm.qmp('query-block-jobs') 171*747051cdSJeff Cody self.assert_qmp(result, 'return[0]/device', 'drive0') 172*747051cdSJeff Cody self.assert_qmp(result, 'return[0]/speed', 1024 * 1024) 173*747051cdSJeff Cody 174*747051cdSJeff Cody self.cancel_and_wait() 175*747051cdSJeff Cody 176*747051cdSJeff Cody 177*747051cdSJeff Codyif __name__ == '__main__': 178*747051cdSJeff Cody iotests.main(supported_fmts=['qcow2', 'qed']) 179