xref: /openbmc/qemu/tests/qemu-iotests/030 (revision d34b867d)
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