xref: /openbmc/qemu/tests/qemu-iotests/055 (revision 87e0331c)
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
32image_len = 64 * 1024 * 1024 # MB
33
34def 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)
42
43def tearDownModule():
44    os.remove(test_img)
45
46
47class TestSingleDrive(iotests.QMPTestCase):
48    def setUp(self):
49        qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
50
51        self.vm = iotests.VM().add_drive(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()
56
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
64
65    def do_test_cancel(self, cmd, target):
66        self.assert_no_active_block_jobs()
67
68        result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
69        self.assert_qmp(result, 'return', {})
70
71        event = self.cancel_and_wait()
72        self.assert_qmp(event, 'data/type', 'backup')
73
74    def test_cancel_drive_backup(self):
75        self.do_test_cancel('drive-backup', target_img)
76
77    def test_cancel_blockdev_backup(self):
78        self.do_test_cancel('blockdev-backup', 'drive1')
79
80    def do_test_pause(self, cmd, target, image):
81        self.assert_no_active_block_jobs()
82
83        self.vm.pause_drive('drive0')
84        result = self.vm.qmp(cmd, device='drive0',
85                             target=target, sync='full')
86        self.assert_qmp(result, 'return', {})
87
88        result = self.vm.qmp('block-job-pause', device='drive0')
89        self.assert_qmp(result, 'return', {})
90
91        self.vm.resume_drive('drive0')
92        self.pause_job('drive0')
93
94        result = self.vm.qmp('query-block-jobs')
95        offset = self.dictpath(result, 'return[0]/offset')
96
97        time.sleep(0.5)
98        result = self.vm.qmp('query-block-jobs')
99        self.assert_qmp(result, 'return[0]/offset', offset)
100
101        result = self.vm.qmp('block-job-resume', device='drive0')
102        self.assert_qmp(result, 'return', {})
103
104        self.wait_until_completed()
105
106        self.vm.shutdown()
107        self.assertTrue(iotests.compare_images(test_img, image),
108                        'target image does not match source after backup')
109
110    def test_pause_drive_backup(self):
111        self.do_test_pause('drive-backup', target_img, target_img)
112
113    def test_pause_blockdev_backup(self):
114        self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
115
116    def test_medium_not_found(self):
117        if iotests.qemu_default_machine != 'pc':
118            return
119
120        result = self.vm.qmp('drive-backup', device='drive2', # CD-ROM
121                             target=target_img, sync='full')
122        self.assert_qmp(result, 'error/class', 'GenericError')
123
124    def test_medium_not_found_blockdev_backup(self):
125        if iotests.qemu_default_machine != 'pc':
126            return
127
128        result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM
129                             target='drive1', sync='full')
130        self.assert_qmp(result, 'error/class', 'GenericError')
131
132    def test_image_not_found(self):
133        result = self.vm.qmp('drive-backup', device='drive0',
134                             target=target_img, sync='full', mode='existing')
135        self.assert_qmp(result, 'error/class', 'GenericError')
136
137    def test_invalid_format(self):
138        result = self.vm.qmp('drive-backup', device='drive0',
139                             target=target_img, sync='full',
140                             format='spaghetti-noodles')
141        self.assert_qmp(result, 'error/class', 'GenericError')
142
143    def do_test_device_not_found(self, cmd, **args):
144        result = self.vm.qmp(cmd, **args)
145        self.assert_qmp(result, 'error/class', 'GenericError')
146
147    def test_device_not_found(self):
148        self.do_test_device_not_found('drive-backup', device='nonexistent',
149                                      target=target_img, sync='full')
150
151        self.do_test_device_not_found('blockdev-backup', device='nonexistent',
152                                      target='drive0', sync='full')
153
154        self.do_test_device_not_found('blockdev-backup', device='drive0',
155                                      target='nonexistent', sync='full')
156
157        self.do_test_device_not_found('blockdev-backup', device='nonexistent',
158                                      target='nonexistent', sync='full')
159
160    def test_target_is_source(self):
161        result = self.vm.qmp('blockdev-backup', device='drive0',
162                             target='drive0', sync='full')
163        self.assert_qmp(result, 'error/class', 'GenericError')
164
165class TestSetSpeed(iotests.QMPTestCase):
166    def setUp(self):
167        qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
168
169        self.vm = iotests.VM().add_drive(test_img)
170        self.vm.add_drive(blockdev_target_img, interface="none")
171        self.vm.launch()
172
173    def tearDown(self):
174        self.vm.shutdown()
175        os.remove(blockdev_target_img)
176        try:
177            os.remove(target_img)
178        except OSError:
179            pass
180
181    def do_test_set_speed(self, cmd, target):
182        self.assert_no_active_block_jobs()
183
184        self.vm.pause_drive('drive0')
185        result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
186        self.assert_qmp(result, 'return', {})
187
188        # Default speed is 0
189        result = self.vm.qmp('query-block-jobs')
190        self.assert_qmp(result, 'return[0]/device', 'drive0')
191        self.assert_qmp(result, 'return[0]/speed', 0)
192
193        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
194        self.assert_qmp(result, 'return', {})
195
196        # Ensure the speed we set was accepted
197        result = self.vm.qmp('query-block-jobs')
198        self.assert_qmp(result, 'return[0]/device', 'drive0')
199        self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
200
201        event = self.cancel_and_wait(resume=True)
202        self.assert_qmp(event, 'data/type', 'backup')
203
204        # Check setting speed option works
205        self.vm.pause_drive('drive0')
206        result = self.vm.qmp(cmd, device='drive0',
207                             target=target, sync='full', speed=4*1024*1024)
208        self.assert_qmp(result, 'return', {})
209
210        result = self.vm.qmp('query-block-jobs')
211        self.assert_qmp(result, 'return[0]/device', 'drive0')
212        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
213
214        event = self.cancel_and_wait(resume=True)
215        self.assert_qmp(event, 'data/type', 'backup')
216
217    def test_set_speed_drive_backup(self):
218        self.do_test_set_speed('drive-backup', target_img)
219
220    def test_set_speed_blockdev_backup(self):
221        self.do_test_set_speed('blockdev-backup', 'drive1')
222
223    def do_test_set_speed_invalid(self, cmd, target):
224        self.assert_no_active_block_jobs()
225
226        result = self.vm.qmp(cmd, device='drive0',
227                             target=target, sync='full', speed=-1)
228        self.assert_qmp(result, 'error/class', 'GenericError')
229
230        self.assert_no_active_block_jobs()
231
232        self.vm.pause_drive('drive0')
233        result = self.vm.qmp(cmd, device='drive0',
234                             target=target, sync='full')
235        self.assert_qmp(result, 'return', {})
236
237        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
238        self.assert_qmp(result, 'error/class', 'GenericError')
239
240        event = self.cancel_and_wait(resume=True)
241        self.assert_qmp(event, 'data/type', 'backup')
242
243    def test_set_speed_invalid_drive_backup(self):
244        self.do_test_set_speed_invalid('drive-backup', target_img)
245
246    def test_set_speed_invalid_blockdev_backup(self):
247        self.do_test_set_speed_invalid('blockdev-backup',  'drive1')
248
249class TestSingleTransaction(iotests.QMPTestCase):
250    def setUp(self):
251        qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
252
253        self.vm = iotests.VM().add_drive(test_img)
254        self.vm.add_drive(blockdev_target_img, interface="none")
255        if iotests.qemu_default_machine == 'pc':
256            self.vm.add_drive(None, 'media=cdrom', 'ide')
257        self.vm.launch()
258
259    def tearDown(self):
260        self.vm.shutdown()
261        os.remove(blockdev_target_img)
262        try:
263            os.remove(target_img)
264        except OSError:
265            pass
266
267    def do_test_cancel(self, cmd, target):
268        self.assert_no_active_block_jobs()
269
270        result = self.vm.qmp('transaction', actions=[{
271                'type': cmd,
272                'data': { 'device': 'drive0',
273                          'target': target,
274                          'sync': 'full' },
275            }
276        ])
277
278        self.assert_qmp(result, 'return', {})
279
280        event = self.cancel_and_wait()
281        self.assert_qmp(event, 'data/type', 'backup')
282
283    def test_cancel_drive_backup(self):
284        self.do_test_cancel('drive-backup', target_img)
285
286    def test_cancel_blockdev_backup(self):
287        self.do_test_cancel('blockdev-backup', 'drive1')
288
289    def do_test_pause(self, cmd, target, image):
290        self.assert_no_active_block_jobs()
291
292        self.vm.pause_drive('drive0')
293        result = self.vm.qmp('transaction', actions=[{
294                'type': cmd,
295                'data': { 'device': 'drive0',
296                          'target': target,
297                          'sync': 'full' },
298            }
299        ])
300        self.assert_qmp(result, 'return', {})
301
302        result = self.vm.qmp('block-job-pause', device='drive0')
303        self.assert_qmp(result, 'return', {})
304
305        self.vm.resume_drive('drive0')
306        self.pause_job('drive0')
307
308        result = self.vm.qmp('query-block-jobs')
309        offset = self.dictpath(result, 'return[0]/offset')
310
311        time.sleep(0.5)
312        result = self.vm.qmp('query-block-jobs')
313        self.assert_qmp(result, 'return[0]/offset', offset)
314
315        result = self.vm.qmp('block-job-resume', device='drive0')
316        self.assert_qmp(result, 'return', {})
317
318        self.wait_until_completed()
319
320        self.vm.shutdown()
321        self.assertTrue(iotests.compare_images(test_img, image),
322                        'target image does not match source after backup')
323
324    def test_pause_drive_backup(self):
325        self.do_test_pause('drive-backup', target_img, target_img)
326
327    def test_pause_blockdev_backup(self):
328        self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
329
330    def do_test_medium_not_found(self, cmd, target):
331        if iotests.qemu_default_machine != 'pc':
332            return
333
334        result = self.vm.qmp('transaction', actions=[{
335                'type': cmd,
336                'data': { 'device': 'drive2', # CD-ROM
337                          'target': target,
338                          'sync': 'full' },
339            }
340        ])
341        self.assert_qmp(result, 'error/class', 'GenericError')
342
343    def test_medium_not_found_drive_backup(self):
344        self.do_test_medium_not_found('drive-backup', target_img)
345
346    def test_medium_not_found_blockdev_backup(self):
347        self.do_test_medium_not_found('blockdev-backup', 'drive1')
348
349    def test_image_not_found(self):
350        result = self.vm.qmp('transaction', actions=[{
351                'type': 'drive-backup',
352                'data': { 'device': 'drive0',
353                          'mode': 'existing',
354                          'target': target_img,
355                          'sync': 'full' },
356            }
357        ])
358        self.assert_qmp(result, 'error/class', 'GenericError')
359
360    def test_device_not_found(self):
361        result = self.vm.qmp('transaction', actions=[{
362                'type': 'drive-backup',
363                'data': { 'device': 'nonexistent',
364                          'mode': 'existing',
365                          'target': target_img,
366                          'sync': 'full' },
367            }
368        ])
369        self.assert_qmp(result, 'error/class', 'GenericError')
370
371        result = self.vm.qmp('transaction', actions=[{
372                'type': 'blockdev-backup',
373                'data': { 'device': 'nonexistent',
374                          'target': 'drive1',
375                          'sync': 'full' },
376            }
377        ])
378        self.assert_qmp(result, 'error/class', 'GenericError')
379
380        result = self.vm.qmp('transaction', actions=[{
381                'type': 'blockdev-backup',
382                'data': { 'device': 'drive0',
383                          'target': 'nonexistent',
384                          'sync': 'full' },
385            }
386        ])
387        self.assert_qmp(result, 'error/class', 'GenericError')
388
389        result = self.vm.qmp('transaction', actions=[{
390                'type': 'blockdev-backup',
391                'data': { 'device': 'nonexistent',
392                          'target': 'nonexistent',
393                          'sync': 'full' },
394            }
395        ])
396        self.assert_qmp(result, 'error/class', 'GenericError')
397
398    def test_target_is_source(self):
399        result = self.vm.qmp('transaction', actions=[{
400                'type': 'blockdev-backup',
401                'data': { 'device': 'drive0',
402                          'target': 'drive0',
403                          'sync': 'full' },
404            }
405        ])
406        self.assert_qmp(result, 'error/class', 'GenericError')
407
408    def test_abort(self):
409        result = self.vm.qmp('transaction', actions=[{
410                'type': 'drive-backup',
411                'data': { 'device': 'nonexistent',
412                          'mode': 'existing',
413                          'target': target_img,
414                          'sync': 'full' },
415            }, {
416                'type': 'Abort',
417                'data': {},
418            }
419        ])
420        self.assert_qmp(result, 'error/class', 'GenericError')
421        self.assert_no_active_block_jobs()
422
423        result = self.vm.qmp('transaction', actions=[{
424                'type': 'blockdev-backup',
425                'data': { 'device': 'nonexistent',
426                          'target': 'drive1',
427                          'sync': 'full' },
428            }, {
429                'type': 'Abort',
430                'data': {},
431            }
432        ])
433        self.assert_qmp(result, 'error/class', 'GenericError')
434        self.assert_no_active_block_jobs()
435
436        result = self.vm.qmp('transaction', actions=[{
437                'type': 'blockdev-backup',
438                'data': { 'device': 'drive0',
439                          'target': 'nonexistent',
440                          'sync': 'full' },
441            }, {
442                'type': 'Abort',
443                'data': {},
444            }
445        ])
446        self.assert_qmp(result, 'error/class', 'GenericError')
447        self.assert_no_active_block_jobs()
448
449
450class TestDriveCompression(iotests.QMPTestCase):
451    image_len = 64 * 1024 * 1024 # MB
452    fmt_supports_compression = [{'type': 'qcow2', 'args': ()},
453                                {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized')}]
454
455    def tearDown(self):
456        self.vm.shutdown()
457        os.remove(blockdev_target_img)
458        try:
459            os.remove(target_img)
460        except OSError:
461            pass
462
463    def do_prepare_drives(self, fmt, args, attach_target):
464        self.vm = iotests.VM().add_drive(test_img)
465
466        qemu_img('create', '-f', fmt, blockdev_target_img,
467                 str(TestDriveCompression.image_len), *args)
468        if attach_target:
469            self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
470
471        self.vm.launch()
472
473    def do_test_compress_complete(self, cmd, format, attach_target, **args):
474        self.do_prepare_drives(format['type'], format['args'], attach_target)
475
476        self.assert_no_active_block_jobs()
477
478        result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
479        self.assert_qmp(result, 'return', {})
480
481        self.wait_until_completed()
482
483        self.vm.shutdown()
484        self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
485                                               iotests.imgfmt, format['type']),
486                        'target image does not match source after backup')
487
488    def test_complete_compress_drive_backup(self):
489        for format in TestDriveCompression.fmt_supports_compression:
490            self.do_test_compress_complete('drive-backup', format, False,
491                                           target=blockdev_target_img, mode='existing')
492
493    def test_complete_compress_blockdev_backup(self):
494        for format in TestDriveCompression.fmt_supports_compression:
495            self.do_test_compress_complete('blockdev-backup', format, True,
496                                           target='drive1')
497
498    def do_test_compress_cancel(self, cmd, format, attach_target, **args):
499        self.do_prepare_drives(format['type'], format['args'], attach_target)
500
501        self.assert_no_active_block_jobs()
502
503        result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
504        self.assert_qmp(result, 'return', {})
505
506        event = self.cancel_and_wait()
507        self.assert_qmp(event, 'data/type', 'backup')
508
509        self.vm.shutdown()
510
511    def test_compress_cancel_drive_backup(self):
512        for format in TestDriveCompression.fmt_supports_compression:
513            self.do_test_compress_cancel('drive-backup', format, False,
514                                         target=blockdev_target_img, mode='existing')
515
516    def test_compress_cancel_blockdev_backup(self):
517       for format in TestDriveCompression.fmt_supports_compression:
518            self.do_test_compress_cancel('blockdev-backup', format, True,
519                                         target='drive1')
520
521    def do_test_compress_pause(self, cmd, format, attach_target, **args):
522        self.do_prepare_drives(format['type'], format['args'], attach_target)
523
524        self.assert_no_active_block_jobs()
525
526        self.vm.pause_drive('drive0')
527        result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
528        self.assert_qmp(result, 'return', {})
529
530        result = self.vm.qmp('block-job-pause', device='drive0')
531        self.assert_qmp(result, 'return', {})
532
533        self.vm.resume_drive('drive0')
534        self.pause_job('drive0')
535
536        result = self.vm.qmp('query-block-jobs')
537        offset = self.dictpath(result, 'return[0]/offset')
538
539        time.sleep(0.5)
540        result = self.vm.qmp('query-block-jobs')
541        self.assert_qmp(result, 'return[0]/offset', offset)
542
543        result = self.vm.qmp('block-job-resume', device='drive0')
544        self.assert_qmp(result, 'return', {})
545
546        self.wait_until_completed()
547
548        self.vm.shutdown()
549        self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
550                                               iotests.imgfmt, format['type']),
551                        'target image does not match source after backup')
552
553    def test_compress_pause_drive_backup(self):
554        for format in TestDriveCompression.fmt_supports_compression:
555            self.do_test_compress_pause('drive-backup', format, False,
556                                        target=blockdev_target_img, mode='existing')
557
558    def test_compress_pause_blockdev_backup(self):
559        for format in TestDriveCompression.fmt_supports_compression:
560            self.do_test_compress_pause('blockdev-backup', format, True,
561                                        target='drive1')
562
563if __name__ == '__main__':
564    iotests.main(supported_fmts=['raw', 'qcow2'])
565