multifd: Add zstd compression multifd support
[qemu/kevin.git] / tests / qemu-iotests / 055
blob82b9f5f47da7b37c7b0326f1e54f73e7f0406146
1 #!/usr/bin/env python3
3 # Tests for drive-backup and blockdev-backup
5 # Copyright (C) 2013, 2014 Red Hat, Inc.
7 # Based on 041.
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/>.
23 import time
24 import os
25 import iotests
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
34 def setUpModule():
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)
43 def tearDownModule():
44     os.remove(test_img)
47 class TestSingleDrive(iotests.QMPTestCase):
48     def setUp(self):
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')
55         self.vm.launch()
57     def tearDown(self):
58         self.vm.shutdown()
59         os.remove(blockdev_target_img)
60         try:
61             os.remove(target_img)
62         except OSError:
63             pass
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')
96         time.sleep(0.5)
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()
105         self.vm.shutdown()
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':
117             return
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':
125             return
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):
165     def setUp(self):
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")
170         self.vm.launch()
172     def tearDown(self):
173         self.vm.shutdown()
174         os.remove(blockdev_target_img)
175         try:
176             os.remove(target_img)
177         except OSError:
178             pass
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', {})
187         # Default speed is 0
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):
251     def setUp(self):
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')
258         self.vm.launch()
260     def tearDown(self):
261         self.vm.shutdown()
262         os.remove(blockdev_target_img)
263         try:
264             os.remove(target_img)
265         except OSError:
266             pass
268     def do_test_cancel(self, cmd, target):
269         self.assert_no_active_block_jobs()
271         result = self.vm.qmp('transaction', actions=[{
272                 'type': cmd,
273                 'data': { 'device': 'drive0',
274                           'target': target,
275                           'sync': 'full',
276                           'speed': 64 * 1024 },
277             }
278         ])
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=[{
295                 'type': cmd,
296                 'data': { 'device': 'drive0',
297                           'target': target,
298                           'sync': 'full',
299                           'speed': 64 * 1024 },
300             }
301         ])
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')
314         time.sleep(0.5)
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()
323         self.vm.shutdown()
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':
335             return
337         result = self.vm.qmp('transaction', actions=[{
338                 'type': cmd,
339                 'data': { 'device': 'drive2', # CD-ROM
340                           'target': target,
341                           'sync': 'full' },
342             }
343         ])
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',
356                           'mode': 'existing',
357                           'target': target_img,
358                           'sync': 'full' },
359             }
360         ])
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',
367                           'mode': 'existing',
368                           'target': target_img,
369                           'sync': 'full' },
370             }
371         ])
372         self.assert_qmp(result, 'error/class', 'GenericError')
374         result = self.vm.qmp('transaction', actions=[{
375                 'type': 'blockdev-backup',
376                 'data': { 'device': 'nonexistent',
377                           'target': 'drive1',
378                           'sync': 'full' },
379             }
380         ])
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',
387                           'sync': 'full' },
388             }
389         ])
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',
396                           'sync': 'full' },
397             }
398         ])
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',
405                           'target': 'drive0',
406                           'sync': 'full' },
407             }
408         ])
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',
415                           'mode': 'existing',
416                           'target': target_img,
417                           'sync': 'full' },
418             }, {
419                 'type': 'Abort',
420                 'data': {},
421             }
422         ])
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',
429                           'target': 'drive1',
430                           'sync': 'full' },
431             }, {
432                 'type': 'Abort',
433                 'data': {},
434             }
435         ])
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',
443                           'sync': 'full' },
444             }, {
445                 'type': 'Abort',
446                 'data': {},
447             }
448         ])
449         self.assert_qmp(result, 'error/class', 'GenericError')
450         self.assert_no_active_block_jobs()
453 class TestDriveCompression(iotests.QMPTestCase):
454     image_len = 64 * 1024 * 1024 # MB
455     fmt_supports_compression = [{'type': 'qcow2', 'args': ()},
456                                 {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
458     def tearDown(self):
459         self.vm.shutdown()
460         os.remove(blockdev_target_img)
461         try:
462             os.remove(target_img)
463         except OSError:
464             pass
466     def do_prepare_drives(self, fmt, args, attach_target):
467         self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
469         qemu_img('create', '-f', fmt, blockdev_target_img,
470                  str(TestDriveCompression.image_len), *args)
471         if attach_target:
472             self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
474         self.vm.launch()
476     def do_test_compress_complete(self, cmd, format, attach_target, **args):
477         self.do_prepare_drives(format['type'], format['args'], attach_target)
479         self.assert_no_active_block_jobs()
481         result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
482         self.assert_qmp(result, 'return', {})
484         self.wait_until_completed()
486         self.vm.shutdown()
487         self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
488                                                iotests.imgfmt, format['type']),
489                         'target image does not match source after backup')
491     def test_complete_compress_drive_backup(self):
492         for format in TestDriveCompression.fmt_supports_compression:
493             self.do_test_compress_complete('drive-backup', format, False,
494                                            target=blockdev_target_img, mode='existing')
496     def test_complete_compress_blockdev_backup(self):
497         for format in TestDriveCompression.fmt_supports_compression:
498             self.do_test_compress_complete('blockdev-backup', format, True,
499                                            target='drive1')
501     def do_test_compress_cancel(self, cmd, format, attach_target, **args):
502         self.do_prepare_drives(format['type'], format['args'], attach_target)
504         self.assert_no_active_block_jobs()
506         self.vm.pause_drive('drive0')
507         result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
508         self.assert_qmp(result, 'return', {})
510         event = self.cancel_and_wait(resume=True)
511         self.assert_qmp(event, 'data/type', 'backup')
513         self.vm.shutdown()
515     def test_compress_cancel_drive_backup(self):
516         for format in TestDriveCompression.fmt_supports_compression:
517             self.do_test_compress_cancel('drive-backup', format, False,
518                                          target=blockdev_target_img, mode='existing')
520     def test_compress_cancel_blockdev_backup(self):
521        for format in TestDriveCompression.fmt_supports_compression:
522             self.do_test_compress_cancel('blockdev-backup', format, True,
523                                          target='drive1')
525     def do_test_compress_pause(self, cmd, format, attach_target, **args):
526         self.do_prepare_drives(format['type'], format['args'], 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')
541         time.sleep(0.5)
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()
550         self.vm.shutdown()
551         self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
552                                                iotests.imgfmt, format['type']),
553                         'target image does not match source after backup')
555     def test_compress_pause_drive_backup(self):
556         for format in TestDriveCompression.fmt_supports_compression:
557             self.do_test_compress_pause('drive-backup', format, False,
558                                         target=blockdev_target_img, mode='existing')
560     def test_compress_pause_blockdev_backup(self):
561         for format in TestDriveCompression.fmt_supports_compression:
562             self.do_test_compress_pause('blockdev-backup', format, True,
563                                         target='drive1')
565 if __name__ == '__main__':
566     iotests.main(supported_fmts=['raw', 'qcow2'],
567                  supported_protocols=['file'])