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