]>
git.proxmox.com Git - mirror_qemu.git/blob - tests/qemu-iotests/040
3 # Tests for image block commit.
5 # Copyright (C) 2012 IBM, Corp.
6 # Copyright (C) 2012 Red Hat, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 # Test for live block commit
22 # Derived from Image Streaming Test 030
27 from iotests
import qemu_img
, qemu_io
31 backing_img
= os
.path
.join(iotests
.test_dir
, 'backing.img')
32 mid_img
= os
.path
.join(iotests
.test_dir
, 'mid.img')
33 test_img
= os
.path
.join(iotests
.test_dir
, 'test.img')
35 class ImageCommitTestCase(iotests
.QMPTestCase
):
36 '''Abstract base class for image commit test cases'''
38 def wait_for_complete(self
, need_ready
=False):
42 for event
in self
.vm
.get_qmp_events(wait
=True):
43 if event
['event'] == 'BLOCK_JOB_COMPLETED':
44 self
.assert_qmp_absent(event
, 'data/error')
45 self
.assert_qmp(event
, 'data/type', 'commit')
46 self
.assert_qmp(event
, 'data/device', 'drive0')
47 self
.assert_qmp(event
, 'data/offset', event
['data']['len'])
49 self
.assertTrue(ready
, "Expecting BLOCK_JOB_COMPLETED event")
51 elif event
['event'] == 'BLOCK_JOB_READY':
53 self
.assert_qmp(event
, 'data/type', 'commit')
54 self
.assert_qmp(event
, 'data/device', 'drive0')
55 self
.vm
.qmp('block-job-complete', device
='drive0')
57 self
.assert_no_active_block_jobs()
60 def run_commit_test(self
, top
, base
, need_ready
=False, node_names
=False):
61 self
.assert_no_active_block_jobs()
63 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
=top
, base_node
=base
)
65 result
= self
.vm
.qmp('block-commit', device
='drive0', top
=top
, base
=base
)
66 self
.assert_qmp(result
, 'return', {})
67 self
.wait_for_complete(need_ready
)
69 def run_default_commit_test(self
):
70 self
.assert_no_active_block_jobs()
71 result
= self
.vm
.qmp('block-commit', device
='drive0')
72 self
.assert_qmp(result
, 'return', {})
73 self
.wait_for_complete()
75 class TestSingleDrive(ImageCommitTestCase
):
76 # Need some space after the copied data so that throttling is effective in
77 # tests that use it rather than just completing the job immediately
78 image_len
= 2 * 1024 * 1024
79 test_len
= 1 * 1024 * 256
82 iotests
.create_image(backing_img
, self
.image_len
)
83 qemu_img('create', '-f', iotests
.imgfmt
,
84 '-o', 'backing_file=%s' % backing_img
, '-F', 'raw', mid_img
)
85 qemu_img('create', '-f', iotests
.imgfmt
,
86 '-o', 'backing_file=%s' % mid_img
,
87 '-F', iotests
.imgfmt
, test_img
)
88 qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img
)
89 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0xef 524288 524288', mid_img
)
90 self
.vm
= iotests
.VM().add_drive(test_img
, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface
="none")
91 self
.vm
.add_device(iotests
.get_virtio_scsi_device())
92 self
.vm
.add_device("scsi-hd,id=scsi0,drive=drive0")
97 self
.vm
.shutdown(has_quit
=self
.has_quit
)
100 os
.remove(backing_img
)
102 def test_commit(self
):
103 self
.run_commit_test(mid_img
, backing_img
)
104 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img
).find("verification failed"))
105 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img
).find("verification failed"))
107 def test_commit_node(self
):
108 self
.run_commit_test("mid", "base", node_names
=True)
109 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img
).find("verification failed"))
110 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img
).find("verification failed"))
112 @iotests.skip_if_unsupported(['throttle'])
113 def test_commit_with_filter_and_quit(self
):
114 result
= self
.vm
.qmp('object-add', qom_type
='throttle-group', id='tg')
115 self
.assert_qmp(result
, 'return', {})
117 # Add a filter outside of the backing chain
118 result
= self
.vm
.qmp('blockdev-add', driver
='throttle', node_name
='filter', throttle_group
='tg', file='mid')
119 self
.assert_qmp(result
, 'return', {})
121 result
= self
.vm
.qmp('block-commit', device
='drive0')
122 self
.assert_qmp(result
, 'return', {})
124 # Quit immediately, thus forcing a simultaneous cancel of the
125 # block job and a bdrv_drain_all()
126 result
= self
.vm
.qmp('quit')
127 self
.assert_qmp(result
, 'return', {})
131 # Same as above, but this time we add the filter after starting the job
132 @iotests.skip_if_unsupported(['throttle'])
133 def test_commit_plus_filter_and_quit(self
):
134 result
= self
.vm
.qmp('object-add', qom_type
='throttle-group', id='tg')
135 self
.assert_qmp(result
, 'return', {})
137 result
= self
.vm
.qmp('block-commit', device
='drive0')
138 self
.assert_qmp(result
, 'return', {})
140 # Add a filter outside of the backing chain
141 result
= self
.vm
.qmp('blockdev-add', driver
='throttle', node_name
='filter', throttle_group
='tg', file='mid')
142 self
.assert_qmp(result
, 'return', {})
144 # Quit immediately, thus forcing a simultaneous cancel of the
145 # block job and a bdrv_drain_all()
146 result
= self
.vm
.qmp('quit')
147 self
.assert_qmp(result
, 'return', {})
151 def test_device_not_found(self
):
152 result
= self
.vm
.qmp('block-commit', device
='nonexistent', top
='%s' % mid_img
)
153 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
155 def test_top_same_base(self
):
156 self
.assert_no_active_block_jobs()
157 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % backing_img
, base
='%s' % backing_img
)
158 self
.assert_qmp(result
, 'error/class', 'GenericError')
159 self
.assert_qmp(result
, 'error/desc', 'Base \'%s\' not found' % backing_img
)
161 def test_top_invalid(self
):
162 self
.assert_no_active_block_jobs()
163 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='badfile', base
='%s' % backing_img
)
164 self
.assert_qmp(result
, 'error/class', 'GenericError')
165 self
.assert_qmp(result
, 'error/desc', 'Top image file badfile not found')
167 def test_base_invalid(self
):
168 self
.assert_no_active_block_jobs()
169 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % mid_img
, base
='badfile')
170 self
.assert_qmp(result
, 'error/class', 'GenericError')
171 self
.assert_qmp(result
, 'error/desc', 'Base \'badfile\' not found')
173 def test_top_node_invalid(self
):
174 self
.assert_no_active_block_jobs()
175 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
='badfile', base_node
='base')
176 self
.assert_qmp(result
, 'error/class', 'GenericError')
177 self
.assert_qmp(result
, 'error/desc', "Cannot find device= nor node_name=badfile")
179 def test_base_node_invalid(self
):
180 self
.assert_no_active_block_jobs()
181 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
='mid', base_node
='badfile')
182 self
.assert_qmp(result
, 'error/class', 'GenericError')
183 self
.assert_qmp(result
, 'error/desc', "Cannot find device= nor node_name=badfile")
185 def test_top_path_and_node(self
):
186 self
.assert_no_active_block_jobs()
187 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
='mid', base_node
='base', top
='%s' % mid_img
)
188 self
.assert_qmp(result
, 'error/class', 'GenericError')
189 self
.assert_qmp(result
, 'error/desc', "'top-node' and 'top' are mutually exclusive")
191 def test_base_path_and_node(self
):
192 self
.assert_no_active_block_jobs()
193 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
='mid', base_node
='base', base
='%s' % backing_img
)
194 self
.assert_qmp(result
, 'error/class', 'GenericError')
195 self
.assert_qmp(result
, 'error/desc', "'base-node' and 'base' are mutually exclusive")
197 def test_top_is_active(self
):
198 self
.run_commit_test(test_img
, backing_img
, need_ready
=True)
199 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img
).find("verification failed"))
200 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img
).find("verification failed"))
202 def test_top_is_default_active(self
):
203 self
.run_default_commit_test()
204 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img
).find("verification failed"))
205 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img
).find("verification failed"))
207 def test_top_and_base_reversed(self
):
208 self
.assert_no_active_block_jobs()
209 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % backing_img
, base
='%s' % mid_img
)
210 self
.assert_qmp(result
, 'error/class', 'GenericError')
211 self
.assert_qmp(result
, 'error/desc', 'Base \'%s\' not found' % mid_img
)
213 def test_top_and_base_node_reversed(self
):
214 self
.assert_no_active_block_jobs()
215 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
='base', base_node
='top')
216 self
.assert_qmp(result
, 'error/class', 'GenericError')
217 self
.assert_qmp(result
, 'error/desc', "'top' is not in this backing file chain")
219 def test_top_node_in_wrong_chain(self
):
220 self
.assert_no_active_block_jobs()
222 result
= self
.vm
.qmp('blockdev-add', driver
='null-co', node_name
='null')
223 self
.assert_qmp(result
, 'return', {})
225 result
= self
.vm
.qmp('block-commit', device
='drive0', top_node
='null', base_node
='base')
226 self
.assert_qmp(result
, 'error/class', 'GenericError')
227 self
.assert_qmp(result
, 'error/desc', "'null' is not in this backing file chain")
229 # When the job is running on a BB that is automatically deleted on hot
230 # unplug, the job is cancelled when the device disappears
231 def test_hot_unplug(self
):
232 if self
.image_len
== 0:
235 self
.assert_no_active_block_jobs()
236 result
= self
.vm
.qmp('block-commit', device
='drive0', top
=mid_img
,
237 base
=backing_img
, speed
=(self
.image_len
// 4))
238 self
.assert_qmp(result
, 'return', {})
239 result
= self
.vm
.qmp('device_del', id='scsi0')
240 self
.assert_qmp(result
, 'return', {})
244 while not cancelled
or not deleted
:
245 for event
in self
.vm
.get_qmp_events(wait
=True):
246 if event
['event'] == 'DEVICE_DELETED':
247 self
.assert_qmp(event
, 'data/device', 'scsi0')
249 elif event
['event'] == 'BLOCK_JOB_CANCELLED':
250 self
.assert_qmp(event
, 'data/device', 'drive0')
252 elif event
['event'] == 'JOB_STATUS_CHANGE':
253 self
.assert_qmp(event
, 'data/id', 'drive0')
255 self
.fail("Unexpected event %s" % (event
['event']))
257 self
.assert_no_active_block_jobs()
259 # Tests that the insertion of the commit_top filter node doesn't make a
260 # difference to query-blockstat
261 def test_implicit_node(self
):
262 if self
.image_len
== 0:
265 self
.assert_no_active_block_jobs()
266 result
= self
.vm
.qmp('block-commit', device
='drive0', top
=mid_img
,
267 base
=backing_img
, speed
=(self
.image_len
// 4))
268 self
.assert_qmp(result
, 'return', {})
270 result
= self
.vm
.qmp('query-block')
271 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
272 self
.assert_qmp(result
, 'return[0]/inserted/drv', iotests
.imgfmt
)
273 self
.assert_qmp(result
, 'return[0]/inserted/backing_file', mid_img
)
274 self
.assert_qmp(result
, 'return[0]/inserted/backing_file_depth', 2)
275 self
.assert_qmp(result
, 'return[0]/inserted/image/filename', test_img
)
276 self
.assert_qmp(result
, 'return[0]/inserted/image/backing-image/filename', mid_img
)
277 self
.assert_qmp(result
, 'return[0]/inserted/image/backing-image/backing-image/filename', backing_img
)
279 result
= self
.vm
.qmp('query-blockstats')
280 self
.assert_qmp(result
, 'return[0]/node-name', 'top')
281 self
.assert_qmp(result
, 'return[0]/backing/node-name', 'mid')
282 self
.assert_qmp(result
, 'return[0]/backing/backing/node-name', 'base')
284 self
.cancel_and_wait()
285 self
.assert_no_active_block_jobs()
287 class TestRelativePaths(ImageCommitTestCase
):
288 image_len
= 1 * 1024 * 1024
289 test_len
= 1 * 1024 * 256
295 test_img
= os
.path
.join(iotests
.test_dir
, dir3
, 'test.img')
296 mid_img
= "../mid.img"
297 backing_img
= "../dir1/backing.img"
299 backing_img_abs
= os
.path
.join(iotests
.test_dir
, dir1
, 'backing.img')
300 mid_img_abs
= os
.path
.join(iotests
.test_dir
, dir2
, 'mid.img')
304 os
.mkdir(os
.path
.join(iotests
.test_dir
, self
.dir1
))
305 os
.mkdir(os
.path
.join(iotests
.test_dir
, self
.dir2
))
306 os
.mkdir(os
.path
.join(iotests
.test_dir
, self
.dir3
))
307 except OSError as exception
:
308 if exception
.errno
!= errno
.EEXIST
:
310 iotests
.create_image(self
.backing_img_abs
, TestRelativePaths
.image_len
)
311 qemu_img('create', '-f', iotests
.imgfmt
,
312 '-o', 'backing_file=%s' % self
.backing_img_abs
,
313 '-F', 'raw', self
.mid_img_abs
)
314 qemu_img('create', '-f', iotests
.imgfmt
,
315 '-o', 'backing_file=%s' % self
.mid_img_abs
,
316 '-F', iotests
.imgfmt
, self
.test_img
)
317 qemu_img('rebase', '-u', '-b', self
.backing_img
,
318 '-F', 'raw', self
.mid_img_abs
)
319 qemu_img('rebase', '-u', '-b', self
.mid_img
,
320 '-F', iotests
.imgfmt
, self
.test_img
)
321 qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self
.backing_img_abs
)
322 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0xef 524288 524288', self
.mid_img_abs
)
323 self
.vm
= iotests
.VM().add_drive(self
.test_img
)
328 os
.remove(self
.test_img
)
329 os
.remove(self
.mid_img_abs
)
330 os
.remove(self
.backing_img_abs
)
332 os
.rmdir(os
.path
.join(iotests
.test_dir
, self
.dir1
))
333 os
.rmdir(os
.path
.join(iotests
.test_dir
, self
.dir3
))
334 os
.rmdir(os
.path
.join(iotests
.test_dir
, self
.dir2
))
335 except OSError as exception
:
336 if exception
.errno
!= errno
.EEXIST
and exception
.errno
!= errno
.ENOTEMPTY
:
339 def test_commit(self
):
340 self
.run_commit_test(self
.mid_img
, self
.backing_img
)
341 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self
.backing_img_abs
).find("verification failed"))
342 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self
.backing_img_abs
).find("verification failed"))
344 def test_device_not_found(self
):
345 result
= self
.vm
.qmp('block-commit', device
='nonexistent', top
='%s' % self
.mid_img
)
346 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
348 def test_top_same_base(self
):
349 self
.assert_no_active_block_jobs()
350 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % self
.mid_img
, base
='%s' % self
.mid_img
)
351 self
.assert_qmp(result
, 'error/class', 'GenericError')
352 self
.assert_qmp(result
, 'error/desc', 'Base \'%s\' not found' % self
.mid_img
)
354 def test_top_invalid(self
):
355 self
.assert_no_active_block_jobs()
356 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='badfile', base
='%s' % self
.backing_img
)
357 self
.assert_qmp(result
, 'error/class', 'GenericError')
358 self
.assert_qmp(result
, 'error/desc', 'Top image file badfile not found')
360 def test_base_invalid(self
):
361 self
.assert_no_active_block_jobs()
362 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % self
.mid_img
, base
='badfile')
363 self
.assert_qmp(result
, 'error/class', 'GenericError')
364 self
.assert_qmp(result
, 'error/desc', 'Base \'badfile\' not found')
366 def test_top_is_active(self
):
367 self
.run_commit_test(self
.test_img
, self
.backing_img
)
368 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self
.backing_img_abs
).find("verification failed"))
369 self
.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self
.backing_img_abs
).find("verification failed"))
371 def test_top_and_base_reversed(self
):
372 self
.assert_no_active_block_jobs()
373 result
= self
.vm
.qmp('block-commit', device
='drive0', top
='%s' % self
.backing_img
, base
='%s' % self
.mid_img
)
374 self
.assert_qmp(result
, 'error/class', 'GenericError')
375 self
.assert_qmp(result
, 'error/desc', 'Base \'%s\' not found' % self
.mid_img
)
378 class TestSetSpeed(ImageCommitTestCase
):
379 image_len
= 80 * 1024 * 1024 # MB
382 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
383 qemu_img('create', '-f', iotests
.imgfmt
,
384 '-o', 'backing_file=%s' % backing_img
, '-F', 'raw', mid_img
)
385 qemu_img('create', '-f', iotests
.imgfmt
,
386 '-o', 'backing_file=%s' % mid_img
,
387 '-F', iotests
.imgfmt
, test_img
)
388 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0x1 0 512', test_img
)
389 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0xef 524288 524288', mid_img
)
390 self
.vm
= iotests
.VM().add_drive('blkdebug::' + test_img
)
397 os
.remove(backing_img
)
399 def test_set_speed(self
):
400 self
.assert_no_active_block_jobs()
402 self
.vm
.pause_drive('drive0')
403 result
= self
.vm
.qmp('block-commit', device
='drive0', top
=mid_img
, speed
=1024 * 1024)
404 self
.assert_qmp(result
, 'return', {})
406 # Ensure the speed we set was accepted
407 result
= self
.vm
.qmp('query-block-jobs')
408 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
409 self
.assert_qmp(result
, 'return[0]/speed', 1024 * 1024)
411 self
.cancel_and_wait(resume
=True)
413 class TestActiveZeroLengthImage(TestSingleDrive
):
416 class TestReopenOverlay(ImageCommitTestCase
):
417 image_len
= 1024 * 1024
418 img0
= os
.path
.join(iotests
.test_dir
, '0.img')
419 img1
= os
.path
.join(iotests
.test_dir
, '1.img')
420 img2
= os
.path
.join(iotests
.test_dir
, '2.img')
421 img3
= os
.path
.join(iotests
.test_dir
, '3.img')
424 iotests
.create_image(self
.img0
, self
.image_len
)
425 qemu_img('create', '-f', iotests
.imgfmt
,
426 '-o', 'backing_file=%s' % self
.img0
, '-F', 'raw', self
.img1
)
427 qemu_img('create', '-f', iotests
.imgfmt
,
428 '-o', 'backing_file=%s' % self
.img1
,
429 '-F', iotests
.imgfmt
, self
.img2
)
430 qemu_img('create', '-f', iotests
.imgfmt
,
431 '-o', 'backing_file=%s' % self
.img2
,
432 '-F', iotests
.imgfmt
, self
.img3
)
433 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0xab 0 128K', self
.img1
)
434 self
.vm
= iotests
.VM().add_drive(self
.img3
)
444 # This tests what happens when the overlay image of the 'top' node
445 # needs to be reopened in read-write mode in order to update the
446 # backing image string.
447 def test_reopen_overlay(self
):
448 self
.run_commit_test(self
.img1
, self
.img0
)
450 class TestErrorHandling(iotests
.QMPTestCase
):
451 image_len
= 2 * 1024 * 1024
454 iotests
.create_image(backing_img
, self
.image_len
)
455 qemu_img('create', '-f', iotests
.imgfmt
,
456 '-o', 'backing_file=%s' % backing_img
,
457 '-F', 'raw', mid_img
)
458 qemu_img('create', '-f', iotests
.imgfmt
,
459 '-o', 'backing_file=%s' % mid_img
,
460 '-F', iotests
.imgfmt
, test_img
)
462 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0x11 0 512k', mid_img
)
463 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P 0x22 0 512k', test_img
)
465 self
.vm
= iotests
.VM()
468 self
.blkdebug_file
= iotests
.file_path("blkdebug.conf")
474 os
.remove(backing_img
)
476 def blockdev_add(self
, **kwargs
):
477 result
= self
.vm
.qmp('blockdev-add', **kwargs
)
478 self
.assert_qmp(result
, 'return', {})
480 def add_block_nodes(self
, base_debug
=None, mid_debug
=None, top_debug
=None):
481 self
.blockdev_add(node_name
='base-file', driver
='file',
482 filename
=backing_img
)
483 self
.blockdev_add(node_name
='mid-file', driver
='file',
485 self
.blockdev_add(node_name
='top-file', driver
='file',
489 self
.blockdev_add(node_name
='base-dbg', driver
='blkdebug',
490 image
='base-file', inject_error
=base_debug
)
492 self
.blockdev_add(node_name
='mid-dbg', driver
='blkdebug',
493 image
='mid-file', inject_error
=mid_debug
)
495 self
.blockdev_add(node_name
='top-dbg', driver
='blkdebug',
496 image
='top-file', inject_error
=top_debug
)
498 self
.blockdev_add(node_name
='base-fmt', driver
='raw',
499 file=('base-dbg' if base_debug
else 'base-file'))
500 self
.blockdev_add(node_name
='mid-fmt', driver
=iotests
.imgfmt
,
501 file=('mid-dbg' if mid_debug
else 'mid-file'),
503 self
.blockdev_add(node_name
='top-fmt', driver
=iotests
.imgfmt
,
504 file=('top-dbg' if top_debug
else 'top-file'),
507 def run_job(self
, expected_events
, error_pauses_job
=False):
508 match_device
= {'data': {'device': 'job0'}}
510 ('BLOCK_JOB_COMPLETED', match_device
),
511 ('BLOCK_JOB_CANCELLED', match_device
),
512 ('BLOCK_JOB_ERROR', match_device
),
513 ('BLOCK_JOB_READY', match_device
),
519 ev
= self
.vm
.events_wait(events
, timeout
=5.0)
520 if ev
['event'] == 'BLOCK_JOB_COMPLETED':
522 elif ev
['event'] == 'BLOCK_JOB_ERROR':
524 result
= self
.vm
.qmp('block-job-resume', device
='job0')
525 self
.assert_qmp(result
, 'return', {})
526 elif ev
['event'] == 'BLOCK_JOB_READY':
527 result
= self
.vm
.qmp('block-job-complete', device
='job0')
528 self
.assert_qmp(result
, 'return', {})
530 self
.fail("Unexpected event: %s" % ev
)
531 log
.append(iotests
.filter_qmp_event(ev
))
534 self
.assertEqual(expected_events
, log
)
536 def event_error(self
, op
, action
):
538 'event': 'BLOCK_JOB_ERROR',
539 'data': {'action': action
, 'device': 'job0', 'operation': op
},
540 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'}
543 def event_ready(self
):
545 'event': 'BLOCK_JOB_READY',
546 'data': {'device': 'job0',
551 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'},
554 def event_completed(self
, errmsg
=None, active
=True):
555 max_len
= 524288 if active
else self
.image_len
559 'offset': 0 if errmsg
else max_len
,
564 data
['error'] = errmsg
567 'event': 'BLOCK_JOB_COMPLETED',
569 'timestamp': {'microseconds': 'USECS', 'seconds': 'SECS'},
572 def blkdebug_event(self
, event
, is_raw
=False):
576 'sector': 512 if is_raw
else 1024,
581 def prepare_and_start_job(self
, on_error
, active
=True,
582 top_event
=None, mid_event
=None, base_event
=None):
584 top_debug
= self
.blkdebug_event(top_event
)
585 mid_debug
= self
.blkdebug_event(mid_event
)
586 base_debug
= self
.blkdebug_event(base_event
, True)
588 self
.add_block_nodes(top_debug
=top_debug
, mid_debug
=mid_debug
,
589 base_debug
=base_debug
)
591 result
= self
.vm
.qmp('block-commit', job_id
='job0', device
='top-fmt',
592 top_node
='top-fmt' if active
else 'mid-fmt',
593 base_node
='mid-fmt' if active
else 'base-fmt',
595 self
.assert_qmp(result
, 'return', {})
597 def testActiveReadErrorReport(self
):
598 self
.prepare_and_start_job('report', top_event
='read_aio')
600 self
.event_error('read', 'report'),
601 self
.event_completed('Input/output error')
605 self
.assertFalse(iotests
.compare_images(test_img
, mid_img
),
606 'target image matches source after error')
608 def testActiveReadErrorStop(self
):
609 self
.prepare_and_start_job('stop', top_event
='read_aio')
611 self
.event_error('read', 'stop'),
613 self
.event_completed()
614 ], error_pauses_job
=True)
617 self
.assertTrue(iotests
.compare_images(test_img
, mid_img
),
618 'target image does not match source after commit')
620 def testActiveReadErrorIgnore(self
):
621 self
.prepare_and_start_job('ignore', top_event
='read_aio')
623 self
.event_error('read', 'ignore'),
625 self
.event_completed()
628 # For commit, 'ignore' actually means retry, so this will succeed
630 self
.assertTrue(iotests
.compare_images(test_img
, mid_img
),
631 'target image does not match source after commit')
633 def testActiveWriteErrorReport(self
):
634 self
.prepare_and_start_job('report', mid_event
='write_aio')
636 self
.event_error('write', 'report'),
637 self
.event_completed('Input/output error')
641 self
.assertFalse(iotests
.compare_images(test_img
, mid_img
),
642 'target image matches source after error')
644 def testActiveWriteErrorStop(self
):
645 self
.prepare_and_start_job('stop', mid_event
='write_aio')
647 self
.event_error('write', 'stop'),
649 self
.event_completed()
650 ], error_pauses_job
=True)
653 self
.assertTrue(iotests
.compare_images(test_img
, mid_img
),
654 'target image does not match source after commit')
656 def testActiveWriteErrorIgnore(self
):
657 self
.prepare_and_start_job('ignore', mid_event
='write_aio')
659 self
.event_error('write', 'ignore'),
661 self
.event_completed()
664 # For commit, 'ignore' actually means retry, so this will succeed
666 self
.assertTrue(iotests
.compare_images(test_img
, mid_img
),
667 'target image does not match source after commit')
669 def testIntermediateReadErrorReport(self
):
670 self
.prepare_and_start_job('report', active
=False, mid_event
='read_aio')
672 self
.event_error('read', 'report'),
673 self
.event_completed('Input/output error', active
=False)
677 self
.assertFalse(iotests
.compare_images(mid_img
, backing_img
, fmt2
='raw'),
678 'target image matches source after error')
680 def testIntermediateReadErrorStop(self
):
681 self
.prepare_and_start_job('stop', active
=False, mid_event
='read_aio')
683 self
.event_error('read', 'stop'),
684 self
.event_completed(active
=False)
685 ], error_pauses_job
=True)
688 self
.assertTrue(iotests
.compare_images(mid_img
, backing_img
, fmt2
='raw'),
689 'target image does not match source after commit')
691 def testIntermediateReadErrorIgnore(self
):
692 self
.prepare_and_start_job('ignore', active
=False, mid_event
='read_aio')
694 self
.event_error('read', 'ignore'),
695 self
.event_completed(active
=False)
698 # For commit, 'ignore' actually means retry, so this will succeed
700 self
.assertTrue(iotests
.compare_images(mid_img
, backing_img
, fmt2
='raw'),
701 'target image does not match source after commit')
703 def testIntermediateWriteErrorReport(self
):
704 self
.prepare_and_start_job('report', active
=False, base_event
='write_aio')
706 self
.event_error('write', 'report'),
707 self
.event_completed('Input/output error', active
=False)
711 self
.assertFalse(iotests
.compare_images(mid_img
, backing_img
, fmt2
='raw'),
712 'target image matches source after error')
714 def testIntermediateWriteErrorStop(self
):
715 self
.prepare_and_start_job('stop', active
=False, base_event
='write_aio')
717 self
.event_error('write', 'stop'),
718 self
.event_completed(active
=False)
719 ], error_pauses_job
=True)
722 self
.assertTrue(iotests
.compare_images(mid_img
, backing_img
, fmt2
='raw'),
723 'target image does not match source after commit')
725 def testIntermediateWriteErrorIgnore(self
):
726 self
.prepare_and_start_job('ignore', active
=False, base_event
='write_aio')
728 self
.event_error('write', 'ignore'),
729 self
.event_completed(active
=False)
732 # For commit, 'ignore' actually means retry, so this will succeed
734 self
.assertTrue(iotests
.compare_images(mid_img
, backing_img
, fmt2
='raw'),
735 'target image does not match source after commit')
737 if __name__
== '__main__':
738 iotests
.main(supported_fmts
=['qcow2', 'qed'],
739 supported_protocols
=['file'])