xref: /openbmc/qemu/tests/qemu-iotests/030 (revision 774a8850)
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
24import struct
25
26backing_img = os.path.join(iotests.test_dir, 'backing.img')
27mid_img = os.path.join(iotests.test_dir, 'mid.img')
28test_img = os.path.join(iotests.test_dir, 'test.img')
29
30class ImageStreamingTestCase(iotests.QMPTestCase):
31    '''Abstract base class for image streaming test cases'''
32
33    def assert_no_active_streams(self):
34        result = self.vm.qmp('query-block-jobs')
35        self.assert_qmp(result, 'return', [])
36
37    def cancel_and_wait(self, drive='drive0'):
38        '''Cancel a block job and wait for it to finish'''
39        result = self.vm.qmp('block-job-cancel', device=drive)
40        self.assert_qmp(result, 'return', {})
41
42        cancelled = False
43        while not cancelled:
44            for event in self.vm.get_qmp_events(wait=True):
45                if event['event'] == 'BLOCK_JOB_CANCELLED':
46                    self.assert_qmp(event, 'data/type', 'stream')
47                    self.assert_qmp(event, 'data/device', drive)
48                    cancelled = True
49
50        self.assert_no_active_streams()
51
52    def create_image(self, name, size):
53        file = open(name, 'w')
54        i = 0
55        while i < size:
56            sector = struct.pack('>l504xl', i / 512, i / 512)
57            file.write(sector)
58            i = i + 512
59        file.close()
60
61
62class TestSingleDrive(ImageStreamingTestCase):
63    image_len = 1 * 1024 * 1024 # MB
64
65    def setUp(self):
66        self.create_image(backing_img, TestSingleDrive.image_len)
67        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
68        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
69        self.vm = iotests.VM().add_drive(test_img)
70        self.vm.launch()
71
72    def tearDown(self):
73        self.vm.shutdown()
74        os.remove(test_img)
75        os.remove(mid_img)
76        os.remove(backing_img)
77
78    def test_stream(self):
79        self.assert_no_active_streams()
80
81        result = self.vm.qmp('block-stream', device='drive0')
82        self.assert_qmp(result, 'return', {})
83
84        completed = False
85        while not completed:
86            for event in self.vm.get_qmp_events(wait=True):
87                if event['event'] == 'BLOCK_JOB_COMPLETED':
88                    self.assert_qmp(event, 'data/type', 'stream')
89                    self.assert_qmp(event, 'data/device', 'drive0')
90                    self.assert_qmp(event, 'data/offset', self.image_len)
91                    self.assert_qmp(event, 'data/len', self.image_len)
92                    completed = True
93
94        self.assert_no_active_streams()
95        self.vm.shutdown()
96
97        self.assertEqual(qemu_io('-c', 'map', backing_img),
98                         qemu_io('-c', 'map', test_img),
99                         'image file map does not match backing file after streaming')
100
101    def test_stream_partial(self):
102        self.assert_no_active_streams()
103
104        result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
105        self.assert_qmp(result, 'return', {})
106
107        completed = False
108        while not completed:
109            for event in self.vm.get_qmp_events(wait=True):
110                if event['event'] == 'BLOCK_JOB_COMPLETED':
111                    self.assert_qmp(event, 'data/type', 'stream')
112                    self.assert_qmp(event, 'data/device', 'drive0')
113                    self.assert_qmp(event, 'data/offset', self.image_len)
114                    self.assert_qmp(event, 'data/len', self.image_len)
115                    completed = True
116
117        self.assert_no_active_streams()
118        self.vm.shutdown()
119
120        self.assertEqual(qemu_io('-c', 'map', mid_img),
121                         qemu_io('-c', 'map', test_img),
122                         'image file map does not match backing file after streaming')
123
124    def test_device_not_found(self):
125        result = self.vm.qmp('block-stream', device='nonexistent')
126        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
127
128
129class TestSmallerBackingFile(ImageStreamingTestCase):
130    backing_len = 1 * 1024 * 1024 # MB
131    image_len = 2 * backing_len
132
133    def setUp(self):
134        self.create_image(backing_img, self.backing_len)
135        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
136        self.vm = iotests.VM().add_drive(test_img)
137        self.vm.launch()
138
139    # If this hangs, then you are missing a fix to complete streaming when the
140    # end of the backing file is reached.
141    def test_stream(self):
142        self.assert_no_active_streams()
143
144        result = self.vm.qmp('block-stream', device='drive0')
145        self.assert_qmp(result, 'return', {})
146
147        completed = False
148        while not completed:
149            for event in self.vm.get_qmp_events(wait=True):
150                if event['event'] == 'BLOCK_JOB_COMPLETED':
151                    self.assert_qmp(event, 'data/type', 'stream')
152                    self.assert_qmp(event, 'data/device', 'drive0')
153                    self.assert_qmp(event, 'data/offset', self.image_len)
154                    self.assert_qmp(event, 'data/len', self.image_len)
155                    completed = True
156
157        self.assert_no_active_streams()
158        self.vm.shutdown()
159
160
161class TestStreamStop(ImageStreamingTestCase):
162    image_len = 8 * 1024 * 1024 * 1024 # GB
163
164    def setUp(self):
165        qemu_img('create', backing_img, str(TestStreamStop.image_len))
166        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
167        self.vm = iotests.VM().add_drive(test_img)
168        self.vm.launch()
169
170    def tearDown(self):
171        self.vm.shutdown()
172        os.remove(test_img)
173        os.remove(backing_img)
174
175    def test_stream_stop(self):
176        import time
177
178        self.assert_no_active_streams()
179
180        result = self.vm.qmp('block-stream', device='drive0')
181        self.assert_qmp(result, 'return', {})
182
183        time.sleep(0.1)
184        events = self.vm.get_qmp_events(wait=False)
185        self.assertEqual(events, [], 'unexpected QMP event: %s' % events)
186
187        self.cancel_and_wait()
188
189class TestSetSpeed(ImageStreamingTestCase):
190    image_len = 80 * 1024 * 1024 # MB
191
192    def setUp(self):
193        qemu_img('create', backing_img, str(TestSetSpeed.image_len))
194        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
195        self.vm = iotests.VM().add_drive(test_img)
196        self.vm.launch()
197
198    def tearDown(self):
199        self.vm.shutdown()
200        os.remove(test_img)
201        os.remove(backing_img)
202
203    # This is a short performance test which is not run by default.
204    # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
205    def perf_test_throughput(self):
206        self.assert_no_active_streams()
207
208        result = self.vm.qmp('block-stream', device='drive0')
209        self.assert_qmp(result, 'return', {})
210
211        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
212        self.assert_qmp(result, 'return', {})
213
214        completed = False
215        while not completed:
216            for event in self.vm.get_qmp_events(wait=True):
217                if event['event'] == 'BLOCK_JOB_COMPLETED':
218                    self.assert_qmp(event, 'data/type', 'stream')
219                    self.assert_qmp(event, 'data/device', 'drive0')
220                    self.assert_qmp(event, 'data/offset', self.image_len)
221                    self.assert_qmp(event, 'data/len', self.image_len)
222                    completed = True
223
224        self.assert_no_active_streams()
225
226    def test_set_speed(self):
227        self.assert_no_active_streams()
228
229        result = self.vm.qmp('block-stream', device='drive0')
230        self.assert_qmp(result, 'return', {})
231
232        # Default speed is 0
233        result = self.vm.qmp('query-block-jobs')
234        self.assert_qmp(result, 'return[0]/device', 'drive0')
235        self.assert_qmp(result, 'return[0]/speed', 0)
236
237        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
238        self.assert_qmp(result, 'return', {})
239
240        # Ensure the speed we set was accepted
241        result = self.vm.qmp('query-block-jobs')
242        self.assert_qmp(result, 'return[0]/device', 'drive0')
243        self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
244
245        self.cancel_and_wait()
246
247        # Check setting speed in block-stream works
248        result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024)
249        self.assert_qmp(result, 'return', {})
250
251        result = self.vm.qmp('query-block-jobs')
252        self.assert_qmp(result, 'return[0]/device', 'drive0')
253        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
254
255        self.cancel_and_wait()
256
257    def test_set_speed_invalid(self):
258        self.assert_no_active_streams()
259
260        result = self.vm.qmp('block-stream', device='drive0', speed=-1)
261        self.assert_qmp(result, 'error/class', 'GenericError')
262
263        self.assert_no_active_streams()
264
265        result = self.vm.qmp('block-stream', device='drive0')
266        self.assert_qmp(result, 'return', {})
267
268        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
269        self.assert_qmp(result, 'error/class', 'GenericError')
270
271        self.cancel_and_wait()
272
273if __name__ == '__main__':
274    iotests.main(supported_fmts=['qcow2', 'qed'])
275