root / tests / qemu-iotests / 030 @ b59b3d57
History | View | Annotate | Download (19.6 kB)
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 time |
22 |
import os |
23 |
import iotests |
24 |
from iotests import qemu_img, qemu_io |
25 |
|
26 |
backing_img = os.path.join(iotests.test_dir, 'backing.img') |
27 |
mid_img = os.path.join(iotests.test_dir, 'mid.img') |
28 |
test_img = os.path.join(iotests.test_dir, 'test.img') |
29 |
|
30 |
class TestSingleDrive(iotests.QMPTestCase): |
31 |
image_len = 1 * 1024 * 1024 # MB |
32 |
|
33 |
def setUp(self): |
34 |
iotests.create_image(backing_img, TestSingleDrive.image_len) |
35 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) |
36 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) |
37 |
qemu_io('-c', 'write -P 0x1 0 512', backing_img) |
38 |
self.vm = iotests.VM().add_drive(test_img) |
39 |
self.vm.launch() |
40 |
|
41 |
def tearDown(self): |
42 |
self.vm.shutdown() |
43 |
os.remove(test_img) |
44 |
os.remove(mid_img) |
45 |
os.remove(backing_img) |
46 |
|
47 |
def test_stream(self): |
48 |
self.assert_no_active_block_jobs() |
49 |
|
50 |
result = self.vm.qmp('block-stream', device='drive0') |
51 |
self.assert_qmp(result, 'return', {}) |
52 |
|
53 |
completed = False |
54 |
while not completed: |
55 |
for event in self.vm.get_qmp_events(wait=True): |
56 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
57 |
self.assert_qmp(event, 'data/type', 'stream') |
58 |
self.assert_qmp(event, 'data/device', 'drive0') |
59 |
self.assert_qmp(event, 'data/offset', self.image_len) |
60 |
self.assert_qmp(event, 'data/len', self.image_len) |
61 |
completed = True |
62 |
|
63 |
self.assert_no_active_block_jobs() |
64 |
self.vm.shutdown() |
65 |
|
66 |
self.assertEqual(qemu_io('-c', 'map', backing_img), |
67 |
qemu_io('-c', 'map', test_img), |
68 |
'image file map does not match backing file after streaming') |
69 |
|
70 |
def test_stream_pause(self): |
71 |
self.assert_no_active_block_jobs() |
72 |
|
73 |
self.vm.pause_drive('drive0') |
74 |
result = self.vm.qmp('block-stream', device='drive0') |
75 |
self.assert_qmp(result, 'return', {}) |
76 |
|
77 |
result = self.vm.qmp('block-job-pause', device='drive0') |
78 |
self.assert_qmp(result, 'return', {}) |
79 |
|
80 |
time.sleep(1) |
81 |
result = self.vm.qmp('query-block-jobs') |
82 |
offset = self.dictpath(result, 'return[0]/offset') |
83 |
|
84 |
time.sleep(1) |
85 |
result = self.vm.qmp('query-block-jobs') |
86 |
self.assert_qmp(result, 'return[0]/offset', offset) |
87 |
|
88 |
result = self.vm.qmp('block-job-resume', device='drive0') |
89 |
self.assert_qmp(result, 'return', {}) |
90 |
|
91 |
self.vm.resume_drive('drive0') |
92 |
completed = False |
93 |
while not completed: |
94 |
for event in self.vm.get_qmp_events(wait=True): |
95 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
96 |
self.assert_qmp(event, 'data/type', 'stream') |
97 |
self.assert_qmp(event, 'data/device', 'drive0') |
98 |
self.assert_qmp(event, 'data/offset', self.image_len) |
99 |
self.assert_qmp(event, 'data/len', self.image_len) |
100 |
completed = True |
101 |
|
102 |
self.assert_no_active_block_jobs() |
103 |
self.vm.shutdown() |
104 |
|
105 |
self.assertEqual(qemu_io('-c', 'map', backing_img), |
106 |
qemu_io('-c', 'map', test_img), |
107 |
'image file map does not match backing file after streaming') |
108 |
|
109 |
def test_stream_partial(self): |
110 |
self.assert_no_active_block_jobs() |
111 |
|
112 |
result = self.vm.qmp('block-stream', device='drive0', base=mid_img) |
113 |
self.assert_qmp(result, 'return', {}) |
114 |
|
115 |
completed = False |
116 |
while not completed: |
117 |
for event in self.vm.get_qmp_events(wait=True): |
118 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
119 |
self.assert_qmp(event, 'data/type', 'stream') |
120 |
self.assert_qmp(event, 'data/device', 'drive0') |
121 |
self.assert_qmp(event, 'data/offset', self.image_len) |
122 |
self.assert_qmp(event, 'data/len', self.image_len) |
123 |
completed = True |
124 |
|
125 |
self.assert_no_active_block_jobs() |
126 |
self.vm.shutdown() |
127 |
|
128 |
self.assertEqual(qemu_io('-c', 'map', mid_img), |
129 |
qemu_io('-c', 'map', test_img), |
130 |
'image file map does not match backing file after streaming') |
131 |
|
132 |
def test_device_not_found(self): |
133 |
result = self.vm.qmp('block-stream', device='nonexistent') |
134 |
self.assert_qmp(result, 'error/class', 'DeviceNotFound') |
135 |
|
136 |
|
137 |
class TestSmallerBackingFile(iotests.QMPTestCase): |
138 |
backing_len = 1 * 1024 * 1024 # MB |
139 |
image_len = 2 * backing_len |
140 |
|
141 |
def setUp(self): |
142 |
iotests.create_image(backing_img, self.backing_len) |
143 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len)) |
144 |
self.vm = iotests.VM().add_drive(test_img) |
145 |
self.vm.launch() |
146 |
|
147 |
# If this hangs, then you are missing a fix to complete streaming when the |
148 |
# end of the backing file is reached. |
149 |
def test_stream(self): |
150 |
self.assert_no_active_block_jobs() |
151 |
|
152 |
result = self.vm.qmp('block-stream', device='drive0') |
153 |
self.assert_qmp(result, 'return', {}) |
154 |
|
155 |
completed = False |
156 |
while not completed: |
157 |
for event in self.vm.get_qmp_events(wait=True): |
158 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
159 |
self.assert_qmp(event, 'data/type', 'stream') |
160 |
self.assert_qmp(event, 'data/device', 'drive0') |
161 |
self.assert_qmp(event, 'data/offset', self.image_len) |
162 |
self.assert_qmp(event, 'data/len', self.image_len) |
163 |
completed = True |
164 |
|
165 |
self.assert_no_active_block_jobs() |
166 |
self.vm.shutdown() |
167 |
|
168 |
class TestErrors(iotests.QMPTestCase): |
169 |
image_len = 2 * 1024 * 1024 # MB |
170 |
|
171 |
# this should match STREAM_BUFFER_SIZE/512 in block/stream.c |
172 |
STREAM_BUFFER_SIZE = 512 * 1024 |
173 |
|
174 |
def create_blkdebug_file(self, name, event, errno): |
175 |
file = open(name, 'w') |
176 |
file.write(''' |
177 |
[inject-error] |
178 |
state = "1" |
179 |
event = "%s" |
180 |
errno = "%d" |
181 |
immediately = "off" |
182 |
once = "on" |
183 |
sector = "%d" |
184 |
|
185 |
[set-state] |
186 |
state = "1" |
187 |
event = "%s" |
188 |
new_state = "2" |
189 |
|
190 |
[set-state] |
191 |
state = "2" |
192 |
event = "%s" |
193 |
new_state = "1" |
194 |
''' % (event, errno, self.STREAM_BUFFER_SIZE / 512, event, event)) |
195 |
file.close() |
196 |
|
197 |
class TestEIO(TestErrors): |
198 |
def setUp(self): |
199 |
self.blkdebug_file = backing_img + ".blkdebug" |
200 |
iotests.create_image(backing_img, TestErrors.image_len) |
201 |
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) |
202 |
qemu_img('create', '-f', iotests.imgfmt, |
203 |
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' |
204 |
% (self.blkdebug_file, backing_img), |
205 |
test_img) |
206 |
self.vm = iotests.VM().add_drive(test_img) |
207 |
self.vm.launch() |
208 |
|
209 |
def tearDown(self): |
210 |
self.vm.shutdown() |
211 |
os.remove(test_img) |
212 |
os.remove(backing_img) |
213 |
os.remove(self.blkdebug_file) |
214 |
|
215 |
def test_report(self): |
216 |
self.assert_no_active_block_jobs() |
217 |
|
218 |
result = self.vm.qmp('block-stream', device='drive0') |
219 |
self.assert_qmp(result, 'return', {}) |
220 |
|
221 |
completed = False |
222 |
error = False |
223 |
while not completed: |
224 |
for event in self.vm.get_qmp_events(wait=True): |
225 |
if event['event'] == 'BLOCK_JOB_ERROR': |
226 |
self.assert_qmp(event, 'data/device', 'drive0') |
227 |
self.assert_qmp(event, 'data/operation', 'read') |
228 |
error = True |
229 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
230 |
self.assertTrue(error, 'job completed unexpectedly') |
231 |
self.assert_qmp(event, 'data/type', 'stream') |
232 |
self.assert_qmp(event, 'data/device', 'drive0') |
233 |
self.assert_qmp(event, 'data/error', 'Input/output error') |
234 |
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) |
235 |
self.assert_qmp(event, 'data/len', self.image_len) |
236 |
completed = True |
237 |
|
238 |
self.assert_no_active_block_jobs() |
239 |
self.vm.shutdown() |
240 |
|
241 |
def test_ignore(self): |
242 |
self.assert_no_active_block_jobs() |
243 |
|
244 |
result = self.vm.qmp('block-stream', device='drive0', on_error='ignore') |
245 |
self.assert_qmp(result, 'return', {}) |
246 |
|
247 |
error = False |
248 |
completed = False |
249 |
while not completed: |
250 |
for event in self.vm.get_qmp_events(wait=True): |
251 |
if event['event'] == 'BLOCK_JOB_ERROR': |
252 |
self.assert_qmp(event, 'data/device', 'drive0') |
253 |
self.assert_qmp(event, 'data/operation', 'read') |
254 |
result = self.vm.qmp('query-block-jobs') |
255 |
self.assert_qmp(result, 'return[0]/paused', False) |
256 |
error = True |
257 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
258 |
self.assertTrue(error, 'job completed unexpectedly') |
259 |
self.assert_qmp(event, 'data/type', 'stream') |
260 |
self.assert_qmp(event, 'data/device', 'drive0') |
261 |
self.assert_qmp(event, 'data/error', 'Input/output error') |
262 |
self.assert_qmp(event, 'data/offset', self.image_len) |
263 |
self.assert_qmp(event, 'data/len', self.image_len) |
264 |
completed = True |
265 |
|
266 |
self.assert_no_active_block_jobs() |
267 |
self.vm.shutdown() |
268 |
|
269 |
def test_stop(self): |
270 |
self.assert_no_active_block_jobs() |
271 |
|
272 |
result = self.vm.qmp('block-stream', device='drive0', on_error='stop') |
273 |
self.assert_qmp(result, 'return', {}) |
274 |
|
275 |
error = False |
276 |
completed = False |
277 |
while not completed: |
278 |
for event in self.vm.get_qmp_events(wait=True): |
279 |
if event['event'] == 'BLOCK_JOB_ERROR': |
280 |
self.assert_qmp(event, 'data/device', 'drive0') |
281 |
self.assert_qmp(event, 'data/operation', 'read') |
282 |
|
283 |
result = self.vm.qmp('query-block-jobs') |
284 |
self.assert_qmp(result, 'return[0]/paused', True) |
285 |
self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) |
286 |
self.assert_qmp(result, 'return[0]/io-status', 'failed') |
287 |
|
288 |
result = self.vm.qmp('block-job-resume', device='drive0') |
289 |
self.assert_qmp(result, 'return', {}) |
290 |
|
291 |
result = self.vm.qmp('query-block-jobs') |
292 |
self.assert_qmp(result, 'return[0]/paused', False) |
293 |
self.assert_qmp(result, 'return[0]/io-status', 'ok') |
294 |
error = True |
295 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
296 |
self.assertTrue(error, 'job completed unexpectedly') |
297 |
self.assert_qmp(event, 'data/type', 'stream') |
298 |
self.assert_qmp(event, 'data/device', 'drive0') |
299 |
self.assert_qmp_absent(event, 'data/error') |
300 |
self.assert_qmp(event, 'data/offset', self.image_len) |
301 |
self.assert_qmp(event, 'data/len', self.image_len) |
302 |
completed = True |
303 |
|
304 |
self.assert_no_active_block_jobs() |
305 |
self.vm.shutdown() |
306 |
|
307 |
def test_enospc(self): |
308 |
self.assert_no_active_block_jobs() |
309 |
|
310 |
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') |
311 |
self.assert_qmp(result, 'return', {}) |
312 |
|
313 |
completed = False |
314 |
error = False |
315 |
while not completed: |
316 |
for event in self.vm.get_qmp_events(wait=True): |
317 |
if event['event'] == 'BLOCK_JOB_ERROR': |
318 |
self.assert_qmp(event, 'data/device', 'drive0') |
319 |
self.assert_qmp(event, 'data/operation', 'read') |
320 |
error = True |
321 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
322 |
self.assertTrue(error, 'job completed unexpectedly') |
323 |
self.assert_qmp(event, 'data/type', 'stream') |
324 |
self.assert_qmp(event, 'data/device', 'drive0') |
325 |
self.assert_qmp(event, 'data/error', 'Input/output error') |
326 |
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) |
327 |
self.assert_qmp(event, 'data/len', self.image_len) |
328 |
completed = True |
329 |
|
330 |
self.assert_no_active_block_jobs() |
331 |
self.vm.shutdown() |
332 |
|
333 |
class TestENOSPC(TestErrors): |
334 |
def setUp(self): |
335 |
self.blkdebug_file = backing_img + ".blkdebug" |
336 |
iotests.create_image(backing_img, TestErrors.image_len) |
337 |
self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28) |
338 |
qemu_img('create', '-f', iotests.imgfmt, |
339 |
'-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' |
340 |
% (self.blkdebug_file, backing_img), |
341 |
test_img) |
342 |
self.vm = iotests.VM().add_drive(test_img) |
343 |
self.vm.launch() |
344 |
|
345 |
def tearDown(self): |
346 |
self.vm.shutdown() |
347 |
os.remove(test_img) |
348 |
os.remove(backing_img) |
349 |
os.remove(self.blkdebug_file) |
350 |
|
351 |
def test_enospc(self): |
352 |
self.assert_no_active_block_jobs() |
353 |
|
354 |
result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') |
355 |
self.assert_qmp(result, 'return', {}) |
356 |
|
357 |
error = False |
358 |
completed = False |
359 |
while not completed: |
360 |
for event in self.vm.get_qmp_events(wait=True): |
361 |
if event['event'] == 'BLOCK_JOB_ERROR': |
362 |
self.assert_qmp(event, 'data/device', 'drive0') |
363 |
self.assert_qmp(event, 'data/operation', 'read') |
364 |
|
365 |
result = self.vm.qmp('query-block-jobs') |
366 |
self.assert_qmp(result, 'return[0]/paused', True) |
367 |
self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) |
368 |
self.assert_qmp(result, 'return[0]/io-status', 'nospace') |
369 |
|
370 |
result = self.vm.qmp('block-job-resume', device='drive0') |
371 |
self.assert_qmp(result, 'return', {}) |
372 |
|
373 |
result = self.vm.qmp('query-block-jobs') |
374 |
self.assert_qmp(result, 'return[0]/paused', False) |
375 |
self.assert_qmp(result, 'return[0]/io-status', 'ok') |
376 |
error = True |
377 |
elif event['event'] == 'BLOCK_JOB_COMPLETED': |
378 |
self.assertTrue(error, 'job completed unexpectedly') |
379 |
self.assert_qmp(event, 'data/type', 'stream') |
380 |
self.assert_qmp(event, 'data/device', 'drive0') |
381 |
self.assert_qmp_absent(event, 'data/error') |
382 |
self.assert_qmp(event, 'data/offset', self.image_len) |
383 |
self.assert_qmp(event, 'data/len', self.image_len) |
384 |
completed = True |
385 |
|
386 |
self.assert_no_active_block_jobs() |
387 |
self.vm.shutdown() |
388 |
|
389 |
class TestStreamStop(iotests.QMPTestCase): |
390 |
image_len = 8 * 1024 * 1024 * 1024 # GB |
391 |
|
392 |
def setUp(self): |
393 |
qemu_img('create', backing_img, str(TestStreamStop.image_len)) |
394 |
qemu_io('-c', 'write -P 0x1 0 32M', backing_img) |
395 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
396 |
qemu_io('-c', 'write -P 0x1 32M 32M', test_img) |
397 |
self.vm = iotests.VM().add_drive("blkdebug::" + test_img) |
398 |
self.vm.launch() |
399 |
|
400 |
def tearDown(self): |
401 |
self.vm.shutdown() |
402 |
os.remove(test_img) |
403 |
os.remove(backing_img) |
404 |
|
405 |
def test_stream_stop(self): |
406 |
self.assert_no_active_block_jobs() |
407 |
|
408 |
self.vm.pause_drive('drive0') |
409 |
result = self.vm.qmp('block-stream', device='drive0') |
410 |
self.assert_qmp(result, 'return', {}) |
411 |
|
412 |
time.sleep(0.1) |
413 |
events = self.vm.get_qmp_events(wait=False) |
414 |
self.assertEqual(events, [], 'unexpected QMP event: %s' % events) |
415 |
|
416 |
self.cancel_and_wait(resume=True) |
417 |
|
418 |
class TestSetSpeed(iotests.QMPTestCase): |
419 |
image_len = 80 * 1024 * 1024 # MB |
420 |
|
421 |
def setUp(self): |
422 |
qemu_img('create', backing_img, str(TestSetSpeed.image_len)) |
423 |
qemu_io('-c', 'write -P 0x1 0 32M', backing_img) |
424 |
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
425 |
qemu_io('-c', 'write -P 0x1 32M 32M', test_img) |
426 |
self.vm = iotests.VM().add_drive('blkdebug::' + test_img) |
427 |
self.vm.launch() |
428 |
|
429 |
def tearDown(self): |
430 |
self.vm.shutdown() |
431 |
os.remove(test_img) |
432 |
os.remove(backing_img) |
433 |
|
434 |
# This is a short performance test which is not run by default. |
435 |
# Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput" |
436 |
def perf_test_throughput(self): |
437 |
self.assert_no_active_block_jobs() |
438 |
|
439 |
result = self.vm.qmp('block-stream', device='drive0') |
440 |
self.assert_qmp(result, 'return', {}) |
441 |
|
442 |
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) |
443 |
self.assert_qmp(result, 'return', {}) |
444 |
|
445 |
completed = False |
446 |
while not completed: |
447 |
for event in self.vm.get_qmp_events(wait=True): |
448 |
if event['event'] == 'BLOCK_JOB_COMPLETED': |
449 |
self.assert_qmp(event, 'data/type', 'stream') |
450 |
self.assert_qmp(event, 'data/device', 'drive0') |
451 |
self.assert_qmp(event, 'data/offset', self.image_len) |
452 |
self.assert_qmp(event, 'data/len', self.image_len) |
453 |
completed = True |
454 |
|
455 |
self.assert_no_active_block_jobs() |
456 |
|
457 |
def test_set_speed(self): |
458 |
self.assert_no_active_block_jobs() |
459 |
|
460 |
self.vm.pause_drive('drive0') |
461 |
result = self.vm.qmp('block-stream', device='drive0') |
462 |
self.assert_qmp(result, 'return', {}) |
463 |
|
464 |
# Default speed is 0 |
465 |
result = self.vm.qmp('query-block-jobs') |
466 |
self.assert_qmp(result, 'return[0]/device', 'drive0') |
467 |
self.assert_qmp(result, 'return[0]/speed', 0) |
468 |
|
469 |
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) |
470 |
self.assert_qmp(result, 'return', {}) |
471 |
|
472 |
# Ensure the speed we set was accepted |
473 |
result = self.vm.qmp('query-block-jobs') |
474 |
self.assert_qmp(result, 'return[0]/device', 'drive0') |
475 |
self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) |
476 |
|
477 |
self.cancel_and_wait(resume=True) |
478 |
self.vm.pause_drive('drive0') |
479 |
|
480 |
# Check setting speed in block-stream works |
481 |
result = self.vm.qmp('block-stream', device='drive0', speed=4 * 1024 * 1024) |
482 |
self.assert_qmp(result, 'return', {}) |
483 |
|
484 |
result = self.vm.qmp('query-block-jobs') |
485 |
self.assert_qmp(result, 'return[0]/device', 'drive0') |
486 |
self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) |
487 |
|
488 |
self.cancel_and_wait(resume=True) |
489 |
|
490 |
def test_set_speed_invalid(self): |
491 |
self.assert_no_active_block_jobs() |
492 |
|
493 |
result = self.vm.qmp('block-stream', device='drive0', speed=-1) |
494 |
self.assert_qmp(result, 'error/class', 'GenericError') |
495 |
|
496 |
self.assert_no_active_block_jobs() |
497 |
|
498 |
result = self.vm.qmp('block-stream', device='drive0') |
499 |
self.assert_qmp(result, 'return', {}) |
500 |
|
501 |
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) |
502 |
self.assert_qmp(result, 'error/class', 'GenericError') |
503 |
|
504 |
self.cancel_and_wait() |
505 |
|
506 |
if __name__ == '__main__': |
507 |
iotests.main(supported_fmts=['qcow2', 'qed']) |