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()
52 self.vm.add_drive('blkdebug::' + test_img, 'node-name=source')
53 self.vm.add_drive(blockdev_target_img, 'node-name=target',
55 if iotests.qemu_default_machine == 'pc':
56 self.vm.add_drive(None, 'media=cdrom', 'ide')
61 os.remove(blockdev_target_img)
67 def do_test_cancel(self, cmd, target):
68 self.assert_no_active_block_jobs()
70 self.vm.pause_drive('drive0')
71 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
72 self.assert_qmp(result, 'return', {})
74 event = self.cancel_and_wait(resume=True)
75 self.assert_qmp(event, 'data/type', 'backup')
77 def test_cancel_drive_backup(self):
78 self.do_test_cancel('drive-backup', target_img)
80 def test_cancel_blockdev_backup(self):
81 self.do_test_cancel('blockdev-backup', 'drive1')
83 def do_test_pause(self, cmd, target, image):
84 self.assert_no_active_block_jobs()
86 self.vm.pause_drive('drive0')
87 result = self.vm.qmp(cmd, device='drive0',
88 target=target, sync='full')
89 self.assert_qmp(result, 'return', {})
91 self.pause_job('drive0', wait=False)
92 self.vm.resume_drive('drive0')
93 self.pause_wait('drive0')
95 result = self.vm.qmp('query-block-jobs')
96 offset = self.dictpath(result, 'return[0]/offset')
99 result = self.vm.qmp('query-block-jobs')
100 self.assert_qmp(result, 'return[0]/offset', offset)
102 result = self.vm.qmp('block-job-resume', device='drive0')
103 self.assert_qmp(result, 'return', {})
105 self.wait_until_completed()
108 self.assertTrue(iotests.compare_images(test_img, image),
109 'target image does not match source after backup')
111 def test_pause_drive_backup(self):
112 self.do_test_pause('drive-backup', target_img, target_img)
114 def test_pause_blockdev_backup(self):
115 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
117 def do_test_resize_blockdev_backup(self, device, node):
119 result = self.vm.qmp('block_resize', device=device, size=65536)
120 self.assert_qmp(result, 'error/class', 'GenericError')
122 result = self.vm.qmp('block_resize', node_name=node, size=65536)
123 self.assert_qmp(result, 'error/class', 'GenericError')
125 result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0',
126 target='drive1', sync='full', auto_finalize=False,
128 self.assert_qmp(result, 'return', {})
130 self.vm.run_job('job0', auto_finalize=False, pre_finalize=pre_finalize)
132 def test_source_resize_blockdev_backup(self):
133 self.do_test_resize_blockdev_backup('drive0', 'source')
135 def test_target_resize_blockdev_backup(self):
136 self.do_test_resize_blockdev_backup('drive1', 'target')
138 def do_test_target_size(self, size):
139 result = self.vm.qmp('block_resize', device='drive1', size=size)
140 self.assert_qmp(result, 'return', {})
142 result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0',
143 target='drive1', sync='full')
144 self.assert_qmp(result, 'error/class', 'GenericError')
146 def test_small_target(self):
147 self.do_test_target_size(image_len // 2)
149 def test_large_target(self):
150 self.do_test_target_size(image_len * 2)
152 def test_medium_not_found(self):
153 if iotests.qemu_default_machine != 'pc':
156 result = self.vm.qmp('drive-backup', device='drive2', # CD-ROM
157 target=target_img, sync='full')
158 self.assert_qmp(result, 'error/class', 'GenericError')
160 def test_medium_not_found_blockdev_backup(self):
161 if iotests.qemu_default_machine != 'pc':
164 result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM
165 target='drive1', sync='full')
166 self.assert_qmp(result, 'error/class', 'GenericError')
168 def test_image_not_found(self):
169 result = self.vm.qmp('drive-backup', device='drive0',
170 target=target_img, sync='full', mode='existing')
171 self.assert_qmp(result, 'error/class', 'GenericError')
173 def test_invalid_format(self):
174 result = self.vm.qmp('drive-backup', device='drive0',
175 target=target_img, sync='full',
176 format='spaghetti-noodles')
177 self.assert_qmp(result, 'error/class', 'GenericError')
179 def do_test_device_not_found(self, cmd, **args):
180 result = self.vm.qmp(cmd, **args)
181 self.assert_qmp(result, 'error/class', 'GenericError')
183 def test_device_not_found(self):
184 self.do_test_device_not_found('drive-backup', device='nonexistent',
185 target=target_img, sync='full')
187 self.do_test_device_not_found('blockdev-backup', device='nonexistent',
188 target='drive0', sync='full')
190 self.do_test_device_not_found('blockdev-backup', device='drive0',
191 target='nonexistent', sync='full')
193 self.do_test_device_not_found('blockdev-backup', device='nonexistent',
194 target='nonexistent', sync='full')
196 def test_target_is_source(self):
197 result = self.vm.qmp('blockdev-backup', device='drive0',
198 target='drive0', sync='full')
199 self.assert_qmp(result, 'error/class', 'GenericError')
201 class TestSetSpeed(iotests.QMPTestCase):
203 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
205 self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
206 self.vm.add_drive(blockdev_target_img, interface="none")
211 os.remove(blockdev_target_img)
213 os.remove(target_img)
217 def do_test_set_speed(self, cmd, target):
218 self.assert_no_active_block_jobs()
220 self.vm.pause_drive('drive0')
221 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
222 self.assert_qmp(result, 'return', {})
225 result = self.vm.qmp('query-block-jobs')
226 self.assert_qmp(result, 'return[0]/device', 'drive0')
227 self.assert_qmp(result, 'return[0]/speed', 0)
229 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
230 self.assert_qmp(result, 'return', {})
232 # Ensure the speed we set was accepted
233 result = self.vm.qmp('query-block-jobs')
234 self.assert_qmp(result, 'return[0]/device', 'drive0')
235 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
237 event = self.cancel_and_wait(resume=True)
238 self.assert_qmp(event, 'data/type', 'backup')
240 # Check setting speed option works
241 self.vm.pause_drive('drive0')
242 result = self.vm.qmp(cmd, device='drive0',
243 target=target, sync='full', speed=4*1024*1024)
244 self.assert_qmp(result, 'return', {})
246 result = self.vm.qmp('query-block-jobs')
247 self.assert_qmp(result, 'return[0]/device', 'drive0')
248 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
250 event = self.cancel_and_wait(resume=True)
251 self.assert_qmp(event, 'data/type', 'backup')
253 def test_set_speed_drive_backup(self):
254 self.do_test_set_speed('drive-backup', target_img)
256 def test_set_speed_blockdev_backup(self):
257 self.do_test_set_speed('blockdev-backup', 'drive1')
259 def do_test_set_speed_invalid(self, cmd, target):
260 self.assert_no_active_block_jobs()
262 result = self.vm.qmp(cmd, device='drive0',
263 target=target, sync='full', speed=-1)
264 self.assert_qmp(result, 'error/class', 'GenericError')
266 self.assert_no_active_block_jobs()
268 self.vm.pause_drive('drive0')
269 result = self.vm.qmp(cmd, device='drive0',
270 target=target, sync='full')
271 self.assert_qmp(result, 'return', {})
273 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
274 self.assert_qmp(result, 'error/class', 'GenericError')
276 event = self.cancel_and_wait(resume=True)
277 self.assert_qmp(event, 'data/type', 'backup')
279 def test_set_speed_invalid_drive_backup(self):
280 self.do_test_set_speed_invalid('drive-backup', target_img)
282 def test_set_speed_invalid_blockdev_backup(self):
283 self.do_test_set_speed_invalid('blockdev-backup', 'drive1')
285 # Note: We cannot use pause_drive() here, or the transaction command
286 # would stall. Instead, we limit the block job speed here.
287 class TestSingleTransaction(iotests.QMPTestCase):
289 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
291 self.vm = iotests.VM().add_drive(test_img)
292 self.vm.add_drive(blockdev_target_img, interface="none")
293 if iotests.qemu_default_machine == 'pc':
294 self.vm.add_drive(None, 'media=cdrom', 'ide')
299 os.remove(blockdev_target_img)
301 os.remove(target_img)
305 def do_test_cancel(self, cmd, target):
306 self.assert_no_active_block_jobs()
308 result = self.vm.qmp('transaction', actions=[{
310 'data': { 'device': 'drive0',
313 'speed': 64 * 1024 },
317 self.assert_qmp(result, 'return', {})
319 event = self.cancel_and_wait()
320 self.assert_qmp(event, 'data/type', 'backup')
322 def test_cancel_drive_backup(self):
323 self.do_test_cancel('drive-backup', target_img)
325 def test_cancel_blockdev_backup(self):
326 self.do_test_cancel('blockdev-backup', 'drive1')
328 def do_test_pause(self, cmd, target, image):
329 self.assert_no_active_block_jobs()
331 result = self.vm.qmp('transaction', actions=[{
333 'data': { 'device': 'drive0',
336 'speed': 64 * 1024 },
339 self.assert_qmp(result, 'return', {})
341 self.pause_job('drive0', wait=False)
343 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
344 self.assert_qmp(result, 'return', {})
346 self.pause_wait('drive0')
348 result = self.vm.qmp('query-block-jobs')
349 offset = self.dictpath(result, 'return[0]/offset')
352 result = self.vm.qmp('query-block-jobs')
353 self.assert_qmp(result, 'return[0]/offset', offset)
355 result = self.vm.qmp('block-job-resume', device='drive0')
356 self.assert_qmp(result, 'return', {})
358 self.wait_until_completed()
361 self.assertTrue(iotests.compare_images(test_img, image),
362 'target image does not match source after backup')
364 def test_pause_drive_backup(self):
365 self.do_test_pause('drive-backup', target_img, target_img)
367 def test_pause_blockdev_backup(self):
368 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
370 def do_test_medium_not_found(self, cmd, target):
371 if iotests.qemu_default_machine != 'pc':
374 result = self.vm.qmp('transaction', actions=[{
376 'data': { 'device': 'drive2', # CD-ROM
381 self.assert_qmp(result, 'error/class', 'GenericError')
383 def test_medium_not_found_drive_backup(self):
384 self.do_test_medium_not_found('drive-backup', target_img)
386 def test_medium_not_found_blockdev_backup(self):
387 self.do_test_medium_not_found('blockdev-backup', 'drive1')
389 def test_image_not_found(self):
390 result = self.vm.qmp('transaction', actions=[{
391 'type': 'drive-backup',
392 'data': { 'device': 'drive0',
394 'target': target_img,
398 self.assert_qmp(result, 'error/class', 'GenericError')
400 def test_device_not_found(self):
401 result = self.vm.qmp('transaction', actions=[{
402 'type': 'drive-backup',
403 'data': { 'device': 'nonexistent',
405 'target': target_img,
409 self.assert_qmp(result, 'error/class', 'GenericError')
411 result = self.vm.qmp('transaction', actions=[{
412 'type': 'blockdev-backup',
413 'data': { 'device': 'nonexistent',
418 self.assert_qmp(result, 'error/class', 'GenericError')
420 result = self.vm.qmp('transaction', actions=[{
421 'type': 'blockdev-backup',
422 'data': { 'device': 'drive0',
423 'target': 'nonexistent',
427 self.assert_qmp(result, 'error/class', 'GenericError')
429 result = self.vm.qmp('transaction', actions=[{
430 'type': 'blockdev-backup',
431 'data': { 'device': 'nonexistent',
432 'target': 'nonexistent',
436 self.assert_qmp(result, 'error/class', 'GenericError')
438 def test_target_is_source(self):
439 result = self.vm.qmp('transaction', actions=[{
440 'type': 'blockdev-backup',
441 'data': { 'device': 'drive0',
446 self.assert_qmp(result, 'error/class', 'GenericError')
448 def test_abort(self):
449 result = self.vm.qmp('transaction', actions=[{
450 'type': 'drive-backup',
451 'data': { 'device': 'nonexistent',
453 'target': target_img,
460 self.assert_qmp(result, 'error/class', 'GenericError')
461 self.assert_no_active_block_jobs()
463 result = self.vm.qmp('transaction', actions=[{
464 'type': 'blockdev-backup',
465 'data': { 'device': 'nonexistent',
473 self.assert_qmp(result, 'error/class', 'GenericError')
474 self.assert_no_active_block_jobs()
476 result = self.vm.qmp('transaction', actions=[{
477 'type': 'blockdev-backup',
478 'data': { 'device': 'drive0',
479 'target': 'nonexistent',
486 self.assert_qmp(result, 'error/class', 'GenericError')
487 self.assert_no_active_block_jobs()
490 class TestCompressedToQcow2(iotests.QMPTestCase):
491 image_len = 64 * 1024 * 1024 # MB
492 target_fmt = {'type': 'qcow2', 'args': (), 'drive-opts': ''}
496 os.remove(blockdev_target_img)
498 os.remove(target_img)
502 def do_prepare_drives(self, attach_target):
503 self.vm = iotests.VM().add_drive('blkdebug::' + test_img,
504 opts=self.target_fmt['drive-opts'])
506 qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img,
507 str(self.image_len), *self.target_fmt['args'])
509 self.vm.add_drive(blockdev_target_img,
510 img_format=self.target_fmt['type'],
512 opts=self.target_fmt['drive-opts'])
516 def do_test_compress_complete(self, cmd, attach_target, **args):
517 self.do_prepare_drives(attach_target)
519 self.assert_no_active_block_jobs()
521 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
522 self.assert_qmp(result, 'return', {})
524 self.wait_until_completed()
527 self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
529 self.target_fmt['type']),
530 'target image does not match source after backup')
532 def test_complete_compress_drive_backup(self):
533 self.do_test_compress_complete('drive-backup', False,
534 target=blockdev_target_img,
537 def test_complete_compress_blockdev_backup(self):
538 self.do_test_compress_complete('blockdev-backup',
539 True, target='drive1')
541 def do_test_compress_cancel(self, cmd, attach_target, **args):
542 self.do_prepare_drives(attach_target)
544 self.assert_no_active_block_jobs()
546 self.vm.pause_drive('drive0')
547 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
548 self.assert_qmp(result, 'return', {})
550 event = self.cancel_and_wait(resume=True)
551 self.assert_qmp(event, 'data/type', 'backup')
555 def test_compress_cancel_drive_backup(self):
556 self.do_test_compress_cancel('drive-backup', False,
557 target=blockdev_target_img,
560 def test_compress_cancel_blockdev_backup(self):
561 self.do_test_compress_cancel('blockdev-backup', True,
564 def do_test_compress_pause(self, cmd, attach_target, **args):
565 self.do_prepare_drives(attach_target)
567 self.assert_no_active_block_jobs()
569 self.vm.pause_drive('drive0')
570 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
571 self.assert_qmp(result, 'return', {})
573 self.pause_job('drive0', wait=False)
574 self.vm.resume_drive('drive0')
575 self.pause_wait('drive0')
577 result = self.vm.qmp('query-block-jobs')
578 offset = self.dictpath(result, 'return[0]/offset')
581 result = self.vm.qmp('query-block-jobs')
582 self.assert_qmp(result, 'return[0]/offset', offset)
584 result = self.vm.qmp('block-job-resume', device='drive0')
585 self.assert_qmp(result, 'return', {})
587 self.wait_until_completed()
590 self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
592 self.target_fmt['type']),
593 'target image does not match source after backup')
595 def test_compress_pause_drive_backup(self):
596 self.do_test_compress_pause('drive-backup', False,
597 target=blockdev_target_img,
600 def test_compress_pause_blockdev_backup(self):
601 self.do_test_compress_pause('blockdev-backup', True,
605 class TestCompressedToVmdk(TestCompressedToQcow2):
606 target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized'),
607 'drive-opts': 'cache.no-flush=on'}
609 @iotests.skip_if_unsupported(['vmdk'])
614 if __name__ == '__main__':
615 iotests.main(supported_fmts=['raw', 'qcow2'],
616 supported_protocols=['file'])