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 class TestSingleDrive(iotests
.QMPTestCase
):
33 image_len
= 64 * 1024 * 1024 # MB
36 # Write data to the image so we can compare later
37 qemu_img('create', '-f', iotests
.imgfmt
, test_img
, str(TestSingleDrive
.image_len
))
38 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x5d 0 64k', 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 -P0xdc 67043328 64k', test_img
)
42 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(TestSingleDrive
.image_len
))
44 self
.vm
= iotests
.VM().add_drive(test_img
).add_drive(blockdev_target_img
)
45 if iotests
.qemu_default_machine
== 'pc':
46 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
52 os
.remove(blockdev_target_img
)
58 def do_test_cancel(self
, cmd
, target
):
59 self
.assert_no_active_block_jobs()
61 result
= self
.vm
.qmp(cmd
, device
='drive0', target
=target
, sync
='full')
62 self
.assert_qmp(result
, 'return', {})
64 event
= self
.cancel_and_wait()
65 self
.assert_qmp(event
, 'data/type', 'backup')
67 def test_cancel_drive_backup(self
):
68 self
.do_test_cancel('drive-backup', target_img
)
70 def test_cancel_blockdev_backup(self
):
71 self
.do_test_cancel('blockdev-backup', 'drive1')
73 def do_test_pause(self
, cmd
, target
, image
):
74 self
.assert_no_active_block_jobs()
76 self
.vm
.pause_drive('drive0')
77 result
= self
.vm
.qmp(cmd
, device
='drive0',
78 target
=target
, sync
='full')
79 self
.assert_qmp(result
, 'return', {})
81 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
82 self
.assert_qmp(result
, 'return', {})
84 self
.vm
.resume_drive('drive0')
86 result
= self
.vm
.qmp('query-block-jobs')
87 offset
= self
.dictpath(result
, 'return[0]/offset')
90 result
= self
.vm
.qmp('query-block-jobs')
91 self
.assert_qmp(result
, 'return[0]/offset', offset
)
93 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
94 self
.assert_qmp(result
, 'return', {})
96 self
.wait_until_completed()
99 self
.assertTrue(iotests
.compare_images(test_img
, image
),
100 'target image does not match source after backup')
102 def test_pause_drive_backup(self
):
103 self
.do_test_pause('drive-backup', target_img
, target_img
)
105 def test_pause_blockdev_backup(self
):
106 self
.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img
)
108 def test_medium_not_found(self
):
109 if iotests
.qemu_default_machine
!= 'pc':
112 result
= self
.vm
.qmp('drive-backup', device
='drive2', # CD-ROM
113 target
=target_img
, sync
='full')
114 self
.assert_qmp(result
, 'error/class', 'GenericError')
116 def test_medium_not_found_blockdev_backup(self
):
117 if iotests
.qemu_default_machine
!= 'pc':
120 result
= self
.vm
.qmp('blockdev-backup', device
='drive2', # CD-ROM
121 target
='drive1', sync
='full')
122 self
.assert_qmp(result
, 'error/class', 'GenericError')
124 def test_image_not_found(self
):
125 result
= self
.vm
.qmp('drive-backup', device
='drive0',
126 target
=target_img
, sync
='full', mode
='existing')
127 self
.assert_qmp(result
, 'error/class', 'GenericError')
129 def test_invalid_format(self
):
130 result
= self
.vm
.qmp('drive-backup', device
='drive0',
131 target
=target_img
, sync
='full',
132 format
='spaghetti-noodles')
133 self
.assert_qmp(result
, 'error/class', 'GenericError')
135 def do_test_device_not_found(self
, cmd
, **args
):
136 result
= self
.vm
.qmp(cmd
, **args
)
137 self
.assert_qmp(result
, 'error/class', 'GenericError')
139 def test_device_not_found(self
):
140 self
.do_test_device_not_found('drive-backup', device
='nonexistent',
141 target
=target_img
, sync
='full')
143 self
.do_test_device_not_found('blockdev-backup', device
='nonexistent',
144 target
='drive0', sync
='full')
146 self
.do_test_device_not_found('blockdev-backup', device
='drive0',
147 target
='nonexistent', sync
='full')
149 self
.do_test_device_not_found('blockdev-backup', device
='nonexistent',
150 target
='nonexistent', sync
='full')
152 def test_target_is_source(self
):
153 result
= self
.vm
.qmp('blockdev-backup', device
='drive0',
154 target
='drive0', sync
='full')
155 self
.assert_qmp(result
, 'error/class', 'GenericError')
157 class TestSetSpeed(iotests
.QMPTestCase
):
158 image_len
= 80 * 1024 * 1024 # MB
161 qemu_img('create', '-f', iotests
.imgfmt
, test_img
, str(TestSetSpeed
.image_len
))
162 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P1 0 512', test_img
)
163 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(TestSingleDrive
.image_len
))
165 self
.vm
= iotests
.VM().add_drive(test_img
).add_drive(blockdev_target_img
)
171 os
.remove(blockdev_target_img
)
173 os
.remove(target_img
)
177 def do_test_set_speed(self
, cmd
, target
):
178 self
.assert_no_active_block_jobs()
180 self
.vm
.pause_drive('drive0')
181 result
= self
.vm
.qmp(cmd
, device
='drive0', target
=target
, sync
='full')
182 self
.assert_qmp(result
, 'return', {})
185 result
= self
.vm
.qmp('query-block-jobs')
186 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
187 self
.assert_qmp(result
, 'return[0]/speed', 0)
189 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
190 self
.assert_qmp(result
, 'return', {})
192 # Ensure the speed we set was accepted
193 result
= self
.vm
.qmp('query-block-jobs')
194 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
195 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
197 event
= self
.cancel_and_wait(resume
=True)
198 self
.assert_qmp(event
, 'data/type', 'backup')
200 # Check setting speed option works
201 self
.vm
.pause_drive('drive0')
202 result
= self
.vm
.qmp(cmd
, device
='drive0',
203 target
=target
, sync
='full', speed
=4*1024*1024)
204 self
.assert_qmp(result
, 'return', {})
206 result
= self
.vm
.qmp('query-block-jobs')
207 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
208 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
210 event
= self
.cancel_and_wait(resume
=True)
211 self
.assert_qmp(event
, 'data/type', 'backup')
213 def test_set_speed_drive_backup(self
):
214 self
.do_test_set_speed('drive-backup', target_img
)
216 def test_set_speed_blockdev_backup(self
):
217 self
.do_test_set_speed('blockdev-backup', 'drive1')
219 def do_test_set_speed_invalid(self
, cmd
, target
):
220 self
.assert_no_active_block_jobs()
222 result
= self
.vm
.qmp(cmd
, device
='drive0',
223 target
=target
, sync
='full', speed
=-1)
224 self
.assert_qmp(result
, 'error/class', 'GenericError')
226 self
.assert_no_active_block_jobs()
228 self
.vm
.pause_drive('drive0')
229 result
= self
.vm
.qmp(cmd
, device
='drive0',
230 target
=target
, sync
='full')
231 self
.assert_qmp(result
, 'return', {})
233 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
234 self
.assert_qmp(result
, 'error/class', 'GenericError')
236 event
= self
.cancel_and_wait(resume
=True)
237 self
.assert_qmp(event
, 'data/type', 'backup')
239 def test_set_speed_invalid_drive_backup(self
):
240 self
.do_test_set_speed_invalid('drive-backup', target_img
)
242 def test_set_speed_invalid_blockdev_backup(self
):
243 self
.do_test_set_speed_invalid('blockdev-backup', 'drive1')
245 class TestSingleTransaction(iotests
.QMPTestCase
):
246 image_len
= 64 * 1024 * 1024 # MB
249 qemu_img('create', '-f', iotests
.imgfmt
, test_img
, str(TestSingleTransaction
.image_len
))
250 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x5d 0 64k', test_img
)
251 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0xd5 1M 32k', test_img
)
252 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0xdc 32M 124k', test_img
)
253 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0xdc 67043328 64k', test_img
)
254 qemu_img('create', '-f', iotests
.imgfmt
, blockdev_target_img
, str(TestSingleDrive
.image_len
))
256 self
.vm
= iotests
.VM().add_drive(test_img
).add_drive(blockdev_target_img
)
257 if iotests
.qemu_default_machine
== 'pc':
258 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
264 os
.remove(blockdev_target_img
)
266 os
.remove(target_img
)
270 def do_test_cancel(self
, cmd
, target
):
271 self
.assert_no_active_block_jobs()
273 result
= self
.vm
.qmp('transaction', actions
=[{
275 'data': { 'device': 'drive0',
281 self
.assert_qmp(result
, 'return', {})
283 event
= self
.cancel_and_wait()
284 self
.assert_qmp(event
, 'data/type', 'backup')
286 def test_cancel_drive_backup(self
):
287 self
.do_test_cancel('drive-backup', target_img
)
289 def test_cancel_blockdev_backup(self
):
290 self
.do_test_cancel('blockdev-backup', 'drive1')
292 def do_test_pause(self
, cmd
, target
, image
):
293 self
.assert_no_active_block_jobs()
295 self
.vm
.pause_drive('drive0')
296 result
= self
.vm
.qmp('transaction', actions
=[{
298 'data': { 'device': 'drive0',
303 self
.assert_qmp(result
, 'return', {})
305 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
306 self
.assert_qmp(result
, 'return', {})
308 self
.vm
.resume_drive('drive0')
310 result
= self
.vm
.qmp('query-block-jobs')
311 offset
= self
.dictpath(result
, 'return[0]/offset')
314 result
= self
.vm
.qmp('query-block-jobs')
315 self
.assert_qmp(result
, 'return[0]/offset', offset
)
317 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
318 self
.assert_qmp(result
, 'return', {})
320 self
.wait_until_completed()
323 self
.assertTrue(iotests
.compare_images(test_img
, image
),
324 'target image does not match source after backup')
326 def test_pause_drive_backup(self
):
327 self
.do_test_pause('drive-backup', target_img
, target_img
)
329 def test_pause_blockdev_backup(self
):
330 self
.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img
)
332 def do_test_medium_not_found(self
, cmd
, target
):
333 if iotests
.qemu_default_machine
!= 'pc':
336 result
= self
.vm
.qmp('transaction', actions
=[{
338 'data': { 'device': 'drive2', # CD-ROM
343 self
.assert_qmp(result
, 'error/class', 'GenericError')
345 def test_medium_not_found_drive_backup(self
):
346 self
.do_test_medium_not_found('drive-backup', target_img
)
348 def test_medium_not_found_blockdev_backup(self
):
349 self
.do_test_medium_not_found('blockdev-backup', 'drive1')
351 def test_image_not_found(self
):
352 result
= self
.vm
.qmp('transaction', actions
=[{
353 'type': 'drive-backup',
354 'data': { 'device': 'drive0',
356 'target': target_img
,
360 self
.assert_qmp(result
, 'error/class', 'GenericError')
362 def test_device_not_found(self
):
363 result
= self
.vm
.qmp('transaction', actions
=[{
364 'type': 'drive-backup',
365 'data': { 'device': 'nonexistent',
367 'target': target_img
,
371 self
.assert_qmp(result
, 'error/class', 'GenericError')
373 result
= self
.vm
.qmp('transaction', actions
=[{
374 'type': 'blockdev-backup',
375 'data': { 'device': 'nonexistent',
380 self
.assert_qmp(result
, 'error/class', 'GenericError')
382 result
= self
.vm
.qmp('transaction', actions
=[{
383 'type': 'blockdev-backup',
384 'data': { 'device': 'drive0',
385 'target': 'nonexistent',
389 self
.assert_qmp(result
, 'error/class', 'GenericError')
391 result
= self
.vm
.qmp('transaction', actions
=[{
392 'type': 'blockdev-backup',
393 'data': { 'device': 'nonexistent',
394 'target': 'nonexistent',
398 self
.assert_qmp(result
, 'error/class', 'GenericError')
400 def test_target_is_source(self
):
401 result
= self
.vm
.qmp('transaction', actions
=[{
402 'type': 'blockdev-backup',
403 'data': { 'device': 'drive0',
408 self
.assert_qmp(result
, 'error/class', 'GenericError')
410 def test_abort(self
):
411 result
= self
.vm
.qmp('transaction', actions
=[{
412 'type': 'drive-backup',
413 'data': { 'device': 'nonexistent',
415 'target': target_img
,
422 self
.assert_qmp(result
, 'error/class', 'GenericError')
423 self
.assert_no_active_block_jobs()
425 result
= self
.vm
.qmp('transaction', actions
=[{
426 'type': 'blockdev-backup',
427 'data': { 'device': 'nonexistent',
435 self
.assert_qmp(result
, 'error/class', 'GenericError')
436 self
.assert_no_active_block_jobs()
438 result
= self
.vm
.qmp('transaction', actions
=[{
439 'type': 'blockdev-backup',
440 'data': { 'device': 'drive0',
441 'target': 'nonexistent',
448 self
.assert_qmp(result
, 'error/class', 'GenericError')
449 self
.assert_no_active_block_jobs()
452 class TestDriveCompression(iotests
.QMPTestCase
):
453 image_len
= 64 * 1024 * 1024 # MB
454 fmt_supports_compression
= [{'type': 'qcow2', 'args': ()},
455 {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
458 # Write data to the image so we can compare later
459 qemu_img('create', '-f', iotests
.imgfmt
, test_img
, str(TestDriveCompression
.image_len
))
460 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x11 0 64k', test_img
)
461 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x00 64k 128k', test_img
)
462 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x22 162k 32k', test_img
)
463 qemu_io('-f', iotests
.imgfmt
, '-c', 'write -P0x33 67043328 64k', test_img
)
468 os
.remove(blockdev_target_img
)
470 os
.remove(target_img
)
474 def do_prepare_drives(self
, fmt
, args
):
475 self
.vm
= iotests
.VM().add_drive(test_img
)
477 qemu_img('create', '-f', fmt
, blockdev_target_img
,
478 str(TestDriveCompression
.image_len
), *args
)
479 self
.vm
.add_drive(blockdev_target_img
, format
=fmt
)
483 def do_test_compress_complete(self
, cmd
, format
, **args
):
484 self
.do_prepare_drives(format
['type'], format
['args'])
486 self
.assert_no_active_block_jobs()
488 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
489 self
.assert_qmp(result
, 'return', {})
491 self
.wait_until_completed()
494 self
.assertTrue(iotests
.compare_images(test_img
, blockdev_target_img
,
495 iotests
.imgfmt
, format
['type']),
496 'target image does not match source after backup')
498 def test_complete_compress_drive_backup(self
):
499 for format
in TestDriveCompression
.fmt_supports_compression
:
500 self
.do_test_compress_complete('drive-backup', format
,
501 target
=blockdev_target_img
, mode
='existing')
503 def test_complete_compress_blockdev_backup(self
):
504 for format
in TestDriveCompression
.fmt_supports_compression
:
505 self
.do_test_compress_complete('blockdev-backup', format
, target
='drive1')
507 def do_test_compress_cancel(self
, cmd
, format
, **args
):
508 self
.do_prepare_drives(format
['type'], format
['args'])
510 self
.assert_no_active_block_jobs()
512 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
513 self
.assert_qmp(result
, 'return', {})
515 event
= self
.cancel_and_wait()
516 self
.assert_qmp(event
, 'data/type', 'backup')
520 def test_compress_cancel_drive_backup(self
):
521 for format
in TestDriveCompression
.fmt_supports_compression
:
522 self
.do_test_compress_cancel('drive-backup', format
,
523 target
=blockdev_target_img
, mode
='existing')
525 def test_compress_cancel_blockdev_backup(self
):
526 for format
in TestDriveCompression
.fmt_supports_compression
:
527 self
.do_test_compress_cancel('blockdev-backup', format
, target
='drive1')
529 def do_test_compress_pause(self
, cmd
, format
, **args
):
530 self
.do_prepare_drives(format
['type'], format
['args'])
532 self
.assert_no_active_block_jobs()
534 self
.vm
.pause_drive('drive0')
535 result
= self
.vm
.qmp(cmd
, device
='drive0', sync
='full', compress
=True, **args
)
536 self
.assert_qmp(result
, 'return', {})
538 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
539 self
.assert_qmp(result
, 'return', {})
541 self
.vm
.resume_drive('drive0')
543 result
= self
.vm
.qmp('query-block-jobs')
544 offset
= self
.dictpath(result
, 'return[0]/offset')
547 result
= self
.vm
.qmp('query-block-jobs')
548 self
.assert_qmp(result
, 'return[0]/offset', offset
)
550 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
551 self
.assert_qmp(result
, 'return', {})
553 self
.wait_until_completed()
556 self
.assertTrue(iotests
.compare_images(test_img
, blockdev_target_img
,
557 iotests
.imgfmt
, format
['type']),
558 'target image does not match source after backup')
560 def test_compress_pause_drive_backup(self
):
561 for format
in TestDriveCompression
.fmt_supports_compression
:
562 self
.do_test_compress_pause('drive-backup', format
,
563 target
=blockdev_target_img
, mode
='existing')
565 def test_compress_pause_blockdev_backup(self
):
566 for format
in TestDriveCompression
.fmt_supports_compression
:
567 self
.do_test_compress_pause('blockdev-backup', format
, target
='drive1')
569 if __name__
== '__main__':
570 iotests
.main(supported_fmts
=['raw', 'qcow2'])