3 # Tests for drive-backup and blockdev-backup
5 # Copyright (C) 2013, 2014 Red Hat, Inc.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from iotests
import qemu_img
, qemu_io
28 test_img
= os
.path
.join(iotests
.test_dir
, 'test.img')
29 target_img
= os
.path
.join(iotests
.test_dir
, 'target.img')
30 blockdev_target_img
= os
.path
.join(iotests
.test_dir
, 'blockdev-target.img')
32 image_len
= 64 * 1024 * 1024 # MB
35 qemu_img('create', '-f', iotests
.imgfmt
, test_img
, str(image_len
))
36 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x11 0 64k', test_img
)
37 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x00 64k 128k', test_img
)
38 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x22 162k 32k', test_img
)
39 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0xd5 1M 32k', test_img
)
40 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0xdc 32M 124k', test_img
)
41 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x33 67043328 64k', test_img
)
47 class TestSingleDrive(iotests
.QMPTestCase
):
49 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(image_len
))
51 self
.vm
= iotests
.VM().add_drive(test_img
)
52 self
.vm
.add_drive(blockdev_target_img
, interface
="none")
53 if iotests
.qemu_default_machine
== 'pc':
54 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
59 os
.remove(blockdev_target_img
)
65 def do_test_cancel(self
, cmd
, target
):
66 self
.assert_no_active_block_jobs()
68 result
= self
.vm
.qmp(cmd
, device
='drive0', target
=target
, sync
='full')
69 self
.assert_qmp(result
, 'return', {})
71 event
= self
.cancel_and_wait()
72 self
.assert_qmp(event
, 'data/type', 'backup')
74 def test_cancel_drive_backup(self
):
75 self
.do_test_cancel('drive-backup', target_img
)
77 def test_cancel_blockdev_backup(self
):
78 self
.do_test_cancel('blockdev-backup', 'drive1')
80 def do_test_pause(self
, cmd
, target
, image
):
81 self
.assert_no_active_block_jobs()
83 self
.vm
.pause_drive('drive0')
84 result
= self
.vm
.qmp(cmd
, device
='drive0',
85 target
=target
, sync
='full')
86 self
.assert_qmp(result
, 'return', {})
88 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
89 self
.assert_qmp(result
, 'return', {})
91 self
.vm
.resume_drive('drive0')
92 self
.pause_job('drive0')
94 result
= self
.vm
.qmp('query-block-jobs')
95 offset
= self
.dictpath(result
, 'return[0]/offset')
98 result
= self
.vm
.qmp('query-block-jobs')
99 self
.assert_qmp(result
, 'return[0]/offset', offset
)
101 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
102 self
.assert_qmp(result
, 'return', {})
104 self
.wait_until_completed()
107 self
.assertTrue(iotests
.compare_images(test_img
, image
),
108 'target image does not match source after backup')
110 def test_pause_drive_backup(self
):
111 self
.do_test_pause('drive-backup', target_img
, target_img
)
113 def test_pause_blockdev_backup(self
):
114 self
.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img
)
116 def test_medium_not_found(self
):
117 if iotests
.qemu_default_machine
!= 'pc':
120 result
= self
.vm
.qmp('drive-backup', device
='drive2', # CD-ROM
121 target
=target_img
, sync
='full')
122 self
.assert_qmp(result
, 'error/class', 'GenericError')
124 def test_medium_not_found_blockdev_backup(self
):
125 if iotests
.qemu_default_machine
!= 'pc':
128 result
= self
.vm
.qmp('blockdev-backup', device
='drive2', # CD-ROM
129 target
='drive1', sync
='full')
130 self
.assert_qmp(result
, 'error/class', 'GenericError')
132 def test_image_not_found(self
):
133 result
= self
.vm
.qmp('drive-backup', device
='drive0',
134 target
=target_img
, sync
='full', mode
='existing')
135 self
.assert_qmp(result
, 'error/class', 'GenericError')
137 def test_invalid_format(self
):
138 result
= self
.vm
.qmp('drive-backup', device
='drive0',
139 target
=target_img
, sync
='full',
140 format
='spaghetti-noodles')
141 self
.assert_qmp(result
, 'error/class', 'GenericError')
143 def do_test_device_not_found(self
, cmd
, **args
):
144 result
= self
.vm
.qmp(cmd
, **args
)
145 self
.assert_qmp(result
, 'error/class', 'GenericError')
147 def test_device_not_found(self
):
148 self
.do_test_device_not_found('drive-backup', device
='nonexistent',
149 target
=target_img
, sync
='full')
151 self
.do_test_device_not_found('blockdev-backup', device
='nonexistent',
152 target
='drive0', sync
='full')
154 self
.do_test_device_not_found('blockdev-backup', device
='drive0',
155 target
='nonexistent', sync
='full')
157 self
.do_test_device_not_found('blockdev-backup', device
='nonexistent',
158 target
='nonexistent', sync
='full')
160 def test_target_is_source(self
):
161 result
= self
.vm
.qmp('blockdev-backup', device
='drive0',
162 target
='drive0', sync
='full')
163 self
.assert_qmp(result
, 'error/class', 'GenericError')
165 class TestSetSpeed(iotests
.QMPTestCase
):
167 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(image_len
))
169 self
.vm
= iotests
.VM().add_drive(test_img
)
170 self
.vm
.add_drive(blockdev_target_img
, interface
="none")
175 os
.remove(blockdev_target_img
)
177 os
.remove(target_img
)
181 def do_test_set_speed(self
, cmd
, target
):
182 self
.assert_no_active_block_jobs()
184 self
.vm
.pause_drive('drive0')
185 result
= self
.vm
.qmp(cmd
, device
='drive0', target
=target
, sync
='full')
186 self
.assert_qmp(result
, 'return', {})
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)
193 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
194 self
.assert_qmp(result
, 'return', {})
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)
201 event
= self
.cancel_and_wait(resume
=True)
202 self
.assert_qmp(event
, 'data/type', 'backup')
204 # Check setting speed option works
205 self
.vm
.pause_drive('drive0')
206 result
= self
.vm
.qmp(cmd
, device
='drive0',
207 target
=target
, sync
='full', speed
=4*1024*1024)
208 self
.assert_qmp(result
, 'return', {})
210 result
= self
.vm
.qmp('query-block-jobs')
211 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
212 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
214 event
= self
.cancel_and_wait(resume
=True)
215 self
.assert_qmp(event
, 'data/type', 'backup')
217 def test_set_speed_drive_backup(self
):
218 self
.do_test_set_speed('drive-backup', target_img
)
220 def test_set_speed_blockdev_backup(self
):
221 self
.do_test_set_speed('blockdev-backup', 'drive1')
223 def do_test_set_speed_invalid(self
, cmd
, target
):
224 self
.assert_no_active_block_jobs()
226 result
= self
.vm
.qmp(cmd
, device
='drive0',
227 target
=target
, sync
='full', speed
=-1)
228 self
.assert_qmp(result
, 'error/class', 'GenericError')
230 self
.assert_no_active_block_jobs()
232 self
.vm
.pause_drive('drive0')
233 result
= self
.vm
.qmp(cmd
, device
='drive0',
234 target
=target
, sync
='full')
235 self
.assert_qmp(result
, 'return', {})
237 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
238 self
.assert_qmp(result
, 'error/class', 'GenericError')
240 event
= self
.cancel_and_wait(resume
=True)
241 self
.assert_qmp(event
, 'data/type', 'backup')
243 def test_set_speed_invalid_drive_backup(self
):
244 self
.do_test_set_speed_invalid('drive-backup', target_img
)
246 def test_set_speed_invalid_blockdev_backup(self
):
247 self
.do_test_set_speed_invalid('blockdev-backup', 'drive1')
249 class TestSingleTransaction(iotests
.QMPTestCase
):
251 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(image_len
))
253 self
.vm
= iotests
.VM().add_drive(test_img
)
254 self
.vm
.add_drive(blockdev_target_img
, interface
="none")
255 if iotests
.qemu_default_machine
== 'pc':
256 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
261 os
.remove(blockdev_target_img
)
263 os
.remove(target_img
)
267 def do_test_cancel(self
, cmd
, target
):
268 self
.assert_no_active_block_jobs()
270 result
= self
.vm
.qmp('transaction', actions
=[{
272 'data': { 'device': 'drive0',
278 self
.assert_qmp(result
, 'return', {})
280 event
= self
.cancel_and_wait()
281 self
.assert_qmp(event
, 'data/type', 'backup')
283 def test_cancel_drive_backup(self
):
284 self
.do_test_cancel('drive-backup', target_img
)
286 def test_cancel_blockdev_backup(self
):
287 self
.do_test_cancel('blockdev-backup', 'drive1')
289 def do_test_pause(self
, cmd
, target
, image
):
290 self
.assert_no_active_block_jobs()
292 self
.vm
.pause_drive('drive0')
293 result
= self
.vm
.qmp('transaction', actions
=[{
295 'data': { 'device': 'drive0',
300 self
.assert_qmp(result
, 'return', {})
302 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
303 self
.assert_qmp(result
, 'return', {})
305 self
.vm
.resume_drive('drive0')
306 self
.pause_job('drive0')
308 result
= self
.vm
.qmp('query-block-jobs')
309 offset
= self
.dictpath(result
, 'return[0]/offset')
312 result
= self
.vm
.qmp('query-block-jobs')
313 self
.assert_qmp(result
, 'return[0]/offset', offset
)
315 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
316 self
.assert_qmp(result
, 'return', {})
318 self
.wait_until_completed()
321 self
.assertTrue(iotests
.compare_images(test_img
, image
),
322 'target image does not match source after backup')
324 def test_pause_drive_backup(self
):
325 self
.do_test_pause('drive-backup', target_img
, target_img
)
327 def test_pause_blockdev_backup(self
):
328 self
.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img
)
330 def do_test_medium_not_found(self
, cmd
, target
):
331 if iotests
.qemu_default_machine
!= 'pc':
334 result
= self
.vm
.qmp('transaction', actions
=[{
336 'data': { 'device': 'drive2', # CD-ROM
341 self
.assert_qmp(result
, 'error/class', 'GenericError')
343 def test_medium_not_found_drive_backup(self
):
344 self
.do_test_medium_not_found('drive-backup', target_img
)
346 def test_medium_not_found_blockdev_backup(self
):
347 self
.do_test_medium_not_found('blockdev-backup', 'drive1')
349 def test_image_not_found(self
):
350 result
= self
.vm
.qmp('transaction', actions
=[{
351 'type': 'drive-backup',
352 'data': { 'device': 'drive0',
354 'target': target_img
,
358 self
.assert_qmp(result
, 'error/class', 'GenericError')
360 def test_device_not_found(self
):
361 result
= self
.vm
.qmp('transaction', actions
=[{
362 'type': 'drive-backup',
363 'data': { 'device': 'nonexistent',
365 'target': target_img
,
369 self
.assert_qmp(result
, 'error/class', 'GenericError')
371 result
= self
.vm
.qmp('transaction', actions
=[{
372 'type': 'blockdev-backup',
373 'data': { 'device': 'nonexistent',
378 self
.assert_qmp(result
, 'error/class', 'GenericError')
380 result
= self
.vm
.qmp('transaction', actions
=[{
381 'type': 'blockdev-backup',
382 'data': { 'device': 'drive0',
383 'target': 'nonexistent',
387 self
.assert_qmp(result
, 'error/class', 'GenericError')
389 result
= self
.vm
.qmp('transaction', actions
=[{
390 'type': 'blockdev-backup',
391 'data': { 'device': 'nonexistent',
392 'target': 'nonexistent',
396 self
.assert_qmp(result
, 'error/class', 'GenericError')
398 def test_target_is_source(self
):
399 result
= self
.vm
.qmp('transaction', actions
=[{
400 'type': 'blockdev-backup',
401 'data': { 'device': 'drive0',
406 self
.assert_qmp(result
, 'error/class', 'GenericError')
408 def test_abort(self
):
409 result
= self
.vm
.qmp('transaction', actions
=[{
410 'type': 'drive-backup',
411 'data': { 'device': 'nonexistent',
413 'target': target_img
,
420 self
.assert_qmp(result
, 'error/class', 'GenericError')
421 self
.assert_no_active_block_jobs()
423 result
= self
.vm
.qmp('transaction', actions
=[{
424 'type': 'blockdev-backup',
425 'data': { 'device': 'nonexistent',
433 self
.assert_qmp(result
, 'error/class', 'GenericError')
434 self
.assert_no_active_block_jobs()
436 result
= self
.vm
.qmp('transaction', actions
=[{
437 'type': 'blockdev-backup',
438 'data': { 'device': 'drive0',
439 'target': 'nonexistent',
446 self
.assert_qmp(result
, 'error/class', 'GenericError')
447 self
.assert_no_active_block_jobs()
450 class TestDriveCompression(iotests
.QMPTestCase
):
451 image_len
= 64 * 1024 * 1024 # MB
452 fmt_supports_compression
= [{'type': 'qcow2', 'args': ()},
453 {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
457 os
.remove(blockdev_target_img
)
459 os
.remove(target_img
)
463 def do_prepare_drives(self
, fmt
, args
, attach_target
):
464 self
.vm
= iotests
.VM().add_drive(test_img
)
466 qemu_img('create', '-f', fmt
, blockdev_target_img
,
467 str(TestDriveCompression
.image_len
), *args
)
469 self
.vm
.add_drive(blockdev_target_img
, format
=fmt
, interface
="none")
473 def do_test_compress_complete(self
, cmd
, format
, attach_target
, **args
):
474 self
.do_prepare_drives(format
['type'], format
['args'], attach_target
)
476 self
.assert_no_active_block_jobs()
478 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
479 self
.assert_qmp(result
, 'return', {})
481 self
.wait_until_completed()
484 self
.assertTrue(iotests
.compare_images(test_img
, blockdev_target_img
,
485 iotests
.imgfmt
, format
['type']),
486 'target image does not match source after backup')
488 def test_complete_compress_drive_backup(self
):
489 for format
in TestDriveCompression
.fmt_supports_compression
:
490 self
.do_test_compress_complete('drive-backup', format
, False,
491 target
=blockdev_target_img
, mode
='existing')
493 def test_complete_compress_blockdev_backup(self
):
494 for format
in TestDriveCompression
.fmt_supports_compression
:
495 self
.do_test_compress_complete('blockdev-backup', format
, True,
498 def do_test_compress_cancel(self
, cmd
, format
, attach_target
, **args
):
499 self
.do_prepare_drives(format
['type'], format
['args'], attach_target
)
501 self
.assert_no_active_block_jobs()
503 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
504 self
.assert_qmp(result
, 'return', {})
506 event
= self
.cancel_and_wait()
507 self
.assert_qmp(event
, 'data/type', 'backup')
511 def test_compress_cancel_drive_backup(self
):
512 for format
in TestDriveCompression
.fmt_supports_compression
:
513 self
.do_test_compress_cancel('drive-backup', format
, False,
514 target
=blockdev_target_img
, mode
='existing')
516 def test_compress_cancel_blockdev_backup(self
):
517 for format
in TestDriveCompression
.fmt_supports_compression
:
518 self
.do_test_compress_cancel('blockdev-backup', format
, True,
521 def do_test_compress_pause(self
, cmd
, format
, attach_target
, **args
):
522 self
.do_prepare_drives(format
['type'], format
['args'], attach_target
)
524 self
.assert_no_active_block_jobs()
526 self
.vm
.pause_drive('drive0')
527 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
528 self
.assert_qmp(result
, 'return', {})
530 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
531 self
.assert_qmp(result
, 'return', {})
533 self
.vm
.resume_drive('drive0')
534 self
.pause_job('drive0')
536 result
= self
.vm
.qmp('query-block-jobs')
537 offset
= self
.dictpath(result
, 'return[0]/offset')
540 result
= self
.vm
.qmp('query-block-jobs')
541 self
.assert_qmp(result
, 'return[0]/offset', offset
)
543 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
544 self
.assert_qmp(result
, 'return', {})
546 self
.wait_until_completed()
549 self
.assertTrue(iotests
.compare_images(test_img
, blockdev_target_img
,
550 iotests
.imgfmt
, format
['type']),
551 'target image does not match source after backup')
553 def test_compress_pause_drive_backup(self
):
554 for format
in TestDriveCompression
.fmt_supports_compression
:
555 self
.do_test_compress_pause('drive-backup', format
, False,
556 target
=blockdev_target_img
, mode
='existing')
558 def test_compress_pause_blockdev_backup(self
):
559 for format
in TestDriveCompression
.fmt_supports_compression
:
560 self
.do_test_compress_pause('blockdev-backup', format
, True,
563 if __name__
== '__main__':
564 iotests
.main(supported_fmts
=['raw', 'qcow2'])