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
).add_drive(blockdev_target_img
)
52 if iotests
.qemu_default_machine
== 'pc':
53 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
58 os
.remove(blockdev_target_img
)
64 def do_test_cancel(self
, cmd
, target
):
65 self
.assert_no_active_block_jobs()
67 result
= self
.vm
.qmp(cmd
, device
='drive0', target
=target
, sync
='full')
68 self
.assert_qmp(result
, 'return', {})
70 event
= self
.cancel_and_wait()
71 self
.assert_qmp(event
, 'data/type', 'backup')
73 def test_cancel_drive_backup(self
):
74 self
.do_test_cancel('drive-backup', target_img
)
76 def test_cancel_blockdev_backup(self
):
77 self
.do_test_cancel('blockdev-backup', 'drive1')
79 def do_test_pause(self
, cmd
, target
, image
):
80 self
.assert_no_active_block_jobs()
82 self
.vm
.pause_drive('drive0')
83 result
= self
.vm
.qmp(cmd
, device
='drive0',
84 target
=target
, sync
='full')
85 self
.assert_qmp(result
, 'return', {})
87 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
88 self
.assert_qmp(result
, 'return', {})
90 self
.vm
.resume_drive('drive0')
92 result
= self
.vm
.qmp('query-block-jobs')
93 offset
= self
.dictpath(result
, 'return[0]/offset')
96 result
= self
.vm
.qmp('query-block-jobs')
97 self
.assert_qmp(result
, 'return[0]/offset', offset
)
99 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
100 self
.assert_qmp(result
, 'return', {})
102 self
.wait_until_completed()
105 self
.assertTrue(iotests
.compare_images(test_img
, image
),
106 'target image does not match source after backup')
108 def test_pause_drive_backup(self
):
109 self
.do_test_pause('drive-backup', target_img
, target_img
)
111 def test_pause_blockdev_backup(self
):
112 self
.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img
)
114 def test_medium_not_found(self
):
115 if iotests
.qemu_default_machine
!= 'pc':
118 result
= self
.vm
.qmp('drive-backup', device
='drive2', # CD-ROM
119 target
=target_img
, sync
='full')
120 self
.assert_qmp(result
, 'error/class', 'GenericError')
122 def test_medium_not_found_blockdev_backup(self
):
123 if iotests
.qemu_default_machine
!= 'pc':
126 result
= self
.vm
.qmp('blockdev-backup', device
='drive2', # CD-ROM
127 target
='drive1', sync
='full')
128 self
.assert_qmp(result
, 'error/class', 'GenericError')
130 def test_image_not_found(self
):
131 result
= self
.vm
.qmp('drive-backup', device
='drive0',
132 target
=target_img
, sync
='full', mode
='existing')
133 self
.assert_qmp(result
, 'error/class', 'GenericError')
135 def test_invalid_format(self
):
136 result
= self
.vm
.qmp('drive-backup', device
='drive0',
137 target
=target_img
, sync
='full',
138 format
='spaghetti-noodles')
139 self
.assert_qmp(result
, 'error/class', 'GenericError')
141 def do_test_device_not_found(self
, cmd
, **args
):
142 result
= self
.vm
.qmp(cmd
, **args
)
143 self
.assert_qmp(result
, 'error/class', 'GenericError')
145 def test_device_not_found(self
):
146 self
.do_test_device_not_found('drive-backup', device
='nonexistent',
147 target
=target_img
, sync
='full')
149 self
.do_test_device_not_found('blockdev-backup', device
='nonexistent',
150 target
='drive0', sync
='full')
152 self
.do_test_device_not_found('blockdev-backup', device
='drive0',
153 target
='nonexistent', sync
='full')
155 self
.do_test_device_not_found('blockdev-backup', device
='nonexistent',
156 target
='nonexistent', sync
='full')
158 def test_target_is_source(self
):
159 result
= self
.vm
.qmp('blockdev-backup', device
='drive0',
160 target
='drive0', sync
='full')
161 self
.assert_qmp(result
, 'error/class', 'GenericError')
163 class TestSetSpeed(iotests
.QMPTestCase
):
165 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(image_len
))
167 self
.vm
= iotests
.VM().add_drive(test_img
).add_drive(blockdev_target_img
)
172 os
.remove(blockdev_target_img
)
174 os
.remove(target_img
)
178 def do_test_set_speed(self
, cmd
, target
):
179 self
.assert_no_active_block_jobs()
181 self
.vm
.pause_drive('drive0')
182 result
= self
.vm
.qmp(cmd
, device
='drive0', target
=target
, sync
='full')
183 self
.assert_qmp(result
, 'return', {})
186 result
= self
.vm
.qmp('query-block-jobs')
187 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
188 self
.assert_qmp(result
, 'return[0]/speed', 0)
190 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
191 self
.assert_qmp(result
, 'return', {})
193 # Ensure the speed we set was accepted
194 result
= self
.vm
.qmp('query-block-jobs')
195 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
196 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
198 event
= self
.cancel_and_wait(resume
=True)
199 self
.assert_qmp(event
, 'data/type', 'backup')
201 # Check setting speed option works
202 self
.vm
.pause_drive('drive0')
203 result
= self
.vm
.qmp(cmd
, device
='drive0',
204 target
=target
, sync
='full', speed
=4*1024*1024)
205 self
.assert_qmp(result
, 'return', {})
207 result
= self
.vm
.qmp('query-block-jobs')
208 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
209 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
211 event
= self
.cancel_and_wait(resume
=True)
212 self
.assert_qmp(event
, 'data/type', 'backup')
214 def test_set_speed_drive_backup(self
):
215 self
.do_test_set_speed('drive-backup', target_img
)
217 def test_set_speed_blockdev_backup(self
):
218 self
.do_test_set_speed('blockdev-backup', 'drive1')
220 def do_test_set_speed_invalid(self
, cmd
, target
):
221 self
.assert_no_active_block_jobs()
223 result
= self
.vm
.qmp(cmd
, device
='drive0',
224 target
=target
, sync
='full', speed
=-1)
225 self
.assert_qmp(result
, 'error/class', 'GenericError')
227 self
.assert_no_active_block_jobs()
229 self
.vm
.pause_drive('drive0')
230 result
= self
.vm
.qmp(cmd
, device
='drive0',
231 target
=target
, sync
='full')
232 self
.assert_qmp(result
, 'return', {})
234 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
235 self
.assert_qmp(result
, 'error/class', 'GenericError')
237 event
= self
.cancel_and_wait(resume
=True)
238 self
.assert_qmp(event
, 'data/type', 'backup')
240 def test_set_speed_invalid_drive_backup(self
):
241 self
.do_test_set_speed_invalid('drive-backup', target_img
)
243 def test_set_speed_invalid_blockdev_backup(self
):
244 self
.do_test_set_speed_invalid('blockdev-backup', 'drive1')
246 class TestSingleTransaction(iotests
.QMPTestCase
):
248 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(image_len
))
250 self
.vm
= iotests
.VM().add_drive(test_img
).add_drive(blockdev_target_img
)
251 if iotests
.qemu_default_machine
== 'pc':
252 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
257 os
.remove(blockdev_target_img
)
259 os
.remove(target_img
)
263 def do_test_cancel(self
, cmd
, target
):
264 self
.assert_no_active_block_jobs()
266 result
= self
.vm
.qmp('transaction', actions
=[{
268 'data': { 'device': 'drive0',
274 self
.assert_qmp(result
, 'return', {})
276 event
= self
.cancel_and_wait()
277 self
.assert_qmp(event
, 'data/type', 'backup')
279 def test_cancel_drive_backup(self
):
280 self
.do_test_cancel('drive-backup', target_img
)
282 def test_cancel_blockdev_backup(self
):
283 self
.do_test_cancel('blockdev-backup', 'drive1')
285 def do_test_pause(self
, cmd
, target
, image
):
286 self
.assert_no_active_block_jobs()
288 self
.vm
.pause_drive('drive0')
289 result
= self
.vm
.qmp('transaction', actions
=[{
291 'data': { 'device': 'drive0',
296 self
.assert_qmp(result
, 'return', {})
298 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
299 self
.assert_qmp(result
, 'return', {})
301 self
.vm
.resume_drive('drive0')
303 result
= self
.vm
.qmp('query-block-jobs')
304 offset
= self
.dictpath(result
, 'return[0]/offset')
307 result
= self
.vm
.qmp('query-block-jobs')
308 self
.assert_qmp(result
, 'return[0]/offset', offset
)
310 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
311 self
.assert_qmp(result
, 'return', {})
313 self
.wait_until_completed()
316 self
.assertTrue(iotests
.compare_images(test_img
, image
),
317 'target image does not match source after backup')
319 def test_pause_drive_backup(self
):
320 self
.do_test_pause('drive-backup', target_img
, target_img
)
322 def test_pause_blockdev_backup(self
):
323 self
.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img
)
325 def do_test_medium_not_found(self
, cmd
, target
):
326 if iotests
.qemu_default_machine
!= 'pc':
329 result
= self
.vm
.qmp('transaction', actions
=[{
331 'data': { 'device': 'drive2', # CD-ROM
336 self
.assert_qmp(result
, 'error/class', 'GenericError')
338 def test_medium_not_found_drive_backup(self
):
339 self
.do_test_medium_not_found('drive-backup', target_img
)
341 def test_medium_not_found_blockdev_backup(self
):
342 self
.do_test_medium_not_found('blockdev-backup', 'drive1')
344 def test_image_not_found(self
):
345 result
= self
.vm
.qmp('transaction', actions
=[{
346 'type': 'drive-backup',
347 'data': { 'device': 'drive0',
349 'target': target_img
,
353 self
.assert_qmp(result
, 'error/class', 'GenericError')
355 def test_device_not_found(self
):
356 result
= self
.vm
.qmp('transaction', actions
=[{
357 'type': 'drive-backup',
358 'data': { 'device': 'nonexistent',
360 'target': target_img
,
364 self
.assert_qmp(result
, 'error/class', 'GenericError')
366 result
= self
.vm
.qmp('transaction', actions
=[{
367 'type': 'blockdev-backup',
368 'data': { 'device': 'nonexistent',
373 self
.assert_qmp(result
, 'error/class', 'GenericError')
375 result
= self
.vm
.qmp('transaction', actions
=[{
376 'type': 'blockdev-backup',
377 'data': { 'device': 'drive0',
378 'target': 'nonexistent',
382 self
.assert_qmp(result
, 'error/class', 'GenericError')
384 result
= self
.vm
.qmp('transaction', actions
=[{
385 'type': 'blockdev-backup',
386 'data': { 'device': 'nonexistent',
387 'target': 'nonexistent',
391 self
.assert_qmp(result
, 'error/class', 'GenericError')
393 def test_target_is_source(self
):
394 result
= self
.vm
.qmp('transaction', actions
=[{
395 'type': 'blockdev-backup',
396 'data': { 'device': 'drive0',
401 self
.assert_qmp(result
, 'error/class', 'GenericError')
403 def test_abort(self
):
404 result
= self
.vm
.qmp('transaction', actions
=[{
405 'type': 'drive-backup',
406 'data': { 'device': 'nonexistent',
408 'target': target_img
,
415 self
.assert_qmp(result
, 'error/class', 'GenericError')
416 self
.assert_no_active_block_jobs()
418 result
= self
.vm
.qmp('transaction', actions
=[{
419 'type': 'blockdev-backup',
420 'data': { 'device': 'nonexistent',
428 self
.assert_qmp(result
, 'error/class', 'GenericError')
429 self
.assert_no_active_block_jobs()
431 result
= self
.vm
.qmp('transaction', actions
=[{
432 'type': 'blockdev-backup',
433 'data': { 'device': 'drive0',
434 'target': 'nonexistent',
441 self
.assert_qmp(result
, 'error/class', 'GenericError')
442 self
.assert_no_active_block_jobs()
445 class TestDriveCompression(iotests
.QMPTestCase
):
446 image_len
= 64 * 1024 * 1024 # MB
447 fmt_supports_compression
= [{'type': 'qcow2', 'args': ()},
448 {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
452 os
.remove(blockdev_target_img
)
454 os
.remove(target_img
)
458 def do_prepare_drives(self
, fmt
, args
):
459 self
.vm
= iotests
.VM().add_drive(test_img
)
461 qemu_img('create', '-f', fmt
, blockdev_target_img
,
462 str(TestDriveCompression
.image_len
), *args
)
463 self
.vm
.add_drive(blockdev_target_img
, format
=fmt
)
467 def do_test_compress_complete(self
, cmd
, format
, **args
):
468 self
.do_prepare_drives(format
['type'], format
['args'])
470 self
.assert_no_active_block_jobs()
472 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
473 self
.assert_qmp(result
, 'return', {})
475 self
.wait_until_completed()
478 self
.assertTrue(iotests
.compare_images(test_img
, blockdev_target_img
,
479 iotests
.imgfmt
, format
['type']),
480 'target image does not match source after backup')
482 def test_complete_compress_drive_backup(self
):
483 for format
in TestDriveCompression
.fmt_supports_compression
:
484 self
.do_test_compress_complete('drive-backup', format
,
485 target
=blockdev_target_img
, mode
='existing')
487 def test_complete_compress_blockdev_backup(self
):
488 for format
in TestDriveCompression
.fmt_supports_compression
:
489 self
.do_test_compress_complete('blockdev-backup', format
, target
='drive1')
491 def do_test_compress_cancel(self
, cmd
, format
, **args
):
492 self
.do_prepare_drives(format
['type'], format
['args'])
494 self
.assert_no_active_block_jobs()
496 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
497 self
.assert_qmp(result
, 'return', {})
499 event
= self
.cancel_and_wait()
500 self
.assert_qmp(event
, 'data/type', 'backup')
504 def test_compress_cancel_drive_backup(self
):
505 for format
in TestDriveCompression
.fmt_supports_compression
:
506 self
.do_test_compress_cancel('drive-backup', format
,
507 target
=blockdev_target_img
, mode
='existing')
509 def test_compress_cancel_blockdev_backup(self
):
510 for format
in TestDriveCompression
.fmt_supports_compression
:
511 self
.do_test_compress_cancel('blockdev-backup', format
, target
='drive1')
513 def do_test_compress_pause(self
, cmd
, format
, **args
):
514 self
.do_prepare_drives(format
['type'], format
['args'])
516 self
.assert_no_active_block_jobs()
518 self
.vm
.pause_drive('drive0')
519 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
520 self
.assert_qmp(result
, 'return', {})
522 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
523 self
.assert_qmp(result
, 'return', {})
525 self
.vm
.resume_drive('drive0')
527 result
= self
.vm
.qmp('query-block-jobs')
528 offset
= self
.dictpath(result
, 'return[0]/offset')
531 result
= self
.vm
.qmp('query-block-jobs')
532 self
.assert_qmp(result
, 'return[0]/offset', offset
)
534 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
535 self
.assert_qmp(result
, 'return', {})
537 self
.wait_until_completed()
540 self
.assertTrue(iotests
.compare_images(test_img
, blockdev_target_img
,
541 iotests
.imgfmt
, format
['type']),
542 'target image does not match source after backup')
544 def test_compress_pause_drive_backup(self
):
545 for format
in TestDriveCompression
.fmt_supports_compression
:
546 self
.do_test_compress_pause('drive-backup', format
,
547 target
=blockdev_target_img
, mode
='existing')
549 def test_compress_pause_blockdev_backup(self
):
550 for format
in TestDriveCompression
.fmt_supports_compression
:
551 self
.do_test_compress_pause('blockdev-backup', format
, target
='drive1')
553 if __name__
== '__main__':
554 iotests
.main(supported_fmts
=['raw', 'qcow2'])