xref: /openbmc/qemu/tests/qemu-iotests/056 (revision 6d8be9676278ae41c79bfde820fbccbd021e5ac2)
1e3409362SIan Main#!/usr/bin/env python
2e3409362SIan Main#
3e3409362SIan Main# Tests for drive-backup
4e3409362SIan Main#
5e3409362SIan Main# Copyright (C) 2013 Red Hat, Inc.
6e3409362SIan Main#
7e3409362SIan Main# Based on 041.
8e3409362SIan Main#
9e3409362SIan Main# This program is free software; you can redistribute it and/or modify
10e3409362SIan Main# it under the terms of the GNU General Public License as published by
11e3409362SIan Main# the Free Software Foundation; either version 2 of the License, or
12e3409362SIan Main# (at your option) any later version.
13e3409362SIan Main#
14e3409362SIan Main# This program is distributed in the hope that it will be useful,
15e3409362SIan Main# but WITHOUT ANY WARRANTY; without even the implied warranty of
16e3409362SIan Main# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17e3409362SIan Main# GNU General Public License for more details.
18e3409362SIan Main#
19e3409362SIan Main# You should have received a copy of the GNU General Public License
20e3409362SIan Main# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21e3409362SIan Main#
22e3409362SIan Main
23e3409362SIan Mainimport time
24e3409362SIan Mainimport os
25e3409362SIan Mainimport iotests
26e3409362SIan Mainfrom iotests import qemu_img, qemu_io, create_image
27e3409362SIan Main
28e3409362SIan Mainbacking_img = os.path.join(iotests.test_dir, 'backing.img')
29e3409362SIan Maintest_img = os.path.join(iotests.test_dir, 'test.img')
30e3409362SIan Maintarget_img = os.path.join(iotests.test_dir, 'target.img')
31e3409362SIan Main
32*6d8be967SJohn Snowdef img_create(img, fmt=iotests.imgfmt, size='64M', **kwargs):
33*6d8be967SJohn Snow    fullname = os.path.join(iotests.test_dir, '%s.%s' % (img, fmt))
34*6d8be967SJohn Snow    optargs = []
35*6d8be967SJohn Snow    for k,v in kwargs.iteritems():
36*6d8be967SJohn Snow        optargs = optargs + ['-o', '%s=%s' % (k,v)]
37*6d8be967SJohn Snow    args = ['create', '-f', fmt] + optargs + [fullname, size]
38*6d8be967SJohn Snow    iotests.qemu_img(*args)
39*6d8be967SJohn Snow    return fullname
40*6d8be967SJohn Snow
41*6d8be967SJohn Snowdef try_remove(img):
42*6d8be967SJohn Snow    try:
43*6d8be967SJohn Snow        os.remove(img)
44*6d8be967SJohn Snow    except OSError:
45*6d8be967SJohn Snow        pass
46*6d8be967SJohn Snow
47*6d8be967SJohn Snowdef io_write_patterns(img, patterns):
48*6d8be967SJohn Snow    for pattern in patterns:
49*6d8be967SJohn Snow        iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
50*6d8be967SJohn Snow
51*6d8be967SJohn Snow
52e3409362SIan Mainclass TestSyncModesNoneAndTop(iotests.QMPTestCase):
53e3409362SIan Main    image_len = 64 * 1024 * 1024 # MB
54e3409362SIan Main
55e3409362SIan Main    def setUp(self):
56e3409362SIan Main        create_image(backing_img, TestSyncModesNoneAndTop.image_len)
57e3409362SIan Main        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
58e3409362SIan Main        qemu_io('-c', 'write -P0x41 0 512', test_img)
59e3409362SIan Main        qemu_io('-c', 'write -P0xd5 1M 32k', test_img)
60e3409362SIan Main        qemu_io('-c', 'write -P0xdc 32M 124k', test_img)
61e3409362SIan Main        qemu_io('-c', 'write -P0xdc 67043328 64k', test_img)
62e3409362SIan Main        self.vm = iotests.VM().add_drive(test_img)
63e3409362SIan Main        self.vm.launch()
64e3409362SIan Main
65e3409362SIan Main    def tearDown(self):
66e3409362SIan Main        self.vm.shutdown()
67e3409362SIan Main        os.remove(test_img)
68e3409362SIan Main        os.remove(backing_img)
69e3409362SIan Main        try:
70e3409362SIan Main            os.remove(target_img)
71e3409362SIan Main        except OSError:
72e3409362SIan Main            pass
73e3409362SIan Main
74e3409362SIan Main    def test_complete_top(self):
75e3409362SIan Main        self.assert_no_active_block_jobs()
76e3409362SIan Main        result = self.vm.qmp('drive-backup', device='drive0', sync='top',
77e3409362SIan Main                             format=iotests.imgfmt, target=target_img)
78e3409362SIan Main        self.assert_qmp(result, 'return', {})
79e3409362SIan Main
809974ad40SFam Zheng        self.wait_until_completed(check_offset=False)
81e3409362SIan Main
82e3409362SIan Main        self.assert_no_active_block_jobs()
83e3409362SIan Main        self.vm.shutdown()
84e3409362SIan Main        self.assertTrue(iotests.compare_images(test_img, target_img),
85e3409362SIan Main                        'target image does not match source after backup')
86e3409362SIan Main
87e3409362SIan Main    def test_cancel_sync_none(self):
88e3409362SIan Main        self.assert_no_active_block_jobs()
89e3409362SIan Main
90e3409362SIan Main        result = self.vm.qmp('drive-backup', device='drive0',
91e3409362SIan Main                             sync='none', target=target_img)
92e3409362SIan Main        self.assert_qmp(result, 'return', {})
93e3409362SIan Main        time.sleep(1)
94e3409362SIan Main        self.vm.hmp_qemu_io('drive0', 'write -P0x5e 0 512')
95e3409362SIan Main        self.vm.hmp_qemu_io('drive0', 'aio_flush')
96e3409362SIan Main        # Verify that the original contents exist in the target image.
97e3409362SIan Main
98e3409362SIan Main        event = self.cancel_and_wait()
99e3409362SIan Main        self.assert_qmp(event, 'data/type', 'backup')
100e3409362SIan Main
101e3409362SIan Main        self.vm.shutdown()
102e3409362SIan Main        time.sleep(1)
103e3409362SIan Main        self.assertEqual(-1, qemu_io('-c', 'read -P0x41 0 512', target_img).find("verification failed"))
104e3409362SIan Main
1059cc0f19dSFam Zhengclass TestBeforeWriteNotifier(iotests.QMPTestCase):
1069cc0f19dSFam Zheng    def setUp(self):
1079cc0f19dSFam Zheng        self.vm = iotests.VM().add_drive_raw("file=blkdebug::null-co://,id=drive0,align=65536,driver=blkdebug")
1089cc0f19dSFam Zheng        self.vm.launch()
1099cc0f19dSFam Zheng
1109cc0f19dSFam Zheng    def tearDown(self):
1119cc0f19dSFam Zheng        self.vm.shutdown()
1129cc0f19dSFam Zheng        os.remove(target_img)
1139cc0f19dSFam Zheng
1149cc0f19dSFam Zheng    def test_before_write_notifier(self):
1159cc0f19dSFam Zheng        self.vm.pause_drive("drive0")
1169cc0f19dSFam Zheng        result = self.vm.qmp('drive-backup', device='drive0',
1179cc0f19dSFam Zheng                             sync='full', target=target_img,
1189cc0f19dSFam Zheng                             format="file", speed=1)
1199cc0f19dSFam Zheng        self.assert_qmp(result, 'return', {})
1209cc0f19dSFam Zheng        result = self.vm.qmp('block-job-pause', device="drive0")
1219cc0f19dSFam Zheng        self.assert_qmp(result, 'return', {})
1229cc0f19dSFam Zheng        # Speed is low enough that this must be an uncopied range, which will
1239cc0f19dSFam Zheng        # trigger the before write notifier
1249cc0f19dSFam Zheng        self.vm.hmp_qemu_io('drive0', 'aio_write -P 1 512512 512')
1259cc0f19dSFam Zheng        self.vm.resume_drive("drive0")
1269cc0f19dSFam Zheng        result = self.vm.qmp('block-job-resume', device="drive0")
1279cc0f19dSFam Zheng        self.assert_qmp(result, 'return', {})
1289cc0f19dSFam Zheng        event = self.cancel_and_wait()
1299cc0f19dSFam Zheng        self.assert_qmp(event, 'data/type', 'backup')
130e3409362SIan Main
131*6d8be967SJohn Snowclass BackupTest(iotests.QMPTestCase):
132*6d8be967SJohn Snow    def setUp(self):
133*6d8be967SJohn Snow        self.vm = iotests.VM()
134*6d8be967SJohn Snow        self.test_img = img_create('test')
135*6d8be967SJohn Snow        self.dest_img = img_create('dest')
136*6d8be967SJohn Snow        self.vm.add_drive(self.test_img)
137*6d8be967SJohn Snow        self.vm.launch()
138*6d8be967SJohn Snow
139*6d8be967SJohn Snow    def tearDown(self):
140*6d8be967SJohn Snow        self.vm.shutdown()
141*6d8be967SJohn Snow        try_remove(self.test_img)
142*6d8be967SJohn Snow        try_remove(self.dest_img)
143*6d8be967SJohn Snow
144*6d8be967SJohn Snow    def hmp_io_writes(self, drive, patterns):
145*6d8be967SJohn Snow        for pattern in patterns:
146*6d8be967SJohn Snow            self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
147*6d8be967SJohn Snow        self.vm.hmp_qemu_io(drive, 'flush')
148*6d8be967SJohn Snow
149*6d8be967SJohn Snow    def qmp_backup_and_wait(self, cmd='drive-backup', serror=None,
150*6d8be967SJohn Snow                            aerror=None, **kwargs):
151*6d8be967SJohn Snow        if not self.qmp_backup(cmd, serror, **kwargs):
152*6d8be967SJohn Snow            return False
153*6d8be967SJohn Snow        return self.qmp_backup_wait(kwargs['device'], aerror)
154*6d8be967SJohn Snow
155*6d8be967SJohn Snow    def qmp_backup(self, cmd='drive-backup',
156*6d8be967SJohn Snow                   error=None, **kwargs):
157*6d8be967SJohn Snow        self.assertTrue('device' in kwargs)
158*6d8be967SJohn Snow        res = self.vm.qmp(cmd, **kwargs)
159*6d8be967SJohn Snow        if error:
160*6d8be967SJohn Snow            self.assert_qmp(res, 'error/desc', error)
161*6d8be967SJohn Snow            return False
162*6d8be967SJohn Snow        self.assert_qmp(res, 'return', {})
163*6d8be967SJohn Snow        return True
164*6d8be967SJohn Snow
165*6d8be967SJohn Snow    def qmp_backup_wait(self, device, error=None):
166*6d8be967SJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
167*6d8be967SJohn Snow                                   match={'data': {'device': device}})
168*6d8be967SJohn Snow        self.assertNotEqual(event, None)
169*6d8be967SJohn Snow        try:
170*6d8be967SJohn Snow            failure = self.dictpath(event, 'data/error')
171*6d8be967SJohn Snow        except AssertionError:
172*6d8be967SJohn Snow            # Backup succeeded.
173*6d8be967SJohn Snow            self.assert_qmp(event, 'data/offset', event['data']['len'])
174*6d8be967SJohn Snow            return True
175*6d8be967SJohn Snow        else:
176*6d8be967SJohn Snow            # Failure.
177*6d8be967SJohn Snow            self.assert_qmp(event, 'data/error', qerror)
178*6d8be967SJohn Snow            return False
179*6d8be967SJohn Snow
180*6d8be967SJohn Snow    def test_dismiss_false(self):
181*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
182*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
183*6d8be967SJohn Snow        self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
184*6d8be967SJohn Snow                                 sync='full', target=self.dest_img,
185*6d8be967SJohn Snow                                 auto_dismiss=True)
186*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
187*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
188*6d8be967SJohn Snow
189*6d8be967SJohn Snow    def test_dismiss_true(self):
190*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
191*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
192*6d8be967SJohn Snow        self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
193*6d8be967SJohn Snow                                 sync='full', target=self.dest_img,
194*6d8be967SJohn Snow                                 auto_dismiss=False)
195*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
196*6d8be967SJohn Snow        self.assert_qmp(res, 'return[0]/status', 'concluded')
197*6d8be967SJohn Snow        res = self.vm.qmp('block-job-dismiss', id='drive0')
198*6d8be967SJohn Snow        self.assert_qmp(res, 'return', {})
199*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
200*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
201*6d8be967SJohn Snow
202*6d8be967SJohn Snow    def test_dismiss_bad_id(self):
203*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
204*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
205*6d8be967SJohn Snow        res = self.vm.qmp('block-job-dismiss', id='foobar')
206*6d8be967SJohn Snow        self.assert_qmp(res, 'error/class', 'DeviceNotActive')
207*6d8be967SJohn Snow
208*6d8be967SJohn Snow    def test_dismiss_collision(self):
209*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
210*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
211*6d8be967SJohn Snow        self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
212*6d8be967SJohn Snow                                 sync='full', target=self.dest_img,
213*6d8be967SJohn Snow                                 auto_dismiss=False)
214*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
215*6d8be967SJohn Snow        self.assert_qmp(res, 'return[0]/status', 'concluded')
216*6d8be967SJohn Snow        # Leave zombie job un-dismissed, observe a failure:
217*6d8be967SJohn Snow        res = self.qmp_backup_and_wait(serror='Need a root block node',
218*6d8be967SJohn Snow                                       device='drive0', format=iotests.imgfmt,
219*6d8be967SJohn Snow                                       sync='full', target=self.dest_img,
220*6d8be967SJohn Snow                                       auto_dismiss=False)
221*6d8be967SJohn Snow        self.assertEqual(res, False)
222*6d8be967SJohn Snow        # OK, dismiss the zombie.
223*6d8be967SJohn Snow        res = self.vm.qmp('block-job-dismiss', id='drive0')
224*6d8be967SJohn Snow        self.assert_qmp(res, 'return', {})
225*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
226*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
227*6d8be967SJohn Snow        # Ensure it's really gone.
228*6d8be967SJohn Snow        self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
229*6d8be967SJohn Snow                                 sync='full', target=self.dest_img,
230*6d8be967SJohn Snow                                 auto_dismiss=False)
231*6d8be967SJohn Snow
232*6d8be967SJohn Snow    def dismissal_failure(self, dismissal_opt):
233*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
234*6d8be967SJohn Snow        self.assert_qmp(res, 'return', [])
235*6d8be967SJohn Snow        # Give blkdebug something to chew on
236*6d8be967SJohn Snow        self.hmp_io_writes('drive0',
237*6d8be967SJohn Snow                           (('0x9a', 0, 512),
238*6d8be967SJohn Snow                           ('0x55', '8M', '352k'),
239*6d8be967SJohn Snow                           ('0x78', '15872k', '1M')))
240*6d8be967SJohn Snow        # Add destination node via blkdebug
241*6d8be967SJohn Snow        res = self.vm.qmp('blockdev-add',
242*6d8be967SJohn Snow                          node_name='target0',
243*6d8be967SJohn Snow                          driver=iotests.imgfmt,
244*6d8be967SJohn Snow                          file={
245*6d8be967SJohn Snow                              'driver': 'blkdebug',
246*6d8be967SJohn Snow                              'image': {
247*6d8be967SJohn Snow                                  'driver': 'file',
248*6d8be967SJohn Snow                                  'filename': self.dest_img
249*6d8be967SJohn Snow                              },
250*6d8be967SJohn Snow                              'inject-error': [{
251*6d8be967SJohn Snow                                  'event': 'write_aio',
252*6d8be967SJohn Snow                                  'errno': 5,
253*6d8be967SJohn Snow                                  'immediately': False,
254*6d8be967SJohn Snow                                  'once': True
255*6d8be967SJohn Snow                              }],
256*6d8be967SJohn Snow                          })
257*6d8be967SJohn Snow        self.assert_qmp(res, 'return', {})
258*6d8be967SJohn Snow
259*6d8be967SJohn Snow        res = self.qmp_backup(cmd='blockdev-backup',
260*6d8be967SJohn Snow                              device='drive0', target='target0',
261*6d8be967SJohn Snow                              on_target_error='stop',
262*6d8be967SJohn Snow                              sync='full',
263*6d8be967SJohn Snow                              auto_dismiss=dismissal_opt)
264*6d8be967SJohn Snow        self.assertTrue(res)
265*6d8be967SJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
266*6d8be967SJohn Snow                                   match={'data': {'device': 'drive0'}})
267*6d8be967SJohn Snow        self.assertNotEqual(event, None)
268*6d8be967SJohn Snow        # OK, job should be wedged
269*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
270*6d8be967SJohn Snow        self.assert_qmp(res, 'return[0]/status', 'paused')
271*6d8be967SJohn Snow        res = self.vm.qmp('block-job-dismiss', id='drive0')
272*6d8be967SJohn Snow        self.assert_qmp(res, 'error/desc',
273*6d8be967SJohn Snow                        "Job 'drive0' in state 'paused' cannot accept"
274*6d8be967SJohn Snow                        " command verb 'dismiss'")
275*6d8be967SJohn Snow        res = self.vm.qmp('query-block-jobs')
276*6d8be967SJohn Snow        self.assert_qmp(res, 'return[0]/status', 'paused')
277*6d8be967SJohn Snow        # OK, unstick job and move forward.
278*6d8be967SJohn Snow        res = self.vm.qmp('block-job-resume', device='drive0')
279*6d8be967SJohn Snow        self.assert_qmp(res, 'return', {})
280*6d8be967SJohn Snow        # And now we need to wait for it to conclude;
281*6d8be967SJohn Snow        res = self.qmp_backup_wait(device='drive0')
282*6d8be967SJohn Snow        self.assertTrue(res)
283*6d8be967SJohn Snow        if not dismissal_opt:
284*6d8be967SJohn Snow            # Job should now be languishing:
285*6d8be967SJohn Snow            res = self.vm.qmp('query-block-jobs')
286*6d8be967SJohn Snow            self.assert_qmp(res, 'return[0]/status', 'concluded')
287*6d8be967SJohn Snow            res = self.vm.qmp('block-job-dismiss', id='drive0')
288*6d8be967SJohn Snow            self.assert_qmp(res, 'return', {})
289*6d8be967SJohn Snow            res = self.vm.qmp('query-block-jobs')
290*6d8be967SJohn Snow            self.assert_qmp(res, 'return', [])
291*6d8be967SJohn Snow
292*6d8be967SJohn Snow    def test_dismiss_premature(self):
293*6d8be967SJohn Snow        self.dismissal_failure(False)
294*6d8be967SJohn Snow
295*6d8be967SJohn Snow    def test_dismiss_erroneous(self):
296*6d8be967SJohn Snow        self.dismissal_failure(True)
297*6d8be967SJohn Snow
298e3409362SIan Mainif __name__ == '__main__':
299e3409362SIan Main    iotests.main(supported_fmts=['qcow2', 'qed'])
300