137ce63ebSStefan Hajnoczi#!/usr/bin/env python 237ce63ebSStefan Hajnoczi# 337ce63ebSStefan Hajnoczi# Tests for image streaming. 437ce63ebSStefan Hajnoczi# 537ce63ebSStefan Hajnoczi# Copyright (C) 2012 IBM Corp. 637ce63ebSStefan Hajnoczi# 737ce63ebSStefan Hajnoczi# This program is free software; you can redistribute it and/or modify 837ce63ebSStefan Hajnoczi# it under the terms of the GNU General Public License as published by 937ce63ebSStefan Hajnoczi# the Free Software Foundation; either version 2 of the License, or 1037ce63ebSStefan Hajnoczi# (at your option) any later version. 1137ce63ebSStefan Hajnoczi# 1237ce63ebSStefan Hajnoczi# This program is distributed in the hope that it will be useful, 1337ce63ebSStefan Hajnoczi# but WITHOUT ANY WARRANTY; without even the implied warranty of 1437ce63ebSStefan Hajnoczi# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1537ce63ebSStefan Hajnoczi# GNU General Public License for more details. 1637ce63ebSStefan Hajnoczi# 1737ce63ebSStefan Hajnoczi# You should have received a copy of the GNU General Public License 1837ce63ebSStefan Hajnoczi# along with this program. If not, see <http://www.gnu.org/licenses/>. 1937ce63ebSStefan Hajnoczi# 2037ce63ebSStefan Hajnoczi 21*0c817347SPaolo Bonziniimport time 2237ce63ebSStefan Hajnocziimport os 2337ce63ebSStefan Hajnocziimport iotests 2437ce63ebSStefan Hajnoczifrom iotests import qemu_img, qemu_io 25ab68cdfaSPaolo Bonziniimport struct 2637ce63ebSStefan Hajnoczi 2737ce63ebSStefan Hajnoczibacking_img = os.path.join(iotests.test_dir, 'backing.img') 286e343609SPaolo Bonzinimid_img = os.path.join(iotests.test_dir, 'mid.img') 2937ce63ebSStefan Hajnoczitest_img = os.path.join(iotests.test_dir, 'test.img') 3037ce63ebSStefan Hajnoczi 3137ce63ebSStefan Hajnocziclass ImageStreamingTestCase(iotests.QMPTestCase): 3237ce63ebSStefan Hajnoczi '''Abstract base class for image streaming test cases''' 3337ce63ebSStefan Hajnoczi 3437ce63ebSStefan Hajnoczi def assert_no_active_streams(self): 3537ce63ebSStefan Hajnoczi result = self.vm.qmp('query-block-jobs') 3637ce63ebSStefan Hajnoczi self.assert_qmp(result, 'return', []) 3737ce63ebSStefan Hajnoczi 38e425306aSStefan Hajnoczi def cancel_and_wait(self, drive='drive0'): 39e425306aSStefan Hajnoczi '''Cancel a block job and wait for it to finish''' 40e425306aSStefan Hajnoczi result = self.vm.qmp('block-job-cancel', device=drive) 41e425306aSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 42e425306aSStefan Hajnoczi 43e425306aSStefan Hajnoczi cancelled = False 44e425306aSStefan Hajnoczi while not cancelled: 45e425306aSStefan Hajnoczi for event in self.vm.get_qmp_events(wait=True): 46e425306aSStefan Hajnoczi if event['event'] == 'BLOCK_JOB_CANCELLED': 47e425306aSStefan Hajnoczi self.assert_qmp(event, 'data/type', 'stream') 48e425306aSStefan Hajnoczi self.assert_qmp(event, 'data/device', drive) 49e425306aSStefan Hajnoczi cancelled = True 50e425306aSStefan Hajnoczi 51e425306aSStefan Hajnoczi self.assert_no_active_streams() 52e425306aSStefan Hajnoczi 53ab68cdfaSPaolo Bonzini def create_image(self, name, size): 54ab68cdfaSPaolo Bonzini file = open(name, 'w') 55ab68cdfaSPaolo Bonzini i = 0 56ab68cdfaSPaolo Bonzini while i < size: 57ab68cdfaSPaolo Bonzini sector = struct.pack('>l504xl', i / 512, i / 512) 58ab68cdfaSPaolo Bonzini file.write(sector) 59ab68cdfaSPaolo Bonzini i = i + 512 60ab68cdfaSPaolo Bonzini file.close() 61ab68cdfaSPaolo Bonzini 62ab68cdfaSPaolo Bonzini 6337ce63ebSStefan Hajnocziclass TestSingleDrive(ImageStreamingTestCase): 6437ce63ebSStefan Hajnoczi image_len = 1 * 1024 * 1024 # MB 6537ce63ebSStefan Hajnoczi 6637ce63ebSStefan Hajnoczi def setUp(self): 67ab68cdfaSPaolo Bonzini self.create_image(backing_img, TestSingleDrive.image_len) 686e343609SPaolo Bonzini qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) 696e343609SPaolo Bonzini qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) 7037ce63ebSStefan Hajnoczi self.vm = iotests.VM().add_drive(test_img) 7137ce63ebSStefan Hajnoczi self.vm.launch() 7237ce63ebSStefan Hajnoczi 7337ce63ebSStefan Hajnoczi def tearDown(self): 7437ce63ebSStefan Hajnoczi self.vm.shutdown() 7537ce63ebSStefan Hajnoczi os.remove(test_img) 766e343609SPaolo Bonzini os.remove(mid_img) 7737ce63ebSStefan Hajnoczi os.remove(backing_img) 7837ce63ebSStefan Hajnoczi 7937ce63ebSStefan Hajnoczi def test_stream(self): 8037ce63ebSStefan Hajnoczi self.assert_no_active_streams() 8137ce63ebSStefan Hajnoczi 82db58f9c0SStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0') 8337ce63ebSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 8437ce63ebSStefan Hajnoczi 8537ce63ebSStefan Hajnoczi completed = False 8637ce63ebSStefan Hajnoczi while not completed: 8737ce63ebSStefan Hajnoczi for event in self.vm.get_qmp_events(wait=True): 8837ce63ebSStefan Hajnoczi if event['event'] == 'BLOCK_JOB_COMPLETED': 8937ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/type', 'stream') 9037ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/device', 'drive0') 9137ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/offset', self.image_len) 9237ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/len', self.image_len) 9337ce63ebSStefan Hajnoczi completed = True 9437ce63ebSStefan Hajnoczi 9537ce63ebSStefan Hajnoczi self.assert_no_active_streams() 96863a5d04SPaolo Bonzini self.vm.shutdown() 9737ce63ebSStefan Hajnoczi 98efcc7a23SPaolo Bonzini self.assertEqual(qemu_io('-c', 'map', backing_img), 99efcc7a23SPaolo Bonzini qemu_io('-c', 'map', test_img), 100efcc7a23SPaolo Bonzini 'image file map does not match backing file after streaming') 10137ce63ebSStefan Hajnoczi 102*0c817347SPaolo Bonzini def test_stream_pause(self): 103*0c817347SPaolo Bonzini self.assert_no_active_streams() 104*0c817347SPaolo Bonzini 105*0c817347SPaolo Bonzini result = self.vm.qmp('block-stream', device='drive0') 106*0c817347SPaolo Bonzini self.assert_qmp(result, 'return', {}) 107*0c817347SPaolo Bonzini 108*0c817347SPaolo Bonzini result = self.vm.qmp('block-job-pause', device='drive0') 109*0c817347SPaolo Bonzini self.assert_qmp(result, 'return', {}) 110*0c817347SPaolo Bonzini 111*0c817347SPaolo Bonzini time.sleep(1) 112*0c817347SPaolo Bonzini result = self.vm.qmp('query-block-jobs') 113*0c817347SPaolo Bonzini offset = self.dictpath(result, 'return[0]/offset') 114*0c817347SPaolo Bonzini 115*0c817347SPaolo Bonzini time.sleep(1) 116*0c817347SPaolo Bonzini result = self.vm.qmp('query-block-jobs') 117*0c817347SPaolo Bonzini self.assert_qmp(result, 'return[0]/offset', offset) 118*0c817347SPaolo Bonzini 119*0c817347SPaolo Bonzini result = self.vm.qmp('block-job-resume', device='drive0') 120*0c817347SPaolo Bonzini self.assert_qmp(result, 'return', {}) 121*0c817347SPaolo Bonzini 122*0c817347SPaolo Bonzini completed = False 123*0c817347SPaolo Bonzini while not completed: 124*0c817347SPaolo Bonzini for event in self.vm.get_qmp_events(wait=True): 125*0c817347SPaolo Bonzini if event['event'] == 'BLOCK_JOB_COMPLETED': 126*0c817347SPaolo Bonzini self.assert_qmp(event, 'data/type', 'stream') 127*0c817347SPaolo Bonzini self.assert_qmp(event, 'data/device', 'drive0') 128*0c817347SPaolo Bonzini self.assert_qmp(event, 'data/offset', self.image_len) 129*0c817347SPaolo Bonzini self.assert_qmp(event, 'data/len', self.image_len) 130*0c817347SPaolo Bonzini completed = True 131*0c817347SPaolo Bonzini 132*0c817347SPaolo Bonzini self.assert_no_active_streams() 133*0c817347SPaolo Bonzini self.vm.shutdown() 134*0c817347SPaolo Bonzini 135*0c817347SPaolo Bonzini self.assertEqual(qemu_io('-c', 'map', backing_img), 136*0c817347SPaolo Bonzini qemu_io('-c', 'map', test_img), 137*0c817347SPaolo Bonzini 'image file map does not match backing file after streaming') 138*0c817347SPaolo Bonzini 1396e343609SPaolo Bonzini def test_stream_partial(self): 1406e343609SPaolo Bonzini self.assert_no_active_streams() 1416e343609SPaolo Bonzini 1426e343609SPaolo Bonzini result = self.vm.qmp('block-stream', device='drive0', base=mid_img) 1436e343609SPaolo Bonzini self.assert_qmp(result, 'return', {}) 1446e343609SPaolo Bonzini 1456e343609SPaolo Bonzini completed = False 1466e343609SPaolo Bonzini while not completed: 1476e343609SPaolo Bonzini for event in self.vm.get_qmp_events(wait=True): 1486e343609SPaolo Bonzini if event['event'] == 'BLOCK_JOB_COMPLETED': 1496e343609SPaolo Bonzini self.assert_qmp(event, 'data/type', 'stream') 1506e343609SPaolo Bonzini self.assert_qmp(event, 'data/device', 'drive0') 1516e343609SPaolo Bonzini self.assert_qmp(event, 'data/offset', self.image_len) 1526e343609SPaolo Bonzini self.assert_qmp(event, 'data/len', self.image_len) 1536e343609SPaolo Bonzini completed = True 1546e343609SPaolo Bonzini 1556e343609SPaolo Bonzini self.assert_no_active_streams() 1566e343609SPaolo Bonzini self.vm.shutdown() 1576e343609SPaolo Bonzini 1586e343609SPaolo Bonzini self.assertEqual(qemu_io('-c', 'map', mid_img), 1596e343609SPaolo Bonzini qemu_io('-c', 'map', test_img), 1606e343609SPaolo Bonzini 'image file map does not match backing file after streaming') 1616e343609SPaolo Bonzini 16237ce63ebSStefan Hajnoczi def test_device_not_found(self): 163db58f9c0SStefan Hajnoczi result = self.vm.qmp('block-stream', device='nonexistent') 16437ce63ebSStefan Hajnoczi self.assert_qmp(result, 'error/class', 'DeviceNotFound') 16537ce63ebSStefan Hajnoczi 166774a8850SStefan Hajnoczi 167774a8850SStefan Hajnocziclass TestSmallerBackingFile(ImageStreamingTestCase): 168774a8850SStefan Hajnoczi backing_len = 1 * 1024 * 1024 # MB 169774a8850SStefan Hajnoczi image_len = 2 * backing_len 170774a8850SStefan Hajnoczi 171774a8850SStefan Hajnoczi def setUp(self): 172774a8850SStefan Hajnoczi self.create_image(backing_img, self.backing_len) 173774a8850SStefan Hajnoczi qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len)) 174774a8850SStefan Hajnoczi self.vm = iotests.VM().add_drive(test_img) 175774a8850SStefan Hajnoczi self.vm.launch() 176774a8850SStefan Hajnoczi 177774a8850SStefan Hajnoczi # If this hangs, then you are missing a fix to complete streaming when the 178774a8850SStefan Hajnoczi # end of the backing file is reached. 179774a8850SStefan Hajnoczi def test_stream(self): 180774a8850SStefan Hajnoczi self.assert_no_active_streams() 181774a8850SStefan Hajnoczi 182774a8850SStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0') 183774a8850SStefan Hajnoczi self.assert_qmp(result, 'return', {}) 184774a8850SStefan Hajnoczi 185774a8850SStefan Hajnoczi completed = False 186774a8850SStefan Hajnoczi while not completed: 187774a8850SStefan Hajnoczi for event in self.vm.get_qmp_events(wait=True): 188774a8850SStefan Hajnoczi if event['event'] == 'BLOCK_JOB_COMPLETED': 189774a8850SStefan Hajnoczi self.assert_qmp(event, 'data/type', 'stream') 190774a8850SStefan Hajnoczi self.assert_qmp(event, 'data/device', 'drive0') 191774a8850SStefan Hajnoczi self.assert_qmp(event, 'data/offset', self.image_len) 192774a8850SStefan Hajnoczi self.assert_qmp(event, 'data/len', self.image_len) 193774a8850SStefan Hajnoczi completed = True 194774a8850SStefan Hajnoczi 195774a8850SStefan Hajnoczi self.assert_no_active_streams() 196774a8850SStefan Hajnoczi self.vm.shutdown() 197774a8850SStefan Hajnoczi 198774a8850SStefan Hajnoczi 19937ce63ebSStefan Hajnocziclass TestStreamStop(ImageStreamingTestCase): 20037ce63ebSStefan Hajnoczi image_len = 8 * 1024 * 1024 * 1024 # GB 20137ce63ebSStefan Hajnoczi 20237ce63ebSStefan Hajnoczi def setUp(self): 20337ce63ebSStefan Hajnoczi qemu_img('create', backing_img, str(TestStreamStop.image_len)) 20437ce63ebSStefan Hajnoczi qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) 20537ce63ebSStefan Hajnoczi self.vm = iotests.VM().add_drive(test_img) 20637ce63ebSStefan Hajnoczi self.vm.launch() 20737ce63ebSStefan Hajnoczi 20837ce63ebSStefan Hajnoczi def tearDown(self): 20937ce63ebSStefan Hajnoczi self.vm.shutdown() 21037ce63ebSStefan Hajnoczi os.remove(test_img) 21137ce63ebSStefan Hajnoczi os.remove(backing_img) 21237ce63ebSStefan Hajnoczi 21337ce63ebSStefan Hajnoczi def test_stream_stop(self): 21437ce63ebSStefan Hajnoczi self.assert_no_active_streams() 21537ce63ebSStefan Hajnoczi 216db58f9c0SStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0') 21737ce63ebSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 21837ce63ebSStefan Hajnoczi 2190fd05e8dSPaolo Bonzini time.sleep(0.1) 22037ce63ebSStefan Hajnoczi events = self.vm.get_qmp_events(wait=False) 22137ce63ebSStefan Hajnoczi self.assertEqual(events, [], 'unexpected QMP event: %s' % events) 22237ce63ebSStefan Hajnoczi 223e425306aSStefan Hajnoczi self.cancel_and_wait() 22437ce63ebSStefan Hajnoczi 22537ce63ebSStefan Hajnocziclass TestSetSpeed(ImageStreamingTestCase): 22637ce63ebSStefan Hajnoczi image_len = 80 * 1024 * 1024 # MB 22737ce63ebSStefan Hajnoczi 22837ce63ebSStefan Hajnoczi def setUp(self): 22937ce63ebSStefan Hajnoczi qemu_img('create', backing_img, str(TestSetSpeed.image_len)) 23037ce63ebSStefan Hajnoczi qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) 23137ce63ebSStefan Hajnoczi self.vm = iotests.VM().add_drive(test_img) 23237ce63ebSStefan Hajnoczi self.vm.launch() 23337ce63ebSStefan Hajnoczi 23437ce63ebSStefan Hajnoczi def tearDown(self): 23537ce63ebSStefan Hajnoczi self.vm.shutdown() 23637ce63ebSStefan Hajnoczi os.remove(test_img) 23737ce63ebSStefan Hajnoczi os.remove(backing_img) 23837ce63ebSStefan Hajnoczi 239e425306aSStefan Hajnoczi # This is a short performance test which is not run by default. 240e425306aSStefan Hajnoczi # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput" 241e425306aSStefan Hajnoczi def perf_test_throughput(self): 24237ce63ebSStefan Hajnoczi self.assert_no_active_streams() 24337ce63ebSStefan Hajnoczi 244db58f9c0SStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0') 24537ce63ebSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 24637ce63ebSStefan Hajnoczi 247e425306aSStefan Hajnoczi result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 24837ce63ebSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 24937ce63ebSStefan Hajnoczi 25037ce63ebSStefan Hajnoczi completed = False 25137ce63ebSStefan Hajnoczi while not completed: 25237ce63ebSStefan Hajnoczi for event in self.vm.get_qmp_events(wait=True): 25337ce63ebSStefan Hajnoczi if event['event'] == 'BLOCK_JOB_COMPLETED': 25437ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/type', 'stream') 25537ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/device', 'drive0') 25637ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/offset', self.image_len) 25737ce63ebSStefan Hajnoczi self.assert_qmp(event, 'data/len', self.image_len) 25837ce63ebSStefan Hajnoczi completed = True 25937ce63ebSStefan Hajnoczi 26037ce63ebSStefan Hajnoczi self.assert_no_active_streams() 26137ce63ebSStefan Hajnoczi 262e425306aSStefan Hajnoczi def test_set_speed(self): 263e425306aSStefan Hajnoczi self.assert_no_active_streams() 264e425306aSStefan Hajnoczi 265e425306aSStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0') 266e425306aSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 267e425306aSStefan Hajnoczi 268e425306aSStefan Hajnoczi # Default speed is 0 269e425306aSStefan Hajnoczi result = self.vm.qmp('query-block-jobs') 270e425306aSStefan Hajnoczi self.assert_qmp(result, 'return[0]/device', 'drive0') 271e425306aSStefan Hajnoczi self.assert_qmp(result, 'return[0]/speed', 0) 272e425306aSStefan Hajnoczi 273e425306aSStefan Hajnoczi result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) 274e425306aSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 275e425306aSStefan Hajnoczi 276e425306aSStefan Hajnoczi # Ensure the speed we set was accepted 277e425306aSStefan Hajnoczi result = self.vm.qmp('query-block-jobs') 278e425306aSStefan Hajnoczi self.assert_qmp(result, 'return[0]/device', 'drive0') 279e425306aSStefan Hajnoczi self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) 280e425306aSStefan Hajnoczi 281e425306aSStefan Hajnoczi self.cancel_and_wait() 282e425306aSStefan Hajnoczi 283e425306aSStefan Hajnoczi # Check setting speed in block-stream works 284e425306aSStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024) 285e425306aSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 286e425306aSStefan Hajnoczi 287e425306aSStefan Hajnoczi result = self.vm.qmp('query-block-jobs') 288e425306aSStefan Hajnoczi self.assert_qmp(result, 'return[0]/device', 'drive0') 289e425306aSStefan Hajnoczi self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) 290e425306aSStefan Hajnoczi 291e425306aSStefan Hajnoczi self.cancel_and_wait() 292e425306aSStefan Hajnoczi 293e425306aSStefan Hajnoczi def test_set_speed_invalid(self): 294e425306aSStefan Hajnoczi self.assert_no_active_streams() 295e425306aSStefan Hajnoczi 296e425306aSStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0', speed=-1) 29758c8cce2SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 298e425306aSStefan Hajnoczi 299e425306aSStefan Hajnoczi self.assert_no_active_streams() 300e425306aSStefan Hajnoczi 301e425306aSStefan Hajnoczi result = self.vm.qmp('block-stream', device='drive0') 302e425306aSStefan Hajnoczi self.assert_qmp(result, 'return', {}) 303e425306aSStefan Hajnoczi 304e425306aSStefan Hajnoczi result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) 30558c8cce2SKevin Wolf self.assert_qmp(result, 'error/class', 'GenericError') 306e425306aSStefan Hajnoczi 307e425306aSStefan Hajnoczi self.cancel_and_wait() 308e425306aSStefan Hajnoczi 30937ce63ebSStefan Hajnocziif __name__ == '__main__': 31037ce63ebSStefan Hajnoczi iotests.main(supported_fmts=['qcow2', 'qed']) 311