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('blkdebug::' + 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 self.vm.pause_drive('drive0')
69 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
70 self.assert_qmp(result, 'return', {})
72 event = self.cancel_and_wait(resume=True)
73 self.assert_qmp(event, 'data/type', 'backup')
75 def test_cancel_drive_backup(self):
76 self.do_test_cancel('drive-backup', target_img)
78 def test_cancel_blockdev_backup(self):
79 self.do_test_cancel('blockdev-backup', 'drive1')
81 def do_test_pause(self, cmd, target, image):
82 self.assert_no_active_block_jobs()
84 self.vm.pause_drive('drive0')
85 result = self.vm.qmp(cmd, device='drive0',
86 target=target, sync='full')
87 self.assert_qmp(result, 'return', {})
89 self.pause_job('drive0', wait=False)
90 self.vm.resume_drive('drive0')
91 self.pause_wait('drive0')
93 result = self.vm.qmp('query-block-jobs')
94 offset = self.dictpath(result, 'return[0]/offset')
97 result = self.vm.qmp('query-block-jobs')
98 self.assert_qmp(result, 'return[0]/offset', offset)
100 result = self.vm.qmp('block-job-resume', device='drive0')
101 self.assert_qmp(result, 'return', {})
103 self.wait_until_completed()
106 self.assertTrue(iotests.compare_images(test_img, image),
107 'target image does not match source after backup')
109 def test_pause_drive_backup(self):
110 self.do_test_pause('drive-backup', target_img, target_img)
112 def test_pause_blockdev_backup(self):
113 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
115 def test_medium_not_found(self):
116 if iotests.qemu_default_machine != 'pc':
119 result = self.vm.qmp('drive-backup', device='drive2', # CD-ROM
120 target=target_img, sync='full')
121 self.assert_qmp(result, 'error/class', 'GenericError')
123 def test_medium_not_found_blockdev_backup(self):
124 if iotests.qemu_default_machine != 'pc':
127 result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM
128 target='drive1', sync='full')
129 self.assert_qmp(result, 'error/class', 'GenericError')
131 def test_image_not_found(self):
132 result = self.vm.qmp('drive-backup', device='drive0',
133 target=target_img, sync='full', mode='existing')
134 self.assert_qmp(result, 'error/class', 'GenericError')
136 def test_invalid_format(self):
137 result = self.vm.qmp('drive-backup', device='drive0',
138 target=target_img, sync='full',
139 format='spaghetti-noodles')
140 self.assert_qmp(result, 'error/class', 'GenericError')
142 def do_test_device_not_found(self, cmd, **args):
143 result = self.vm.qmp(cmd, **args)
144 self.assert_qmp(result, 'error/class', 'GenericError')
146 def test_device_not_found(self):
147 self.do_test_device_not_found('drive-backup', device='nonexistent',
148 target=target_img, sync='full')
150 self.do_test_device_not_found('blockdev-backup', device='nonexistent',
151 target='drive0', sync='full')
153 self.do_test_device_not_found('blockdev-backup', device='drive0',
154 target='nonexistent', sync='full')
156 self.do_test_device_not_found('blockdev-backup', device='nonexistent',
157 target='nonexistent', sync='full')
159 def test_target_is_source(self):
160 result = self.vm.qmp('blockdev-backup', device='drive0',
161 target='drive0', sync='full')
162 self.assert_qmp(result, 'error/class', 'GenericError')
164 class TestSetSpeed(iotests.QMPTestCase):
166 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
168 self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
169 self.vm.add_drive(blockdev_target_img, interface="none")
174 os.remove(blockdev_target_img)
176 os.remove(target_img)
180 def do_test_set_speed(self, cmd, target):
181 self.assert_no_active_block_jobs()
183 self.vm.pause_drive('drive0')
184 result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
185 self.assert_qmp(result, 'return', {})
188 result = self.vm.qmp('query-block-jobs')
189 self.assert_qmp(result, 'return[0]/device', 'drive0')
190 self.assert_qmp(result, 'return[0]/speed', 0)
192 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
193 self.assert_qmp(result, 'return', {})
195 # Ensure the speed we set was accepted
196 result = self.vm.qmp('query-block-jobs')
197 self.assert_qmp(result, 'return[0]/device', 'drive0')
198 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
200 event = self.cancel_and_wait(resume=True)
201 self.assert_qmp(event, 'data/type', 'backup')
203 # Check setting speed option works
204 self.vm.pause_drive('drive0')
205 result = self.vm.qmp(cmd, device='drive0',
206 target=target, sync='full', speed=4*1024*1024)
207 self.assert_qmp(result, 'return', {})
209 result = self.vm.qmp('query-block-jobs')
210 self.assert_qmp(result, 'return[0]/device', 'drive0')
211 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
213 event = self.cancel_and_wait(resume=True)
214 self.assert_qmp(event, 'data/type', 'backup')
216 def test_set_speed_drive_backup(self):
217 self.do_test_set_speed('drive-backup', target_img)
219 def test_set_speed_blockdev_backup(self):
220 self.do_test_set_speed('blockdev-backup', 'drive1')
222 def do_test_set_speed_invalid(self, cmd, target):
223 self.assert_no_active_block_jobs()
225 result = self.vm.qmp(cmd, device='drive0',
226 target=target, sync='full', speed=-1)
227 self.assert_qmp(result, 'error/class', 'GenericError')
229 self.assert_no_active_block_jobs()
231 self.vm.pause_drive('drive0')
232 result = self.vm.qmp(cmd, device='drive0',
233 target=target, sync='full')
234 self.assert_qmp(result, 'return', {})
236 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
237 self.assert_qmp(result, 'error/class', 'GenericError')
239 event = self.cancel_and_wait(resume=True)
240 self.assert_qmp(event, 'data/type', 'backup')
242 def test_set_speed_invalid_drive_backup(self):
243 self.do_test_set_speed_invalid('drive-backup', target_img)
245 def test_set_speed_invalid_blockdev_backup(self):
246 self.do_test_set_speed_invalid('blockdev-backup', 'drive1')
248 # Note: We cannot use pause_drive() here, or the transaction command
249 # would stall. Instead, we limit the block job speed here.
250 class TestSingleTransaction(iotests.QMPTestCase):
252 qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
254 self.vm = iotests.VM().add_drive(test_img)
255 self.vm.add_drive(blockdev_target_img, interface="none")
256 if iotests.qemu_default_machine == 'pc':
257 self.vm.add_drive(None, 'media=cdrom', 'ide')
262 os.remove(blockdev_target_img)
264 os.remove(target_img)
268 def do_test_cancel(self, cmd, target):
269 self.assert_no_active_block_jobs()
271 result = self.vm.qmp('transaction', actions=[{
273 'data': { 'device': 'drive0',
276 'speed': 64 * 1024 },
280 self.assert_qmp(result, 'return', {})
282 event = self.cancel_and_wait()
283 self.assert_qmp(event, 'data/type', 'backup')
285 def test_cancel_drive_backup(self):
286 self.do_test_cancel('drive-backup', target_img)
288 def test_cancel_blockdev_backup(self):
289 self.do_test_cancel('blockdev-backup', 'drive1')
291 def do_test_pause(self, cmd, target, image):
292 self.assert_no_active_block_jobs()
294 result = self.vm.qmp('transaction', actions=[{
296 'data': { 'device': 'drive0',
299 'speed': 64 * 1024 },
302 self.assert_qmp(result, 'return', {})
304 self.pause_job('drive0', wait=False)
306 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
307 self.assert_qmp(result, 'return', {})
309 self.pause_wait('drive0')
311 result = self.vm.qmp('query-block-jobs')
312 offset = self.dictpath(result, 'return[0]/offset')
315 result = self.vm.qmp('query-block-jobs')
316 self.assert_qmp(result, 'return[0]/offset', offset)
318 result = self.vm.qmp('block-job-resume', device='drive0')
319 self.assert_qmp(result, 'return', {})
321 self.wait_until_completed()
324 self.assertTrue(iotests.compare_images(test_img, image),
325 'target image does not match source after backup')
327 def test_pause_drive_backup(self):
328 self.do_test_pause('drive-backup', target_img, target_img)
330 def test_pause_blockdev_backup(self):
331 self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
333 def do_test_medium_not_found(self, cmd, target):
334 if iotests.qemu_default_machine != 'pc':
337 result = self.vm.qmp('transaction', actions=[{
339 'data': { 'device': 'drive2', # CD-ROM
344 self.assert_qmp(result, 'error/class', 'GenericError')
346 def test_medium_not_found_drive_backup(self):
347 self.do_test_medium_not_found('drive-backup', target_img)
349 def test_medium_not_found_blockdev_backup(self):
350 self.do_test_medium_not_found('blockdev-backup', 'drive1')
352 def test_image_not_found(self):
353 result = self.vm.qmp('transaction', actions=[{
354 'type': 'drive-backup',
355 'data': { 'device': 'drive0',
357 'target': target_img,
361 self.assert_qmp(result, 'error/class', 'GenericError')
363 def test_device_not_found(self):
364 result = self.vm.qmp('transaction', actions=[{
365 'type': 'drive-backup',
366 'data': { 'device': 'nonexistent',
368 'target': target_img,
372 self.assert_qmp(result, 'error/class', 'GenericError')
374 result = self.vm.qmp('transaction', actions=[{
375 'type': 'blockdev-backup',
376 'data': { 'device': 'nonexistent',
381 self.assert_qmp(result, 'error/class', 'GenericError')
383 result = self.vm.qmp('transaction', actions=[{
384 'type': 'blockdev-backup',
385 'data': { 'device': 'drive0',
386 'target': 'nonexistent',
390 self.assert_qmp(result, 'error/class', 'GenericError')
392 result = self.vm.qmp('transaction', actions=[{
393 'type': 'blockdev-backup',
394 'data': { 'device': 'nonexistent',
395 'target': 'nonexistent',
399 self.assert_qmp(result, 'error/class', 'GenericError')
401 def test_target_is_source(self):
402 result = self.vm.qmp('transaction', actions=[{
403 'type': 'blockdev-backup',
404 'data': { 'device': 'drive0',
409 self.assert_qmp(result, 'error/class', 'GenericError')
411 def test_abort(self):
412 result = self.vm.qmp('transaction', actions=[{
413 'type': 'drive-backup',
414 'data': { 'device': 'nonexistent',
416 'target': target_img,
423 self.assert_qmp(result, 'error/class', 'GenericError')
424 self.assert_no_active_block_jobs()
426 result = self.vm.qmp('transaction', actions=[{
427 'type': 'blockdev-backup',
428 'data': { 'device': 'nonexistent',
436 self.assert_qmp(result, 'error/class', 'GenericError')
437 self.assert_no_active_block_jobs()
439 result = self.vm.qmp('transaction', actions=[{
440 'type': 'blockdev-backup',
441 'data': { 'device': 'drive0',
442 'target': 'nonexistent',
449 self.assert_qmp(result, 'error/class', 'GenericError')
450 self.assert_no_active_block_jobs()
453 class TestCompressedToQcow2(iotests.QMPTestCase):
454 image_len = 64 * 1024 * 1024 # MB
455 target_fmt = {'type': 'qcow2', 'args': ()}
459 os.remove(blockdev_target_img)
461 os.remove(target_img)
465 def do_prepare_drives(self, attach_target):
466 self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
468 qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img,
469 str(self.image_len), *self.target_fmt['args'])
471 self.vm.add_drive(blockdev_target_img,
472 img_format=self.target_fmt['type'],
477 def do_test_compress_complete(self, cmd, attach_target, **args):
478 self.do_prepare_drives(attach_target)
480 self.assert_no_active_block_jobs()
482 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
483 self.assert_qmp(result, 'return', {})
485 self.wait_until_completed()
488 self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
490 self.target_fmt['type']),
491 'target image does not match source after backup')
493 def test_complete_compress_drive_backup(self):
494 self.do_test_compress_complete('drive-backup', False,
495 target=blockdev_target_img,
498 def test_complete_compress_blockdev_backup(self):
499 self.do_test_compress_complete('blockdev-backup',
500 True, target='drive1')
502 def do_test_compress_cancel(self, cmd, attach_target, **args):
503 self.do_prepare_drives(attach_target)
505 self.assert_no_active_block_jobs()
507 self.vm.pause_drive('drive0')
508 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
509 self.assert_qmp(result, 'return', {})
511 event = self.cancel_and_wait(resume=True)
512 self.assert_qmp(event, 'data/type', 'backup')
516 def test_compress_cancel_drive_backup(self):
517 self.do_test_compress_cancel('drive-backup', False,
518 target=blockdev_target_img,
521 def test_compress_cancel_blockdev_backup(self):
522 self.do_test_compress_cancel('blockdev-backup', True,
525 def do_test_compress_pause(self, cmd, attach_target, **args):
526 self.do_prepare_drives(attach_target)
528 self.assert_no_active_block_jobs()
530 self.vm.pause_drive('drive0')
531 result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
532 self.assert_qmp(result, 'return', {})
534 self.pause_job('drive0', wait=False)
535 self.vm.resume_drive('drive0')
536 self.pause_wait('drive0')
538 result = self.vm.qmp('query-block-jobs')
539 offset = self.dictpath(result, 'return[0]/offset')
542 result = self.vm.qmp('query-block-jobs')
543 self.assert_qmp(result, 'return[0]/offset', offset)
545 result = self.vm.qmp('block-job-resume', device='drive0')
546 self.assert_qmp(result, 'return', {})
548 self.wait_until_completed()
551 self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
553 self.target_fmt['type']),
554 'target image does not match source after backup')
556 def test_compress_pause_drive_backup(self):
557 self.do_test_compress_pause('drive-backup', False,
558 target=blockdev_target_img,
561 def test_compress_pause_blockdev_backup(self):
562 self.do_test_compress_pause('blockdev-backup', True,
566 class TestCompressedToVmdk(TestCompressedToQcow2):
567 target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}
570 if __name__ == '__main__':
571 iotests.main(supported_fmts=['raw', 'qcow2'],
572 supported_protocols=['file'])