]>
Commit | Line | Data |
---|---|---|
37ce63eb SH |
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 | ||
21 | import os | |
22 | import iotests | |
23 | from iotests import qemu_img, qemu_io | |
ab68cdfa | 24 | import struct |
37ce63eb SH |
25 | |
26 | backing_img = os.path.join(iotests.test_dir, 'backing.img') | |
6e343609 | 27 | mid_img = os.path.join(iotests.test_dir, 'mid.img') |
37ce63eb SH |
28 | test_img = os.path.join(iotests.test_dir, 'test.img') |
29 | ||
30 | class 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 | ||
e425306a SH |
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 | ||
ab68cdfa PB |
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 | ||
37ce63eb SH |
62 | class TestSingleDrive(ImageStreamingTestCase): |
63 | image_len = 1 * 1024 * 1024 # MB | |
64 | ||
65 | def setUp(self): | |
ab68cdfa | 66 | self.create_image(backing_img, TestSingleDrive.image_len) |
6e343609 PB |
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) | |
37ce63eb SH |
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) | |
6e343609 | 75 | os.remove(mid_img) |
37ce63eb SH |
76 | os.remove(backing_img) |
77 | ||
78 | def test_stream(self): | |
79 | self.assert_no_active_streams() | |
80 | ||
db58f9c0 | 81 | result = self.vm.qmp('block-stream', device='drive0') |
37ce63eb SH |
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() | |
863a5d04 | 95 | self.vm.shutdown() |
37ce63eb | 96 | |
efcc7a23 PB |
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') | |
37ce63eb | 100 | |
6e343609 PB |
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 | ||
37ce63eb | 124 | def test_device_not_found(self): |
db58f9c0 | 125 | result = self.vm.qmp('block-stream', device='nonexistent') |
37ce63eb SH |
126 | self.assert_qmp(result, 'error/class', 'DeviceNotFound') |
127 | ||
128 | class TestStreamStop(ImageStreamingTestCase): | |
129 | image_len = 8 * 1024 * 1024 * 1024 # GB | |
130 | ||
131 | def setUp(self): | |
132 | qemu_img('create', backing_img, str(TestStreamStop.image_len)) | |
133 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
134 | self.vm = iotests.VM().add_drive(test_img) | |
135 | self.vm.launch() | |
136 | ||
137 | def tearDown(self): | |
138 | self.vm.shutdown() | |
139 | os.remove(test_img) | |
140 | os.remove(backing_img) | |
141 | ||
142 | def test_stream_stop(self): | |
143 | import time | |
144 | ||
145 | self.assert_no_active_streams() | |
146 | ||
db58f9c0 | 147 | result = self.vm.qmp('block-stream', device='drive0') |
37ce63eb SH |
148 | self.assert_qmp(result, 'return', {}) |
149 | ||
0fd05e8d | 150 | time.sleep(0.1) |
37ce63eb SH |
151 | events = self.vm.get_qmp_events(wait=False) |
152 | self.assertEqual(events, [], 'unexpected QMP event: %s' % events) | |
153 | ||
e425306a | 154 | self.cancel_and_wait() |
37ce63eb | 155 | |
37ce63eb SH |
156 | class TestSetSpeed(ImageStreamingTestCase): |
157 | image_len = 80 * 1024 * 1024 # MB | |
158 | ||
159 | def setUp(self): | |
160 | qemu_img('create', backing_img, str(TestSetSpeed.image_len)) | |
161 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
162 | self.vm = iotests.VM().add_drive(test_img) | |
163 | self.vm.launch() | |
164 | ||
165 | def tearDown(self): | |
166 | self.vm.shutdown() | |
167 | os.remove(test_img) | |
168 | os.remove(backing_img) | |
169 | ||
e425306a SH |
170 | # This is a short performance test which is not run by default. |
171 | # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput" | |
172 | def perf_test_throughput(self): | |
37ce63eb SH |
173 | self.assert_no_active_streams() |
174 | ||
db58f9c0 | 175 | result = self.vm.qmp('block-stream', device='drive0') |
37ce63eb SH |
176 | self.assert_qmp(result, 'return', {}) |
177 | ||
e425306a | 178 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) |
37ce63eb SH |
179 | self.assert_qmp(result, 'return', {}) |
180 | ||
181 | completed = False | |
182 | while not completed: | |
183 | for event in self.vm.get_qmp_events(wait=True): | |
184 | if event['event'] == 'BLOCK_JOB_COMPLETED': | |
185 | self.assert_qmp(event, 'data/type', 'stream') | |
186 | self.assert_qmp(event, 'data/device', 'drive0') | |
187 | self.assert_qmp(event, 'data/offset', self.image_len) | |
188 | self.assert_qmp(event, 'data/len', self.image_len) | |
189 | completed = True | |
190 | ||
191 | self.assert_no_active_streams() | |
192 | ||
e425306a SH |
193 | def test_set_speed(self): |
194 | self.assert_no_active_streams() | |
195 | ||
196 | result = self.vm.qmp('block-stream', device='drive0') | |
197 | self.assert_qmp(result, 'return', {}) | |
198 | ||
199 | # Default speed is 0 | |
200 | result = self.vm.qmp('query-block-jobs') | |
201 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
202 | self.assert_qmp(result, 'return[0]/speed', 0) | |
203 | ||
204 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) | |
205 | self.assert_qmp(result, 'return', {}) | |
206 | ||
207 | # Ensure the speed we set was accepted | |
208 | result = self.vm.qmp('query-block-jobs') | |
209 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
210 | self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) | |
211 | ||
212 | self.cancel_and_wait() | |
213 | ||
214 | # Check setting speed in block-stream works | |
215 | result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024) | |
216 | self.assert_qmp(result, 'return', {}) | |
217 | ||
218 | result = self.vm.qmp('query-block-jobs') | |
219 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
220 | self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) | |
221 | ||
222 | self.cancel_and_wait() | |
223 | ||
224 | def test_set_speed_invalid(self): | |
225 | self.assert_no_active_streams() | |
226 | ||
227 | result = self.vm.qmp('block-stream', device='drive0', speed=-1) | |
228 | self.assert_qmp(result, 'error/class', 'InvalidParameter') | |
229 | self.assert_qmp(result, 'error/data/name', 'speed') | |
230 | ||
231 | self.assert_no_active_streams() | |
232 | ||
233 | result = self.vm.qmp('block-stream', device='drive0') | |
234 | self.assert_qmp(result, 'return', {}) | |
235 | ||
236 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) | |
237 | self.assert_qmp(result, 'error/class', 'InvalidParameter') | |
238 | self.assert_qmp(result, 'error/data/name', 'speed') | |
239 | ||
240 | self.cancel_and_wait() | |
241 | ||
37ce63eb SH |
242 | if __name__ == '__main__': |
243 | iotests.main(supported_fmts=['qcow2', 'qed']) |