3 # Tests for image streaming.
5 # Copyright (C) 2012 IBM Corp.
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.
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.
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/>.
24 from iotests
import qemu_img
, qemu_io
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')
30 class TestSingleDrive(iotests
.QMPTestCase
):
31 image_len
= 1 * 1024 * 1024 # MB
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 self
.vm
= iotests
.VM().add_drive(test_img
)
44 os
.remove(backing_img
)
46 def test_stream(self
):
47 self
.assert_no_active_block_jobs()
49 result
= self
.vm
.qmp('block-stream', device
='drive0')
50 self
.assert_qmp(result
, 'return', {})
54 for event
in self
.vm
.get_qmp_events(wait
=True):
55 if event
['event'] == 'BLOCK_JOB_COMPLETED':
56 self
.assert_qmp(event
, 'data/type', 'stream')
57 self
.assert_qmp(event
, 'data/device', 'drive0')
58 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
59 self
.assert_qmp(event
, 'data/len', self
.image_len
)
62 self
.assert_no_active_block_jobs()
65 self
.assertEqual(qemu_io('-c', 'map', backing_img
),
66 qemu_io('-c', 'map', test_img
),
67 'image file map does not match backing file after streaming')
69 def test_stream_pause(self
):
70 self
.assert_no_active_block_jobs()
72 result
= self
.vm
.qmp('block-stream', device
='drive0')
73 self
.assert_qmp(result
, 'return', {})
75 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
76 self
.assert_qmp(result
, 'return', {})
79 result
= self
.vm
.qmp('query-block-jobs')
80 offset
= self
.dictpath(result
, 'return[0]/offset')
83 result
= self
.vm
.qmp('query-block-jobs')
84 self
.assert_qmp(result
, 'return[0]/offset', offset
)
86 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
87 self
.assert_qmp(result
, 'return', {})
91 for event
in self
.vm
.get_qmp_events(wait
=True):
92 if event
['event'] == 'BLOCK_JOB_COMPLETED':
93 self
.assert_qmp(event
, 'data/type', 'stream')
94 self
.assert_qmp(event
, 'data/device', 'drive0')
95 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
96 self
.assert_qmp(event
, 'data/len', self
.image_len
)
99 self
.assert_no_active_block_jobs()
102 self
.assertEqual(qemu_io('-c', 'map', backing_img
),
103 qemu_io('-c', 'map', test_img
),
104 'image file map does not match backing file after streaming')
106 def test_stream_partial(self
):
107 self
.assert_no_active_block_jobs()
109 result
= self
.vm
.qmp('block-stream', device
='drive0', base
=mid_img
)
110 self
.assert_qmp(result
, 'return', {})
114 for event
in self
.vm
.get_qmp_events(wait
=True):
115 if event
['event'] == 'BLOCK_JOB_COMPLETED':
116 self
.assert_qmp(event
, 'data/type', 'stream')
117 self
.assert_qmp(event
, 'data/device', 'drive0')
118 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
119 self
.assert_qmp(event
, 'data/len', self
.image_len
)
122 self
.assert_no_active_block_jobs()
125 self
.assertEqual(qemu_io('-c', 'map', mid_img
),
126 qemu_io('-c', 'map', test_img
),
127 'image file map does not match backing file after streaming')
129 def test_device_not_found(self
):
130 result
= self
.vm
.qmp('block-stream', device
='nonexistent')
131 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
134 class TestSmallerBackingFile(iotests
.QMPTestCase
):
135 backing_len
= 1 * 1024 * 1024 # MB
136 image_len
= 2 * backing_len
139 iotests
.create_image(backing_img
, self
.backing_len
)
140 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
, str(self
.image_len
))
141 self
.vm
= iotests
.VM().add_drive(test_img
)
144 # If this hangs, then you are missing a fix to complete streaming when the
145 # end of the backing file is reached.
146 def test_stream(self
):
147 self
.assert_no_active_block_jobs()
149 result
= self
.vm
.qmp('block-stream', device
='drive0')
150 self
.assert_qmp(result
, 'return', {})
154 for event
in self
.vm
.get_qmp_events(wait
=True):
155 if event
['event'] == 'BLOCK_JOB_COMPLETED':
156 self
.assert_qmp(event
, 'data/type', 'stream')
157 self
.assert_qmp(event
, 'data/device', 'drive0')
158 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
159 self
.assert_qmp(event
, 'data/len', self
.image_len
)
162 self
.assert_no_active_block_jobs()
165 class TestErrors(iotests
.QMPTestCase
):
166 image_len
= 2 * 1024 * 1024 # MB
168 # this should match STREAM_BUFFER_SIZE/512 in block/stream.c
169 STREAM_BUFFER_SIZE
= 512 * 1024
171 def create_blkdebug_file(self
, name
, event
, errno
):
172 file = open(name
, 'w')
191 ''' % (event
, errno
, self
.STREAM_BUFFER_SIZE
/ 512, event
, event
))
194 class TestEIO(TestErrors
):
196 self
.blkdebug_file
= backing_img
+ ".blkdebug"
197 iotests
.create_image(backing_img
, TestErrors
.image_len
)
198 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 5)
199 qemu_img('create', '-f', iotests
.imgfmt
,
200 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
201 % (self
.blkdebug_file
, backing_img
),
203 self
.vm
= iotests
.VM().add_drive(test_img
)
209 os
.remove(backing_img
)
210 os
.remove(self
.blkdebug_file
)
212 def test_report(self
):
213 self
.assert_no_active_block_jobs()
215 result
= self
.vm
.qmp('block-stream', device
='drive0')
216 self
.assert_qmp(result
, 'return', {})
221 for event
in self
.vm
.get_qmp_events(wait
=True):
222 if event
['event'] == 'BLOCK_JOB_ERROR':
223 self
.assert_qmp(event
, 'data/device', 'drive0')
224 self
.assert_qmp(event
, 'data/operation', 'read')
226 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
227 self
.assertTrue(error
, 'job completed unexpectedly')
228 self
.assert_qmp(event
, 'data/type', 'stream')
229 self
.assert_qmp(event
, 'data/device', 'drive0')
230 self
.assert_qmp(event
, 'data/error', 'Input/output error')
231 self
.assert_qmp(event
, 'data/offset', self
.STREAM_BUFFER_SIZE
)
232 self
.assert_qmp(event
, 'data/len', self
.image_len
)
235 self
.assert_no_active_block_jobs()
238 def test_ignore(self
):
239 self
.assert_no_active_block_jobs()
241 result
= self
.vm
.qmp('block-stream', device
='drive0', on_error
='ignore')
242 self
.assert_qmp(result
, 'return', {})
247 for event
in self
.vm
.get_qmp_events(wait
=True):
248 if event
['event'] == 'BLOCK_JOB_ERROR':
249 self
.assert_qmp(event
, 'data/device', 'drive0')
250 self
.assert_qmp(event
, 'data/operation', 'read')
251 result
= self
.vm
.qmp('query-block-jobs')
252 self
.assert_qmp(result
, 'return[0]/paused', False)
254 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
255 self
.assertTrue(error
, 'job completed unexpectedly')
256 self
.assert_qmp(event
, 'data/type', 'stream')
257 self
.assert_qmp(event
, 'data/device', 'drive0')
258 self
.assert_qmp(event
, 'data/error', 'Input/output error')
259 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
260 self
.assert_qmp(event
, 'data/len', self
.image_len
)
263 self
.assert_no_active_block_jobs()
267 self
.assert_no_active_block_jobs()
269 result
= self
.vm
.qmp('block-stream', device
='drive0', on_error
='stop')
270 self
.assert_qmp(result
, 'return', {})
275 for event
in self
.vm
.get_qmp_events(wait
=True):
276 if event
['event'] == 'BLOCK_JOB_ERROR':
277 self
.assert_qmp(event
, 'data/device', 'drive0')
278 self
.assert_qmp(event
, 'data/operation', 'read')
280 result
= self
.vm
.qmp('query-block-jobs')
281 self
.assert_qmp(result
, 'return[0]/paused', True)
282 self
.assert_qmp(result
, 'return[0]/offset', self
.STREAM_BUFFER_SIZE
)
283 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
285 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
286 self
.assert_qmp(result
, 'return', {})
288 result
= self
.vm
.qmp('query-block-jobs')
289 self
.assert_qmp(result
, 'return[0]/paused', False)
290 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
292 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
293 self
.assertTrue(error
, 'job completed unexpectedly')
294 self
.assert_qmp(event
, 'data/type', 'stream')
295 self
.assert_qmp(event
, 'data/device', 'drive0')
296 self
.assert_qmp_absent(event
, 'data/error')
297 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
298 self
.assert_qmp(event
, 'data/len', self
.image_len
)
301 self
.assert_no_active_block_jobs()
304 def test_enospc(self
):
305 self
.assert_no_active_block_jobs()
307 result
= self
.vm
.qmp('block-stream', device
='drive0', on_error
='enospc')
308 self
.assert_qmp(result
, 'return', {})
313 for event
in self
.vm
.get_qmp_events(wait
=True):
314 if event
['event'] == 'BLOCK_JOB_ERROR':
315 self
.assert_qmp(event
, 'data/device', 'drive0')
316 self
.assert_qmp(event
, 'data/operation', 'read')
318 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
319 self
.assertTrue(error
, 'job completed unexpectedly')
320 self
.assert_qmp(event
, 'data/type', 'stream')
321 self
.assert_qmp(event
, 'data/device', 'drive0')
322 self
.assert_qmp(event
, 'data/error', 'Input/output error')
323 self
.assert_qmp(event
, 'data/offset', self
.STREAM_BUFFER_SIZE
)
324 self
.assert_qmp(event
, 'data/len', self
.image_len
)
327 self
.assert_no_active_block_jobs()
330 class TestENOSPC(TestErrors
):
332 self
.blkdebug_file
= backing_img
+ ".blkdebug"
333 iotests
.create_image(backing_img
, TestErrors
.image_len
)
334 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 28)
335 qemu_img('create', '-f', iotests
.imgfmt
,
336 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
337 % (self
.blkdebug_file
, backing_img
),
339 self
.vm
= iotests
.VM().add_drive(test_img
)
345 os
.remove(backing_img
)
346 os
.remove(self
.blkdebug_file
)
348 def test_enospc(self
):
349 self
.assert_no_active_block_jobs()
351 result
= self
.vm
.qmp('block-stream', device
='drive0', on_error
='enospc')
352 self
.assert_qmp(result
, 'return', {})
357 for event
in self
.vm
.get_qmp_events(wait
=True):
358 if event
['event'] == 'BLOCK_JOB_ERROR':
359 self
.assert_qmp(event
, 'data/device', 'drive0')
360 self
.assert_qmp(event
, 'data/operation', 'read')
362 result
= self
.vm
.qmp('query-block-jobs')
363 self
.assert_qmp(result
, 'return[0]/paused', True)
364 self
.assert_qmp(result
, 'return[0]/offset', self
.STREAM_BUFFER_SIZE
)
365 self
.assert_qmp(result
, 'return[0]/io-status', 'nospace')
367 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
368 self
.assert_qmp(result
, 'return', {})
370 result
= self
.vm
.qmp('query-block-jobs')
371 self
.assert_qmp(result
, 'return[0]/paused', False)
372 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
374 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
375 self
.assertTrue(error
, 'job completed unexpectedly')
376 self
.assert_qmp(event
, 'data/type', 'stream')
377 self
.assert_qmp(event
, 'data/device', 'drive0')
378 self
.assert_qmp_absent(event
, 'data/error')
379 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
380 self
.assert_qmp(event
, 'data/len', self
.image_len
)
383 self
.assert_no_active_block_jobs()
386 class TestStreamStop(iotests
.QMPTestCase
):
387 image_len
= 8 * 1024 * 1024 * 1024 # GB
390 qemu_img('create', backing_img
, str(TestStreamStop
.image_len
))
391 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
392 self
.vm
= iotests
.VM().add_drive(test_img
)
398 os
.remove(backing_img
)
400 def test_stream_stop(self
):
401 self
.assert_no_active_block_jobs()
403 result
= self
.vm
.qmp('block-stream', device
='drive0')
404 self
.assert_qmp(result
, 'return', {})
407 events
= self
.vm
.get_qmp_events(wait
=False)
408 self
.assertEqual(events
, [], 'unexpected QMP event: %s' % events
)
410 self
.cancel_and_wait()
412 class TestSetSpeed(iotests
.QMPTestCase
):
413 image_len
= 80 * 1024 * 1024 # MB
416 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
417 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
418 self
.vm
= iotests
.VM().add_drive(test_img
)
424 os
.remove(backing_img
)
426 # This is a short performance test which is not run by default.
427 # Invoke "IMGFMT=qed ./030 TestSetSpeed.perf_test_throughput"
428 def perf_test_throughput(self
):
429 self
.assert_no_active_block_jobs()
431 result
= self
.vm
.qmp('block-stream', device
='drive0')
432 self
.assert_qmp(result
, 'return', {})
434 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
435 self
.assert_qmp(result
, 'return', {})
439 for event
in self
.vm
.get_qmp_events(wait
=True):
440 if event
['event'] == 'BLOCK_JOB_COMPLETED':
441 self
.assert_qmp(event
, 'data/type', 'stream')
442 self
.assert_qmp(event
, 'data/device', 'drive0')
443 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
444 self
.assert_qmp(event
, 'data/len', self
.image_len
)
447 self
.assert_no_active_block_jobs()
449 def test_set_speed(self
):
450 self
.assert_no_active_block_jobs()
452 result
= self
.vm
.qmp('block-stream', device
='drive0')
453 self
.assert_qmp(result
, 'return', {})
456 result
= self
.vm
.qmp('query-block-jobs')
457 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
458 self
.assert_qmp(result
, 'return[0]/speed', 0)
460 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
461 self
.assert_qmp(result
, 'return', {})
463 # Ensure the speed we set was accepted
464 result
= self
.vm
.qmp('query-block-jobs')
465 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
466 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
468 self
.cancel_and_wait()
470 # Check setting speed in block-stream works
471 result
= self
.vm
.qmp('block-stream', device
='drive0', speed
=4 * 1024 * 1024)
472 self
.assert_qmp(result
, 'return', {})
474 result
= self
.vm
.qmp('query-block-jobs')
475 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
476 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
478 self
.cancel_and_wait()
480 def test_set_speed_invalid(self
):
481 self
.assert_no_active_block_jobs()
483 result
= self
.vm
.qmp('block-stream', device
='drive0', speed
=-1)
484 self
.assert_qmp(result
, 'error/class', 'GenericError')
486 self
.assert_no_active_block_jobs()
488 result
= self
.vm
.qmp('block-stream', device
='drive0')
489 self
.assert_qmp(result
, 'return', {})
491 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
492 self
.assert_qmp(result
, 'error/class', 'GenericError')
494 self
.cancel_and_wait()
496 if __name__
== '__main__':
497 iotests
.main(supported_fmts
=['qcow2', 'qed'])