xref: /openbmc/qemu/tests/qemu-iotests/055 (revision 77a8257e)
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        self.vm.launch()
46
47    def tearDown(self):
48        self.vm.shutdown()
49        os.remove(test_img)
50        os.remove(blockdev_target_img)
51        try:
52            os.remove(target_img)
53        except OSError:
54            pass
55
56    def do_test_cancel(self, cmd, target):
57        self.assert_no_active_block_jobs()
58
59        result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
60        self.assert_qmp(result, 'return', {})
61
62        event = self.cancel_and_wait()
63        self.assert_qmp(event, 'data/type', 'backup')
64
65    def test_cancel_drive_backup(self):
66        self.do_test_cancel('drive-backup', target_img)
67
68    def test_cancel_blockdev_backup(self):
69        self.do_test_cancel('blockdev-backup', 'drive1')
70
71    def do_test_pause(self, cmd, target, image):
72        self.assert_no_active_block_jobs()
73
74        self.vm.pause_drive('drive0')
75        result = self.vm.qmp(cmd, device='drive0',
76                             target=target, sync='full')
77        self.assert_qmp(result, 'return', {})
78
79        result = self.vm.qmp('block-job-pause', device='drive0')
80        self.assert_qmp(result, 'return', {})
81
82        self.vm.resume_drive('drive0')
83        time.sleep(1)
84        result = self.vm.qmp('query-block-jobs')
85        offset = self.dictpath(result, 'return[0]/offset')
86
87        time.sleep(1)
88        result = self.vm.qmp('query-block-jobs')
89        self.assert_qmp(result, 'return[0]/offset', offset)
90
91        result = self.vm.qmp('block-job-resume', device='drive0')
92        self.assert_qmp(result, 'return', {})
93
94        self.wait_until_completed()
95
96        self.vm.shutdown()
97        self.assertTrue(iotests.compare_images(test_img, image),
98                        'target image does not match source after backup')
99
100    def test_pause_drive_backup(self):
101        self.do_test_pause('drive-backup', target_img, target_img)
102
103    def test_pause_blockdev_backup(self):
104        self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
105
106    def test_medium_not_found(self):
107        result = self.vm.qmp('drive-backup', device='ide1-cd0',
108                             target=target_img, sync='full')
109        self.assert_qmp(result, 'error/class', 'GenericError')
110
111    def test_medium_not_found_blockdev_backup(self):
112        result = self.vm.qmp('blockdev-backup', device='ide1-cd0',
113                             target='drive1', sync='full')
114        self.assert_qmp(result, 'error/class', 'GenericError')
115
116    def test_image_not_found(self):
117        result = self.vm.qmp('drive-backup', device='drive0',
118                             target=target_img, sync='full', mode='existing')
119        self.assert_qmp(result, 'error/class', 'GenericError')
120
121    def test_invalid_format(self):
122        result = self.vm.qmp('drive-backup', device='drive0',
123                             target=target_img, sync='full',
124                             format='spaghetti-noodles')
125        self.assert_qmp(result, 'error/class', 'GenericError')
126
127    def do_test_device_not_found(self, cmd, **args):
128        result = self.vm.qmp(cmd, **args)
129        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
130
131    def test_device_not_found(self):
132        self.do_test_device_not_found('drive-backup', device='nonexistent',
133                                      target=target_img, sync='full')
134
135        self.do_test_device_not_found('blockdev-backup', device='nonexistent',
136                                      target='drive0', sync='full')
137
138        self.do_test_device_not_found('blockdev-backup', device='drive0',
139                                      target='nonexistent', sync='full')
140
141        self.do_test_device_not_found('blockdev-backup', device='nonexistent',
142                                      target='nonexistent', sync='full')
143
144    def test_target_is_source(self):
145        result = self.vm.qmp('blockdev-backup', device='drive0',
146                             target='drive0', sync='full')
147        self.assert_qmp(result, 'error/class', 'GenericError')
148
149class TestSetSpeed(iotests.QMPTestCase):
150    image_len = 80 * 1024 * 1024 # MB
151
152    def setUp(self):
153        qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSetSpeed.image_len))
154        qemu_io('-f', iotests.imgfmt, '-c', 'write -P1 0 512', test_img)
155        qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len))
156
157        self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
158        self.vm.launch()
159
160    def tearDown(self):
161        self.vm.shutdown()
162        os.remove(test_img)
163        os.remove(blockdev_target_img)
164        try:
165            os.remove(target_img)
166        except OSError:
167            pass
168
169    def do_test_set_speed(self, cmd, target):
170        self.assert_no_active_block_jobs()
171
172        self.vm.pause_drive('drive0')
173        result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
174        self.assert_qmp(result, 'return', {})
175
176        # Default speed is 0
177        result = self.vm.qmp('query-block-jobs')
178        self.assert_qmp(result, 'return[0]/device', 'drive0')
179        self.assert_qmp(result, 'return[0]/speed', 0)
180
181        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
182        self.assert_qmp(result, 'return', {})
183
184        # Ensure the speed we set was accepted
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', 8 * 1024 * 1024)
188
189        event = self.cancel_and_wait(resume=True)
190        self.assert_qmp(event, 'data/type', 'backup')
191
192        # Check setting speed option works
193        self.vm.pause_drive('drive0')
194        result = self.vm.qmp(cmd, device='drive0',
195                             target=target, sync='full', speed=4*1024*1024)
196        self.assert_qmp(result, 'return', {})
197
198        result = self.vm.qmp('query-block-jobs')
199        self.assert_qmp(result, 'return[0]/device', 'drive0')
200        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
201
202        event = self.cancel_and_wait(resume=True)
203        self.assert_qmp(event, 'data/type', 'backup')
204
205    def test_set_speed_drive_backup(self):
206        self.do_test_set_speed('drive-backup', target_img)
207
208    def test_set_speed_blockdev_backup(self):
209        self.do_test_set_speed('blockdev-backup', 'drive1')
210
211    def do_test_set_speed_invalid(self, cmd, target):
212        self.assert_no_active_block_jobs()
213
214        result = self.vm.qmp(cmd, device='drive0',
215                             target=target, sync='full', speed=-1)
216        self.assert_qmp(result, 'error/class', 'GenericError')
217
218        self.assert_no_active_block_jobs()
219
220        self.vm.pause_drive('drive0')
221        result = self.vm.qmp(cmd, device='drive0',
222                             target=target, sync='full')
223        self.assert_qmp(result, 'return', {})
224
225        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
226        self.assert_qmp(result, 'error/class', 'GenericError')
227
228        event = self.cancel_and_wait(resume=True)
229        self.assert_qmp(event, 'data/type', 'backup')
230
231    def test_set_speed_invalid_drive_backup(self):
232        self.do_test_set_speed_invalid('drive-backup', target_img)
233
234    def test_set_speed_invalid_blockdev_backup(self):
235        self.do_test_set_speed_invalid('blockdev-backup',  'drive1')
236
237class TestSingleTransaction(iotests.QMPTestCase):
238    image_len = 64 * 1024 * 1024 # MB
239
240    def setUp(self):
241        qemu_img('create', '-f', iotests.imgfmt, test_img, str(TestSingleTransaction.image_len))
242        qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 0 64k', test_img)
243        qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img)
244        qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img)
245        qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 67043328 64k', test_img)
246        qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(TestSingleDrive.image_len))
247
248        self.vm = iotests.VM().add_drive(test_img).add_drive(blockdev_target_img)
249        self.vm.launch()
250
251    def tearDown(self):
252        self.vm.shutdown()
253        os.remove(test_img)
254        os.remove(blockdev_target_img)
255        try:
256            os.remove(target_img)
257        except OSError:
258            pass
259
260    def do_test_cancel(self, cmd, target):
261        self.assert_no_active_block_jobs()
262
263        result = self.vm.qmp('transaction', actions=[{
264                'type': cmd,
265                'data': { 'device': 'drive0',
266                          'target': target,
267                          'sync': 'full' },
268            }
269        ])
270
271        self.assert_qmp(result, 'return', {})
272
273        event = self.cancel_and_wait()
274        self.assert_qmp(event, 'data/type', 'backup')
275
276    def test_cancel_drive_backup(self):
277        self.do_test_cancel('drive-backup', target_img)
278
279    def test_cancel_blockdev_backup(self):
280        self.do_test_cancel('blockdev-backup', 'drive1')
281
282    def do_test_pause(self, cmd, target, image):
283        self.assert_no_active_block_jobs()
284
285        self.vm.pause_drive('drive0')
286        result = self.vm.qmp('transaction', actions=[{
287                'type': cmd,
288                'data': { 'device': 'drive0',
289                          'target': target,
290                          'sync': 'full' },
291            }
292        ])
293        self.assert_qmp(result, 'return', {})
294
295        result = self.vm.qmp('block-job-pause', device='drive0')
296        self.assert_qmp(result, 'return', {})
297
298        self.vm.resume_drive('drive0')
299        time.sleep(1)
300        result = self.vm.qmp('query-block-jobs')
301        offset = self.dictpath(result, 'return[0]/offset')
302
303        time.sleep(1)
304        result = self.vm.qmp('query-block-jobs')
305        self.assert_qmp(result, 'return[0]/offset', offset)
306
307        result = self.vm.qmp('block-job-resume', device='drive0')
308        self.assert_qmp(result, 'return', {})
309
310        self.wait_until_completed()
311
312        self.vm.shutdown()
313        self.assertTrue(iotests.compare_images(test_img, image),
314                        'target image does not match source after backup')
315
316    def test_pause_drive_backup(self):
317        self.do_test_pause('drive-backup', target_img, target_img)
318
319    def test_pause_blockdev_backup(self):
320        self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
321
322    def do_test_medium_not_found(self, cmd, target):
323        result = self.vm.qmp('transaction', actions=[{
324                'type': cmd,
325                'data': { 'device': 'ide1-cd0',
326                          'target': target,
327                          'sync': 'full' },
328            }
329        ])
330        self.assert_qmp(result, 'error/class', 'GenericError')
331
332    def test_medium_not_found_drive_backup(self):
333        self.do_test_medium_not_found('drive-backup', target_img)
334
335    def test_medium_not_found_blockdev_backup(self):
336        self.do_test_medium_not_found('blockdev-backup', 'drive1')
337
338    def test_image_not_found(self):
339        result = self.vm.qmp('transaction', actions=[{
340                'type': 'drive-backup',
341                'data': { 'device': 'drive0',
342                          'mode': 'existing',
343                          'target': target_img,
344                          'sync': 'full' },
345            }
346        ])
347        self.assert_qmp(result, 'error/class', 'GenericError')
348
349    def test_device_not_found(self):
350        result = self.vm.qmp('transaction', actions=[{
351                'type': 'drive-backup',
352                'data': { 'device': 'nonexistent',
353                          'mode': 'existing',
354                          'target': target_img,
355                          'sync': 'full' },
356            }
357        ])
358        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
359
360        result = self.vm.qmp('transaction', actions=[{
361                'type': 'blockdev-backup',
362                'data': { 'device': 'nonexistent',
363                          'target': 'drive1',
364                          'sync': 'full' },
365            }
366        ])
367        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
368
369        result = self.vm.qmp('transaction', actions=[{
370                'type': 'blockdev-backup',
371                'data': { 'device': 'drive0',
372                          'target': 'nonexistent',
373                          'sync': 'full' },
374            }
375        ])
376        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
377
378        result = self.vm.qmp('transaction', actions=[{
379                'type': 'blockdev-backup',
380                'data': { 'device': 'nonexistent',
381                          'target': 'nonexistent',
382                          'sync': 'full' },
383            }
384        ])
385        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
386
387    def test_target_is_source(self):
388        result = self.vm.qmp('transaction', actions=[{
389                'type': 'blockdev-backup',
390                'data': { 'device': 'drive0',
391                          'target': 'drive0',
392                          'sync': 'full' },
393            }
394        ])
395        self.assert_qmp(result, 'error/class', 'GenericError')
396
397    def test_abort(self):
398        result = self.vm.qmp('transaction', actions=[{
399                'type': 'drive-backup',
400                'data': { 'device': 'nonexistent',
401                          'mode': 'existing',
402                          'target': target_img,
403                          'sync': 'full' },
404            }, {
405                'type': 'Abort',
406                'data': {},
407            }
408        ])
409        self.assert_qmp(result, 'error/class', 'GenericError')
410        self.assert_no_active_block_jobs()
411
412        result = self.vm.qmp('transaction', actions=[{
413                'type': 'blockdev-backup',
414                'data': { 'device': 'nonexistent',
415                          'target': 'drive1',
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': 'drive0',
428                          'target': 'nonexistent',
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
438if __name__ == '__main__':
439    iotests.main(supported_fmts=['raw', 'qcow2'])
440