1#!/usr/bin/env python 2# 3# Tests for image streaming. 4# 5# Copyright (C) 2012 IBM Corp. 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19# 20 21import os 22import iotests 23from iotests import qemu_img, qemu_io 24 25backing_img = os.path.join(iotests.test_dir, 'backing.img') 26mid_img = os.path.join(iotests.test_dir, 'mid.img') 27test_img = os.path.join(iotests.test_dir, 'test.img') 28 29class ImageStreamingTestCase(iotests.QMPTestCase): 30 '''Abstract base class for image streaming test cases''' 31 32 def assert_no_active_streams(self): 33 result = self.vm.qmp('query-block-jobs') 34 self.assert_qmp(result, 'return', []) 35 36 def cancel_and_wait(self, drive='drive0'): 37 '''Cancel a block job and wait for it to finish''' 38 result = self.vm.qmp('block-job-cancel', device=drive) 39 self.assert_qmp(result, 'return', {}) 40 41 cancelled = False 42 while not cancelled: 43 for event in self.vm.get_qmp_events(wait=True): 44 if event['event'] == 'BLOCK_JOB_CANCELLED': 45 self.assert_qmp(event, 'data/type', 'stream') 46 self.assert_qmp(event, 'data/device', drive) 47 cancelled = True 48 49 self.assert_no_active_streams() 50 51class TestSingleDrive(ImageStreamingTestCase): 52 image_len = 1 * 1024 * 1024 # MB 53 54 def setUp(self): 55 qemu_img('create', backing_img, str(TestSingleDrive.image_len)) 56 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) 57 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) 58 self.vm = iotests.VM().add_drive(test_img) 59 self.vm.launch() 60 61 def tearDown(self): 62 self.vm.shutdown() 63 os.remove(test_img) 64 os.remove(mid_img) 65 os.remove(backing_img) 66 67 def test_stream(self): 68 self.assert_no_active_streams() 69 70 result = self.vm.qmp('block-stream', device='drive0') 71 self.assert_qmp(result, 'return', {}) 72 73 completed = False 74 while not completed: 75 for event in self.vm.get_qmp_events(wait=True): 76 if event['event'] == 'BLOCK_JOB_COMPLETED': 77 self.assert_qmp(event, 'data/type', 'stream') 78 self.assert_qmp(event, 'data/device', 'drive0') 79 self.assert_qmp(event, 'data/offset', self.image_len) 80 self.assert_qmp(event, 'data/len', self.image_len) 81 completed = True 82 83 self.assert_no_active_streams() 84 self.vm.shutdown() 85 86 self.assertEqual(qemu_io('-c', 'map', backing_img), 87 qemu_io('-c', 'map', test_img), 88 'image file map does not match backing file after streaming') 89 90 def test_stream_partial(self): 91 self.assert_no_active_streams() 92 93 result = self.vm.qmp('block-stream', device='drive0', base=mid_img) 94 self.assert_qmp(result, 'return', {}) 95 96 completed = False 97 while not completed: 98 for event in self.vm.get_qmp_events(wait=True): 99 if event['event'] == 'BLOCK_JOB_COMPLETED': 100 self.assert_qmp(event, 'data/type', 'stream') 101 self.assert_qmp(event, 'data/device', 'drive0') 102 self.assert_qmp(event, 'data/offset', self.image_len) 103 self.assert_qmp(event, 'data/len', self.image_len) 104 completed = True 105 106 self.assert_no_active_streams() 107 self.vm.shutdown() 108 109 self.assertEqual(qemu_io('-c', 'map', mid_img), 110 qemu_io('-c', 'map', test_img), 111 'image file map does not match backing file after streaming') 112 113 def test_device_not_found(self): 114 result = self.vm.qmp('block-stream', device='nonexistent') 115 self.assert_qmp(result, 'error/class', 'DeviceNotFound') 116 117class TestStreamStop(ImageStreamingTestCase): 118 image_len = 8 * 1024 * 1024 * 1024 # GB 119 120 def setUp(self): 121 qemu_img('create', backing_img, str(TestStreamStop.image_len)) 122 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) 123 self.vm = iotests.VM().add_drive(test_img) 124 self.vm.launch() 125 126 def tearDown(self): 127 self.vm.shutdown() 128 os.remove(test_img) 129 os.remove(backing_img) 130 131 def test_stream_stop(self): 132 import time 133 134 self.assert_no_active_streams() 135 136 result = self.vm.qmp('block-stream', device='drive0') 137 self.assert_qmp(result, 'return', {}) 138 139 time.sleep(1) 140 events = self.vm.get_qmp_events(wait=False) 141 self.assertEqual(events, [], 'unexpected QMP event: %s' % events) 142 143 self.cancel_and_wait() 144 145class TestSetSpeed(ImageStreamingTestCase): 146 image_len = 80 * 1024 * 1024 # MB 147 148 def setUp(self): 149 qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 150 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) 151 self.vm = iotests.VM().add_drive(test_img) 152 self.vm.launch() 153 154 def tearDown(self): 155 self.vm.shutdown() 156 os.remove(test_img) 157 os.remove(backing_img) 158 159 # This is a short performance test which is not run by default. 160 # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput" 161 def perf_test_throughput(self): 162 self.assert_no_active_streams() 163 164 result = self.vm.qmp('block-stream', device='drive0') 165 self.assert_qmp(result, 'return', {}) 166 167 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 168 self.assert_qmp(result, 'return', {}) 169 170 completed = False 171 while not completed: 172 for event in self.vm.get_qmp_events(wait=True): 173 if event['event'] == 'BLOCK_JOB_COMPLETED': 174 self.assert_qmp(event, 'data/type', 'stream') 175 self.assert_qmp(event, 'data/device', 'drive0') 176 self.assert_qmp(event, 'data/offset', self.image_len) 177 self.assert_qmp(event, 'data/len', self.image_len) 178 completed = True 179 180 self.assert_no_active_streams() 181 182 def test_set_speed(self): 183 self.assert_no_active_streams() 184 185 result = self.vm.qmp('block-stream', device='drive0') 186 self.assert_qmp(result, 'return', {}) 187 188 # Default speed is 0 189 result = self.vm.qmp('query-block-jobs') 190 self.assert_qmp(result, 'return[0]/device', 'drive0') 191 self.assert_qmp(result, 'return[0]/speed', 0) 192 193 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 194 self.assert_qmp(result, 'return', {}) 195 196 # Ensure the speed we set was accepted 197 result = self.vm.qmp('query-block-jobs') 198 self.assert_qmp(result, 'return[0]/device', 'drive0') 199 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) 200 201 self.cancel_and_wait() 202 203 # Check setting speed in block-stream works 204 result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024) 205 self.assert_qmp(result, 'return', {}) 206 207 result = self.vm.qmp('query-block-jobs') 208 self.assert_qmp(result, 'return[0]/device', 'drive0') 209 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) 210 211 self.cancel_and_wait() 212 213 def test_set_speed_invalid(self): 214 self.assert_no_active_streams() 215 216 result = self.vm.qmp('block-stream', device='drive0', speed=-1) 217 self.assert_qmp(result, 'error/class', 'InvalidParameter') 218 self.assert_qmp(result, 'error/data/name', 'speed') 219 220 self.assert_no_active_streams() 221 222 result = self.vm.qmp('block-stream', device='drive0') 223 self.assert_qmp(result, 'return', {}) 224 225 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) 226 self.assert_qmp(result, 'error/class', 'InvalidParameter') 227 self.assert_qmp(result, 'error/data/name', 'speed') 228 229 self.cancel_and_wait() 230 231if __name__ == '__main__': 232 iotests.main(supported_fmts=['qcow2', 'qed']) 233