xref: /openbmc/qemu/tests/qemu-iotests/055 (revision 0c0c1fd9)
1#!/usr/bin/env python
2#
3# Tests for drive-backup and blockdev-backup
4#
5# Copyright (C) 2013, 2014 Red Hat, Inc.
6#
7# Based on 041.
8#
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.
13#
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.
18#
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/>.
21#
22
23import time
24import os
25import iotests
26from iotests import qemu_img, qemu_io
27
28test_img = os.path.join(iotests.test_dir, 'test.img')
29target_img = os.path.join(iotests.test_dir, 'target.img')
30blockdev_target_img = os.path.join(iotests.test_dir, 'blockdev-target.img')
31
32class TestSingleDrive(iotests.QMPTestCase):
33    image_len = 64 * 1024 * 1024 # MB
34
35    def setUp(self):
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))
43
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')
47        self.vm.launch()
48
49    def tearDown(self):
50        self.vm.shutdown()
51        os.remove(test_img)
52        os.remove(blockdev_target_img)
53        try:
54            os.remove(target_img)
55        except OSError:
56            pass
57
58    def do_test_cancel(self, cmd, target):
59        self.assert_no_active_block_jobs()
60
61        result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
62        self.assert_qmp(result, 'return', {})
63
64        event = self.cancel_and_wait()
65        self.assert_qmp(event, 'data/type', 'backup')
66
67    def test_cancel_drive_backup(self):
68        self.do_test_cancel('drive-backup', target_img)
69
70    def test_cancel_blockdev_backup(self):
71        self.do_test_cancel('blockdev-backup', 'drive1')
72
73    def do_test_pause(self, cmd, target, image):
74        self.assert_no_active_block_jobs()
75
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', {})
80
81        result = self.vm.qmp('block-job-pause', device='drive0')
82        self.assert_qmp(result, 'return', {})
83
84        self.vm.resume_drive('drive0')
85        time.sleep(1)
86        result = self.vm.qmp('query-block-jobs')
87        offset = self.dictpath(result, 'return[0]/offset')
88
89        time.sleep(1)
90        result = self.vm.qmp('query-block-jobs')
91        self.assert_qmp(result, 'return[0]/offset', offset)
92
93        result = self.vm.qmp('block-job-resume', device='drive0')
94        self.assert_qmp(result, 'return', {})
95
96        self.wait_until_completed()
97
98        self.vm.shutdown()
99        self.assertTrue(iotests.compare_images(test_img, image),
100                        'target image does not match source after backup')
101
102    def test_pause_drive_backup(self):
103        self.do_test_pause('drive-backup', target_img, target_img)
104
105    def test_pause_blockdev_backup(self):
106        self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
107
108    def test_medium_not_found(self):
109        if iotests.qemu_default_machine != 'pc':
110            return
111
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')
115
116    def test_medium_not_found_blockdev_backup(self):
117        if iotests.qemu_default_machine != 'pc':
118            return
119
120        result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM
121                             target='drive1', sync='full')
122        self.assert_qmp(result, 'error/class', 'GenericError')
123
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')
128
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')
134
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')
138
139    def test_device_not_found(self):
140        self.do_test_device_not_found('drive-backup', device='nonexistent',
141                                      target=target_img, sync='full')
142
143        self.do_test_device_not_found('blockdev-backup', device='nonexistent',
144                                      target='drive0', sync='full')
145
146        self.do_test_device_not_found('blockdev-backup', device='drive0',
147                                      target='nonexistent', sync='full')
148
149        self.do_test_device_not_found('blockdev-backup', device='nonexistent',
150                                      target='nonexistent', sync='full')
151
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')
156
157class TestSetSpeed(iotests.QMPTestCase):
158    image_len = 80 * 1024 * 1024 # MB
159
160    def setUp(self):
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))
164
165        self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
166        self.vm.launch()
167
168    def tearDown(self):
169        self.vm.shutdown()
170        os.remove(test_img)
171        os.remove(blockdev_target_img)
172        try:
173            os.remove(target_img)
174        except OSError:
175            pass
176
177    def do_test_set_speed(self, cmd, target):
178        self.assert_no_active_block_jobs()
179
180        self.vm.pause_drive('drive0')
181        result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
182        self.assert_qmp(result, 'return', {})
183
184        # Default speed is 0
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)
188
189        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
190        self.assert_qmp(result, 'return', {})
191
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)
196
197        event = self.cancel_and_wait(resume=True)
198        self.assert_qmp(event, 'data/type', 'backup')
199
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', {})
205
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)
209
210        event = self.cancel_and_wait(resume=True)
211        self.assert_qmp(event, 'data/type', 'backup')
212
213    def test_set_speed_drive_backup(self):
214        self.do_test_set_speed('drive-backup', target_img)
215
216    def test_set_speed_blockdev_backup(self):
217        self.do_test_set_speed('blockdev-backup', 'drive1')
218
219    def do_test_set_speed_invalid(self, cmd, target):
220        self.assert_no_active_block_jobs()
221
222        result = self.vm.qmp(cmd, device='drive0',
223                             target=target, sync='full', speed=-1)
224        self.assert_qmp(result, 'error/class', 'GenericError')
225
226        self.assert_no_active_block_jobs()
227
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', {})
232
233        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
234        self.assert_qmp(result, 'error/class', 'GenericError')
235
236        event = self.cancel_and_wait(resume=True)
237        self.assert_qmp(event, 'data/type', 'backup')
238
239    def test_set_speed_invalid_drive_backup(self):
240        self.do_test_set_speed_invalid('drive-backup', target_img)
241
242    def test_set_speed_invalid_blockdev_backup(self):
243        self.do_test_set_speed_invalid('blockdev-backup',  'drive1')
244
245class TestSingleTransaction(iotests.QMPTestCase):
246    image_len = 64 * 1024 * 1024 # MB
247
248    def setUp(self):
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))
255
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')
259        self.vm.launch()
260
261    def tearDown(self):
262        self.vm.shutdown()
263        os.remove(test_img)
264        os.remove(blockdev_target_img)
265        try:
266            os.remove(target_img)
267        except OSError:
268            pass
269
270    def do_test_cancel(self, cmd, target):
271        self.assert_no_active_block_jobs()
272
273        result = self.vm.qmp('transaction', actions=[{
274                'type': cmd,
275                'data': { 'device': 'drive0',
276                          'target': target,
277                          'sync': 'full' },
278            }
279        ])
280
281        self.assert_qmp(result, 'return', {})
282
283        event = self.cancel_and_wait()
284        self.assert_qmp(event, 'data/type', 'backup')
285
286    def test_cancel_drive_backup(self):
287        self.do_test_cancel('drive-backup', target_img)
288
289    def test_cancel_blockdev_backup(self):
290        self.do_test_cancel('blockdev-backup', 'drive1')
291
292    def do_test_pause(self, cmd, target, image):
293        self.assert_no_active_block_jobs()
294
295        self.vm.pause_drive('drive0')
296        result = self.vm.qmp('transaction', actions=[{
297                'type': cmd,
298                'data': { 'device': 'drive0',
299                          'target': target,
300                          'sync': 'full' },
301            }
302        ])
303        self.assert_qmp(result, 'return', {})
304
305        result = self.vm.qmp('block-job-pause', device='drive0')
306        self.assert_qmp(result, 'return', {})
307
308        self.vm.resume_drive('drive0')
309        time.sleep(1)
310        result = self.vm.qmp('query-block-jobs')
311        offset = self.dictpath(result, 'return[0]/offset')
312
313        time.sleep(1)
314        result = self.vm.qmp('query-block-jobs')
315        self.assert_qmp(result, 'return[0]/offset', offset)
316
317        result = self.vm.qmp('block-job-resume', device='drive0')
318        self.assert_qmp(result, 'return', {})
319
320        self.wait_until_completed()
321
322        self.vm.shutdown()
323        self.assertTrue(iotests.compare_images(test_img, image),
324                        'target image does not match source after backup')
325
326    def test_pause_drive_backup(self):
327        self.do_test_pause('drive-backup', target_img, target_img)
328
329    def test_pause_blockdev_backup(self):
330        self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
331
332    def do_test_medium_not_found(self, cmd, target):
333        if iotests.qemu_default_machine != 'pc':
334            return
335
336        result = self.vm.qmp('transaction', actions=[{
337                'type': cmd,
338                'data': { 'device': 'drive2', # CD-ROM
339                          'target': target,
340                          'sync': 'full' },
341            }
342        ])
343        self.assert_qmp(result, 'error/class', 'GenericError')
344
345    def test_medium_not_found_drive_backup(self):
346        self.do_test_medium_not_found('drive-backup', target_img)
347
348    def test_medium_not_found_blockdev_backup(self):
349        self.do_test_medium_not_found('blockdev-backup', 'drive1')
350
351    def test_image_not_found(self):
352        result = self.vm.qmp('transaction', actions=[{
353                'type': 'drive-backup',
354                'data': { 'device': 'drive0',
355                          'mode': 'existing',
356                          'target': target_img,
357                          'sync': 'full' },
358            }
359        ])
360        self.assert_qmp(result, 'error/class', 'GenericError')
361
362    def test_device_not_found(self):
363        result = self.vm.qmp('transaction', actions=[{
364                'type': 'drive-backup',
365                'data': { 'device': 'nonexistent',
366                          'mode': 'existing',
367                          'target': target_img,
368                          'sync': 'full' },
369            }
370        ])
371        self.assert_qmp(result, 'error/class', 'GenericError')
372
373        result = self.vm.qmp('transaction', actions=[{
374                'type': 'blockdev-backup',
375                'data': { 'device': 'nonexistent',
376                          'target': 'drive1',
377                          'sync': 'full' },
378            }
379        ])
380        self.assert_qmp(result, 'error/class', 'GenericError')
381
382        result = self.vm.qmp('transaction', actions=[{
383                'type': 'blockdev-backup',
384                'data': { 'device': 'drive0',
385                          'target': 'nonexistent',
386                          'sync': 'full' },
387            }
388        ])
389        self.assert_qmp(result, 'error/class', 'GenericError')
390
391        result = self.vm.qmp('transaction', actions=[{
392                'type': 'blockdev-backup',
393                'data': { 'device': 'nonexistent',
394                          'target': 'nonexistent',
395                          'sync': 'full' },
396            }
397        ])
398        self.assert_qmp(result, 'error/class', 'GenericError')
399
400    def test_target_is_source(self):
401        result = self.vm.qmp('transaction', actions=[{
402                'type': 'blockdev-backup',
403                'data': { 'device': 'drive0',
404                          'target': 'drive0',
405                          'sync': 'full' },
406            }
407        ])
408        self.assert_qmp(result, 'error/class', 'GenericError')
409
410    def test_abort(self):
411        result = self.vm.qmp('transaction', actions=[{
412                'type': 'drive-backup',
413                'data': { 'device': 'nonexistent',
414                          'mode': 'existing',
415                          'target': target_img,
416                          'sync': 'full' },
417            }, {
418                'type': 'Abort',
419                'data': {},
420            }
421        ])
422        self.assert_qmp(result, 'error/class', 'GenericError')
423        self.assert_no_active_block_jobs()
424
425        result = self.vm.qmp('transaction', actions=[{
426                'type': 'blockdev-backup',
427                'data': { 'device': 'nonexistent',
428                          'target': 'drive1',
429                          'sync': 'full' },
430            }, {
431                'type': 'Abort',
432                'data': {},
433            }
434        ])
435        self.assert_qmp(result, 'error/class', 'GenericError')
436        self.assert_no_active_block_jobs()
437
438        result = self.vm.qmp('transaction', actions=[{
439                'type': 'blockdev-backup',
440                'data': { 'device': 'drive0',
441                          'target': 'nonexistent',
442                          'sync': 'full' },
443            }, {
444                'type': 'Abort',
445                'data': {},
446            }
447        ])
448        self.assert_qmp(result, 'error/class', 'GenericError')
449        self.assert_no_active_block_jobs()
450
451
452class TestDriveCompression(iotests.QMPTestCase):
453    image_len = 64 * 1024 * 1024 # MB
454    fmt_supports_compression = [{'type': 'qcow2', 'args': ()},
455                                {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
456
457    def setUp(self):
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)
464
465    def tearDown(self):
466        self.vm.shutdown()
467        os.remove(test_img)
468        os.remove(blockdev_target_img)
469        try:
470            os.remove(target_img)
471        except OSError:
472            pass
473
474    def do_prepare_drives(self, fmt, args):
475        self.vm = iotests.VM().add_drive(test_img)
476
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)
480
481        self.vm.launch()
482
483    def do_test_compress_complete(self, cmd, format, **args):
484        self.do_prepare_drives(format['type'], format['args'])
485
486        self.assert_no_active_block_jobs()
487
488        result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
489        self.assert_qmp(result, 'return', {})
490
491        self.wait_until_completed()
492
493        self.vm.shutdown()
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')
497
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')
502
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')
506
507    def do_test_compress_cancel(self, cmd, format, **args):
508        self.do_prepare_drives(format['type'], format['args'])
509
510        self.assert_no_active_block_jobs()
511
512        result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
513        self.assert_qmp(result, 'return', {})
514
515        event = self.cancel_and_wait()
516        self.assert_qmp(event, 'data/type', 'backup')
517
518        self.vm.shutdown()
519
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')
524
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')
528
529    def do_test_compress_pause(self, cmd, format, **args):
530        self.do_prepare_drives(format['type'], format['args'])
531
532        self.assert_no_active_block_jobs()
533
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', {})
537
538        result = self.vm.qmp('block-job-pause', device='drive0')
539        self.assert_qmp(result, 'return', {})
540
541        self.vm.resume_drive('drive0')
542        time.sleep(1)
543        result = self.vm.qmp('query-block-jobs')
544        offset = self.dictpath(result, 'return[0]/offset')
545
546        time.sleep(1)
547        result = self.vm.qmp('query-block-jobs')
548        self.assert_qmp(result, 'return[0]/offset', offset)
549
550        result = self.vm.qmp('block-job-resume', device='drive0')
551        self.assert_qmp(result, 'return', {})
552
553        self.wait_until_completed()
554
555        self.vm.shutdown()
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')
559
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')
564
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')
568
569if __name__ == '__main__':
570    iotests.main(supported_fmts=['raw', 'qcow2'])
571