xref: /qemu/tests/qemu-iotests/124 (revision ce2cbc49)
19f7264f5SJohn Snow#!/usr/bin/env python
29f7264f5SJohn Snow#
39f7264f5SJohn Snow# Tests for incremental drive-backup
49f7264f5SJohn Snow#
59f7264f5SJohn Snow# Copyright (C) 2015 John Snow for Red Hat, Inc.
69f7264f5SJohn Snow#
79f7264f5SJohn Snow# Based on 056.
89f7264f5SJohn Snow#
99f7264f5SJohn Snow# This program is free software; you can redistribute it and/or modify
109f7264f5SJohn Snow# it under the terms of the GNU General Public License as published by
119f7264f5SJohn Snow# the Free Software Foundation; either version 2 of the License, or
129f7264f5SJohn Snow# (at your option) any later version.
139f7264f5SJohn Snow#
149f7264f5SJohn Snow# This program is distributed in the hope that it will be useful,
159f7264f5SJohn Snow# but WITHOUT ANY WARRANTY; without even the implied warranty of
169f7264f5SJohn Snow# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
179f7264f5SJohn Snow# GNU General Public License for more details.
189f7264f5SJohn Snow#
199f7264f5SJohn Snow# You should have received a copy of the GNU General Public License
209f7264f5SJohn Snow# along with this program.  If not, see <http://www.gnu.org/licenses/>.
219f7264f5SJohn Snow#
229f7264f5SJohn Snow
239f7264f5SJohn Snowimport os
249f7264f5SJohn Snowimport iotests
259f7264f5SJohn Snow
269f7264f5SJohn Snow
279f7264f5SJohn Snowdef io_write_patterns(img, patterns):
289f7264f5SJohn Snow    for pattern in patterns:
299f7264f5SJohn Snow        iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
309f7264f5SJohn Snow
319f7264f5SJohn Snow
32a3d71595SJohn Snowdef try_remove(img):
33a3d71595SJohn Snow    try:
34a3d71595SJohn Snow        os.remove(img)
35a3d71595SJohn Snow    except OSError:
36a3d71595SJohn Snow        pass
37a3d71595SJohn Snow
38a3d71595SJohn Snow
39749ad5e8SJohn Snowdef transaction_action(action, **kwargs):
40749ad5e8SJohn Snow    return {
41749ad5e8SJohn Snow        'type': action,
42fc6c796fSJohn Snow        'data': dict((k.replace('_', '-'), v) for k, v in kwargs.iteritems())
43749ad5e8SJohn Snow    }
44749ad5e8SJohn Snow
45749ad5e8SJohn Snow
46749ad5e8SJohn Snowdef transaction_bitmap_clear(node, name, **kwargs):
47749ad5e8SJohn Snow    return transaction_action('block-dirty-bitmap-clear',
48749ad5e8SJohn Snow                              node=node, name=name, **kwargs)
49749ad5e8SJohn Snow
50749ad5e8SJohn Snow
51749ad5e8SJohn Snowdef transaction_drive_backup(device, target, **kwargs):
52749ad5e8SJohn Snow    return transaction_action('drive-backup', device=device, target=target,
53749ad5e8SJohn Snow                              **kwargs)
54749ad5e8SJohn Snow
55749ad5e8SJohn Snow
56a3d71595SJohn Snowclass Bitmap:
57a3d71595SJohn Snow    def __init__(self, name, drive):
58a3d71595SJohn Snow        self.name = name
59a3d71595SJohn Snow        self.drive = drive
60a3d71595SJohn Snow        self.num = 0
61a3d71595SJohn Snow        self.backups = list()
62a3d71595SJohn Snow
63a3d71595SJohn Snow    def base_target(self):
64a3d71595SJohn Snow        return (self.drive['backup'], None)
65a3d71595SJohn Snow
66a3d71595SJohn Snow    def new_target(self, num=None):
67a3d71595SJohn Snow        if num is None:
68a3d71595SJohn Snow            num = self.num
69a3d71595SJohn Snow        self.num = num + 1
70a3d71595SJohn Snow        base = os.path.join(iotests.test_dir,
71a3d71595SJohn Snow                            "%s.%s." % (self.drive['id'], self.name))
72a3d71595SJohn Snow        suff = "%i.%s" % (num, self.drive['fmt'])
73a3d71595SJohn Snow        target = base + "inc" + suff
74a3d71595SJohn Snow        reference = base + "ref" + suff
75a3d71595SJohn Snow        self.backups.append((target, reference))
76a3d71595SJohn Snow        return (target, reference)
77a3d71595SJohn Snow
78a3d71595SJohn Snow    def last_target(self):
79a3d71595SJohn Snow        if self.backups:
80a3d71595SJohn Snow            return self.backups[-1]
81a3d71595SJohn Snow        return self.base_target()
82a3d71595SJohn Snow
83a3d71595SJohn Snow    def del_target(self):
84a3d71595SJohn Snow        for image in self.backups.pop():
85a3d71595SJohn Snow            try_remove(image)
86a3d71595SJohn Snow        self.num -= 1
87a3d71595SJohn Snow
88a3d71595SJohn Snow    def cleanup(self):
89a3d71595SJohn Snow        for backup in self.backups:
90a3d71595SJohn Snow            for image in backup:
91a3d71595SJohn Snow                try_remove(image)
92a3d71595SJohn Snow
93a3d71595SJohn Snow
941b19bb9dSJohn Snowclass TestIncrementalBackupBase(iotests.QMPTestCase):
951b19bb9dSJohn Snow    def __init__(self, *args):
961b19bb9dSJohn Snow        super(TestIncrementalBackupBase, self).__init__(*args)
979f7264f5SJohn Snow        self.bitmaps = list()
989f7264f5SJohn Snow        self.files = list()
999f7264f5SJohn Snow        self.drives = list()
1009f7264f5SJohn Snow        self.vm = iotests.VM()
1019f7264f5SJohn Snow        self.err_img = os.path.join(iotests.test_dir, 'err.%s' % iotests.imgfmt)
1029f7264f5SJohn Snow
1031b19bb9dSJohn Snow
1041b19bb9dSJohn Snow    def setUp(self):
1059f7264f5SJohn Snow        # Create a base image with a distinctive patterning
1069f7264f5SJohn Snow        drive0 = self.add_node('drive0')
1079f7264f5SJohn Snow        self.img_create(drive0['file'], drive0['fmt'])
1089f7264f5SJohn Snow        self.vm.add_drive(drive0['file'])
1091b19bb9dSJohn Snow        self.write_default_pattern(drive0['file'])
1101b19bb9dSJohn Snow        self.vm.launch()
1111b19bb9dSJohn Snow
1121b19bb9dSJohn Snow
1131b19bb9dSJohn Snow    def write_default_pattern(self, target):
1141b19bb9dSJohn Snow        io_write_patterns(target, (('0x41', 0, 512),
1159f7264f5SJohn Snow                                   ('0xd5', '1M', '32k'),
1169f7264f5SJohn Snow                                   ('0xdc', '32M', '124k')))
1179f7264f5SJohn Snow
1189f7264f5SJohn Snow
1199f7264f5SJohn Snow    def add_node(self, node_id, fmt=iotests.imgfmt, path=None, backup=None):
1209f7264f5SJohn Snow        if path is None:
1219f7264f5SJohn Snow            path = os.path.join(iotests.test_dir, '%s.%s' % (node_id, fmt))
1229f7264f5SJohn Snow        if backup is None:
1239f7264f5SJohn Snow            backup = os.path.join(iotests.test_dir,
1249f7264f5SJohn Snow                                  '%s.full.backup.%s' % (node_id, fmt))
1259f7264f5SJohn Snow
1269f7264f5SJohn Snow        self.drives.append({
1279f7264f5SJohn Snow            'id': node_id,
1289f7264f5SJohn Snow            'file': path,
1299f7264f5SJohn Snow            'backup': backup,
1309f7264f5SJohn Snow            'fmt': fmt })
1319f7264f5SJohn Snow        return self.drives[-1]
1329f7264f5SJohn Snow
1339f7264f5SJohn Snow
1349f7264f5SJohn Snow    def img_create(self, img, fmt=iotests.imgfmt, size='64M',
1359f7264f5SJohn Snow                   parent=None, parentFormat=None):
1369f7264f5SJohn Snow        if parent:
1379f7264f5SJohn Snow            if parentFormat is None:
1389f7264f5SJohn Snow                parentFormat = fmt
1399f7264f5SJohn Snow            iotests.qemu_img('create', '-f', fmt, img, size,
1409f7264f5SJohn Snow                             '-b', parent, '-F', parentFormat)
1419f7264f5SJohn Snow        else:
1429f7264f5SJohn Snow            iotests.qemu_img('create', '-f', fmt, img, size)
1439f7264f5SJohn Snow        self.files.append(img)
1449f7264f5SJohn Snow
145a3d71595SJohn Snow
146a3d71595SJohn Snow    def do_qmp_backup(self, error='Input/output error', **kwargs):
147a3d71595SJohn Snow        res = self.vm.qmp('drive-backup', **kwargs)
148a3d71595SJohn Snow        self.assert_qmp(res, 'return', {})
149fc6c796fSJohn Snow        return self.wait_qmp_backup(kwargs['device'], error)
150a3d71595SJohn Snow
151fc6c796fSJohn Snow
152fc6c796fSJohn Snow    def wait_qmp_backup(self, device, error='Input/output error'):
153a3d71595SJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
154fc6c796fSJohn Snow                                   match={'data': {'device': device}})
155ff793890SJohn Snow        self.assertNotEqual(event, None)
156a3d71595SJohn Snow
157a3d71595SJohn Snow        try:
158a3d71595SJohn Snow            failure = self.dictpath(event, 'data/error')
159a3d71595SJohn Snow        except AssertionError:
160a3d71595SJohn Snow            # Backup succeeded.
161a3d71595SJohn Snow            self.assert_qmp(event, 'data/offset', event['data']['len'])
162a3d71595SJohn Snow            return True
163a3d71595SJohn Snow        else:
164a3d71595SJohn Snow            # Backup failed.
165a3d71595SJohn Snow            self.assert_qmp(event, 'data/error', error)
166a3d71595SJohn Snow            return False
167a3d71595SJohn Snow
168a3d71595SJohn Snow
169fc6c796fSJohn Snow    def wait_qmp_backup_cancelled(self, device):
170fc6c796fSJohn Snow        event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED',
171fc6c796fSJohn Snow                                   match={'data': {'device': device}})
172fc6c796fSJohn Snow        self.assertNotEqual(event, None)
173fc6c796fSJohn Snow
174fc6c796fSJohn Snow
175a3d71595SJohn Snow    def create_anchor_backup(self, drive=None):
176a3d71595SJohn Snow        if drive is None:
177a3d71595SJohn Snow            drive = self.drives[-1]
178a3d71595SJohn Snow        res = self.do_qmp_backup(device=drive['id'], sync='full',
179a3d71595SJohn Snow                                 format=drive['fmt'], target=drive['backup'])
180a3d71595SJohn Snow        self.assertTrue(res)
181a3d71595SJohn Snow        self.files.append(drive['backup'])
182a3d71595SJohn Snow        return drive['backup']
183a3d71595SJohn Snow
184a3d71595SJohn Snow
185a3d71595SJohn Snow    def make_reference_backup(self, bitmap=None):
186a3d71595SJohn Snow        if bitmap is None:
187a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
188a3d71595SJohn Snow        _, reference = bitmap.last_target()
189a3d71595SJohn Snow        res = self.do_qmp_backup(device=bitmap.drive['id'], sync='full',
190a3d71595SJohn Snow                                 format=bitmap.drive['fmt'], target=reference)
191a3d71595SJohn Snow        self.assertTrue(res)
192a3d71595SJohn Snow
193a3d71595SJohn Snow
19459fc5d84SJohn Snow    def add_bitmap(self, name, drive, **kwargs):
195a3d71595SJohn Snow        bitmap = Bitmap(name, drive)
196a3d71595SJohn Snow        self.bitmaps.append(bitmap)
197a3d71595SJohn Snow        result = self.vm.qmp('block-dirty-bitmap-add', node=drive['id'],
19859fc5d84SJohn Snow                             name=bitmap.name, **kwargs)
199a3d71595SJohn Snow        self.assert_qmp(result, 'return', {})
200a3d71595SJohn Snow        return bitmap
201a3d71595SJohn Snow
202a3d71595SJohn Snow
203a3d71595SJohn Snow    def prepare_backup(self, bitmap=None, parent=None):
204a3d71595SJohn Snow        if bitmap is None:
205a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
206a3d71595SJohn Snow        if parent is None:
207a3d71595SJohn Snow            parent, _ = bitmap.last_target()
208a3d71595SJohn Snow
209a3d71595SJohn Snow        target, _ = bitmap.new_target()
210a3d71595SJohn Snow        self.img_create(target, bitmap.drive['fmt'], parent=parent)
211a3d71595SJohn Snow        return target
212a3d71595SJohn Snow
213a3d71595SJohn Snow
214a3d71595SJohn Snow    def create_incremental(self, bitmap=None, parent=None,
215a3d71595SJohn Snow                           parentFormat=None, validate=True):
216a3d71595SJohn Snow        if bitmap is None:
217a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
218a3d71595SJohn Snow        if parent is None:
219a3d71595SJohn Snow            parent, _ = bitmap.last_target()
220a3d71595SJohn Snow
221a3d71595SJohn Snow        target = self.prepare_backup(bitmap, parent)
222a3d71595SJohn Snow        res = self.do_qmp_backup(device=bitmap.drive['id'],
2234b80ab2bSJohn Snow                                 sync='incremental', bitmap=bitmap.name,
224a3d71595SJohn Snow                                 format=bitmap.drive['fmt'], target=target,
225a3d71595SJohn Snow                                 mode='existing')
226a3d71595SJohn Snow        if not res:
227a3d71595SJohn Snow            bitmap.del_target();
228a3d71595SJohn Snow            self.assertFalse(validate)
229a3d71595SJohn Snow        else:
230a3d71595SJohn Snow            self.make_reference_backup(bitmap)
231a3d71595SJohn Snow        return res
232a3d71595SJohn Snow
233a3d71595SJohn Snow
234a3d71595SJohn Snow    def check_backups(self):
235a3d71595SJohn Snow        for bitmap in self.bitmaps:
236a3d71595SJohn Snow            for incremental, reference in bitmap.backups:
237a3d71595SJohn Snow                self.assertTrue(iotests.compare_images(incremental, reference))
238a3d71595SJohn Snow            last = bitmap.last_target()[0]
239a3d71595SJohn Snow            self.assertTrue(iotests.compare_images(last, bitmap.drive['file']))
240a3d71595SJohn Snow
241a3d71595SJohn Snow
242a3d71595SJohn Snow    def hmp_io_writes(self, drive, patterns):
243a3d71595SJohn Snow        for pattern in patterns:
244a3d71595SJohn Snow            self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
245a3d71595SJohn Snow        self.vm.hmp_qemu_io(drive, 'flush')
246a3d71595SJohn Snow
247a3d71595SJohn Snow
24859fc5d84SJohn Snow    def do_incremental_simple(self, **kwargs):
249a3d71595SJohn Snow        self.create_anchor_backup()
25059fc5d84SJohn Snow        self.add_bitmap('bitmap0', self.drives[0], **kwargs)
251a3d71595SJohn Snow
252a3d71595SJohn Snow        # Sanity: Create a "hollow" incremental backup
253a3d71595SJohn Snow        self.create_incremental()
254a3d71595SJohn Snow        # Three writes: One complete overwrite, one new segment,
255a3d71595SJohn Snow        # and one partial overlap.
256a3d71595SJohn Snow        self.hmp_io_writes(self.drives[0]['id'], (('0xab', 0, 512),
257a3d71595SJohn Snow                                                  ('0xfe', '16M', '256k'),
258a3d71595SJohn Snow                                                  ('0x64', '32736k', '64k')))
259a3d71595SJohn Snow        self.create_incremental()
260a3d71595SJohn Snow        # Three more writes, one of each kind, like above
261a3d71595SJohn Snow        self.hmp_io_writes(self.drives[0]['id'], (('0x9a', 0, 512),
262a3d71595SJohn Snow                                                  ('0x55', '8M', '352k'),
263a3d71595SJohn Snow                                                  ('0x78', '15872k', '1M')))
264a3d71595SJohn Snow        self.create_incremental()
265a3d71595SJohn Snow        self.vm.shutdown()
266a3d71595SJohn Snow        self.check_backups()
267a3d71595SJohn Snow
268a3d71595SJohn Snow
2691b19bb9dSJohn Snow    def tearDown(self):
2701b19bb9dSJohn Snow        self.vm.shutdown()
2711b19bb9dSJohn Snow        for bitmap in self.bitmaps:
2721b19bb9dSJohn Snow            bitmap.cleanup()
2731b19bb9dSJohn Snow        for filename in self.files:
2741b19bb9dSJohn Snow            try_remove(filename)
2751b19bb9dSJohn Snow
2761b19bb9dSJohn Snow
2771b19bb9dSJohn Snow
2781b19bb9dSJohn Snowclass TestIncrementalBackup(TestIncrementalBackupBase):
27959fc5d84SJohn Snow    def test_incremental_simple(self):
28059fc5d84SJohn Snow        '''
28159fc5d84SJohn Snow        Test: Create and verify three incremental backups.
28259fc5d84SJohn Snow
28359fc5d84SJohn Snow        Create a bitmap and a full backup before VM execution begins,
28459fc5d84SJohn Snow        then create a series of three incremental backups "during execution,"
28559fc5d84SJohn Snow        i.e.; after IO requests begin modifying the drive.
28659fc5d84SJohn Snow        '''
28759fc5d84SJohn Snow        return self.do_incremental_simple()
28859fc5d84SJohn Snow
28959fc5d84SJohn Snow
29059fc5d84SJohn Snow    def test_small_granularity(self):
29159fc5d84SJohn Snow        '''
29259fc5d84SJohn Snow        Test: Create and verify backups made with a small granularity bitmap.
29359fc5d84SJohn Snow
29459fc5d84SJohn Snow        Perform the same test as test_incremental_simple, but with a granularity
29559fc5d84SJohn Snow        of only 32KiB instead of the present default of 64KiB.
29659fc5d84SJohn Snow        '''
29759fc5d84SJohn Snow        return self.do_incremental_simple(granularity=32768)
29859fc5d84SJohn Snow
29959fc5d84SJohn Snow
30059fc5d84SJohn Snow    def test_large_granularity(self):
30159fc5d84SJohn Snow        '''
30259fc5d84SJohn Snow        Test: Create and verify backups made with a large granularity bitmap.
30359fc5d84SJohn Snow
30459fc5d84SJohn Snow        Perform the same test as test_incremental_simple, but with a granularity
30559fc5d84SJohn Snow        of 128KiB instead of the present default of 64KiB.
30659fc5d84SJohn Snow        '''
30759fc5d84SJohn Snow        return self.do_incremental_simple(granularity=131072)
30859fc5d84SJohn Snow
30959fc5d84SJohn Snow
310749ad5e8SJohn Snow    def test_incremental_transaction(self):
311749ad5e8SJohn Snow        '''Test: Verify backups made from transactionally created bitmaps.
312749ad5e8SJohn Snow
313749ad5e8SJohn Snow        Create a bitmap "before" VM execution begins, then create a second
314749ad5e8SJohn Snow        bitmap AFTER writes have already occurred. Use transactions to create
315749ad5e8SJohn Snow        a full backup and synchronize both bitmaps to this backup.
316749ad5e8SJohn Snow        Create an incremental backup through both bitmaps and verify that
317749ad5e8SJohn Snow        both backups match the current drive0 image.
318749ad5e8SJohn Snow        '''
319749ad5e8SJohn Snow
320749ad5e8SJohn Snow        drive0 = self.drives[0]
321749ad5e8SJohn Snow        bitmap0 = self.add_bitmap('bitmap0', drive0)
322749ad5e8SJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
323749ad5e8SJohn Snow                                          ('0xfe', '16M', '256k'),
324749ad5e8SJohn Snow                                          ('0x64', '32736k', '64k')))
325749ad5e8SJohn Snow        bitmap1 = self.add_bitmap('bitmap1', drive0)
326749ad5e8SJohn Snow
327749ad5e8SJohn Snow        result = self.vm.qmp('transaction', actions=[
328749ad5e8SJohn Snow            transaction_bitmap_clear(bitmap0.drive['id'], bitmap0.name),
329749ad5e8SJohn Snow            transaction_bitmap_clear(bitmap1.drive['id'], bitmap1.name),
330749ad5e8SJohn Snow            transaction_drive_backup(drive0['id'], drive0['backup'],
331749ad5e8SJohn Snow                                     sync='full', format=drive0['fmt'])
332749ad5e8SJohn Snow        ])
333749ad5e8SJohn Snow        self.assert_qmp(result, 'return', {})
334749ad5e8SJohn Snow        self.wait_until_completed(drive0['id'])
335749ad5e8SJohn Snow        self.files.append(drive0['backup'])
336749ad5e8SJohn Snow
337749ad5e8SJohn Snow        self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512),
338749ad5e8SJohn Snow                                          ('0x55', '8M', '352k'),
339749ad5e8SJohn Snow                                          ('0x78', '15872k', '1M')))
340749ad5e8SJohn Snow        # Both bitmaps should be correctly in sync.
341749ad5e8SJohn Snow        self.create_incremental(bitmap0)
342749ad5e8SJohn Snow        self.create_incremental(bitmap1)
343749ad5e8SJohn Snow        self.vm.shutdown()
344749ad5e8SJohn Snow        self.check_backups()
345749ad5e8SJohn Snow
346749ad5e8SJohn Snow
347fc6c796fSJohn Snow    def test_transaction_failure(self):
348fc6c796fSJohn Snow        '''Test: Verify backups made from a transaction that partially fails.
349fc6c796fSJohn Snow
350fc6c796fSJohn Snow        Add a second drive with its own unique pattern, and add a bitmap to each
351fc6c796fSJohn Snow        drive. Use blkdebug to interfere with the backup on just one drive and
352fc6c796fSJohn Snow        attempt to create a coherent incremental backup across both drives.
353fc6c796fSJohn Snow
354fc6c796fSJohn Snow        verify a failure in one but not both, then delete the failed stubs and
355fc6c796fSJohn Snow        re-run the same transaction.
356fc6c796fSJohn Snow
357fc6c796fSJohn Snow        verify that both incrementals are created successfully.
358fc6c796fSJohn Snow        '''
359fc6c796fSJohn Snow
360fc6c796fSJohn Snow        # Create a second drive, with pattern:
361fc6c796fSJohn Snow        drive1 = self.add_node('drive1')
362fc6c796fSJohn Snow        self.img_create(drive1['file'], drive1['fmt'])
363fc6c796fSJohn Snow        io_write_patterns(drive1['file'], (('0x14', 0, 512),
364fc6c796fSJohn Snow                                           ('0x5d', '1M', '32k'),
365fc6c796fSJohn Snow                                           ('0xcd', '32M', '124k')))
366fc6c796fSJohn Snow
367fc6c796fSJohn Snow        # Create a blkdebug interface to this img as 'drive1'
368fc6c796fSJohn Snow        result = self.vm.qmp('blockdev-add', options={
369fc6c796fSJohn Snow            'id': drive1['id'],
370fc6c796fSJohn Snow            'driver': drive1['fmt'],
371fc6c796fSJohn Snow            'file': {
372fc6c796fSJohn Snow                'driver': 'blkdebug',
373fc6c796fSJohn Snow                'image': {
374fc6c796fSJohn Snow                    'driver': 'file',
375fc6c796fSJohn Snow                    'filename': drive1['file']
376fc6c796fSJohn Snow                },
377fc6c796fSJohn Snow                'set-state': [{
378fc6c796fSJohn Snow                    'event': 'flush_to_disk',
379fc6c796fSJohn Snow                    'state': 1,
380fc6c796fSJohn Snow                    'new_state': 2
381fc6c796fSJohn Snow                }],
382fc6c796fSJohn Snow                'inject-error': [{
383fc6c796fSJohn Snow                    'event': 'read_aio',
384fc6c796fSJohn Snow                    'errno': 5,
385fc6c796fSJohn Snow                    'state': 2,
386fc6c796fSJohn Snow                    'immediately': False,
387fc6c796fSJohn Snow                    'once': True
388fc6c796fSJohn Snow                }],
389fc6c796fSJohn Snow            }
390fc6c796fSJohn Snow        })
391fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
392fc6c796fSJohn Snow
393fc6c796fSJohn Snow        # Create bitmaps and full backups for both drives
394fc6c796fSJohn Snow        drive0 = self.drives[0]
395fc6c796fSJohn Snow        dr0bm0 = self.add_bitmap('bitmap0', drive0)
396fc6c796fSJohn Snow        dr1bm0 = self.add_bitmap('bitmap0', drive1)
397fc6c796fSJohn Snow        self.create_anchor_backup(drive0)
398fc6c796fSJohn Snow        self.create_anchor_backup(drive1)
399fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
400fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
401fc6c796fSJohn Snow
402fc6c796fSJohn Snow        # Emulate some writes
403fc6c796fSJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
404fc6c796fSJohn Snow                                          ('0xfe', '16M', '256k'),
405fc6c796fSJohn Snow                                          ('0x64', '32736k', '64k')))
406fc6c796fSJohn Snow        self.hmp_io_writes(drive1['id'], (('0xba', 0, 512),
407fc6c796fSJohn Snow                                          ('0xef', '16M', '256k'),
408fc6c796fSJohn Snow                                          ('0x46', '32736k', '64k')))
409fc6c796fSJohn Snow
410fc6c796fSJohn Snow        # Create incremental backup targets
411fc6c796fSJohn Snow        target0 = self.prepare_backup(dr0bm0)
412fc6c796fSJohn Snow        target1 = self.prepare_backup(dr1bm0)
413fc6c796fSJohn Snow
414fc6c796fSJohn Snow        # Ask for a new incremental backup per-each drive,
415fc6c796fSJohn Snow        # expecting drive1's backup to fail:
416fc6c796fSJohn Snow        transaction = [
417fc6c796fSJohn Snow            transaction_drive_backup(drive0['id'], target0, sync='incremental',
418fc6c796fSJohn Snow                                     format=drive0['fmt'], mode='existing',
419fc6c796fSJohn Snow                                     bitmap=dr0bm0.name),
420fc6c796fSJohn Snow            transaction_drive_backup(drive1['id'], target1, sync='incremental',
421fc6c796fSJohn Snow                                     format=drive1['fmt'], mode='existing',
422fc6c796fSJohn Snow                                     bitmap=dr1bm0.name)
423fc6c796fSJohn Snow        ]
424fc6c796fSJohn Snow        result = self.vm.qmp('transaction', actions=transaction,
425fc6c796fSJohn Snow                             properties={'completion-mode': 'grouped'} )
426fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
427fc6c796fSJohn Snow
428fc6c796fSJohn Snow        # Observe that drive0's backup is cancelled and drive1 completes with
429fc6c796fSJohn Snow        # an error.
430fc6c796fSJohn Snow        self.wait_qmp_backup_cancelled(drive0['id'])
431fc6c796fSJohn Snow        self.assertFalse(self.wait_qmp_backup(drive1['id']))
432fc6c796fSJohn Snow        error = self.vm.event_wait('BLOCK_JOB_ERROR')
433fc6c796fSJohn Snow        self.assert_qmp(error, 'data', {'device': drive1['id'],
434fc6c796fSJohn Snow                                        'action': 'report',
435fc6c796fSJohn Snow                                        'operation': 'read'})
436fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
437fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
438fc6c796fSJohn Snow
439fc6c796fSJohn Snow        # Delete drive0's successful target and eliminate our record of the
440fc6c796fSJohn Snow        # unsuccessful drive1 target. Then re-run the same transaction.
441fc6c796fSJohn Snow        dr0bm0.del_target()
442fc6c796fSJohn Snow        dr1bm0.del_target()
443fc6c796fSJohn Snow        target0 = self.prepare_backup(dr0bm0)
444fc6c796fSJohn Snow        target1 = self.prepare_backup(dr1bm0)
445fc6c796fSJohn Snow
446fc6c796fSJohn Snow        # Re-run the exact same transaction.
447fc6c796fSJohn Snow        result = self.vm.qmp('transaction', actions=transaction,
448fc6c796fSJohn Snow                             properties={'completion-mode':'grouped'})
449fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
450fc6c796fSJohn Snow
451fc6c796fSJohn Snow        # Both should complete successfully this time.
452fc6c796fSJohn Snow        self.assertTrue(self.wait_qmp_backup(drive0['id']))
453fc6c796fSJohn Snow        self.assertTrue(self.wait_qmp_backup(drive1['id']))
454fc6c796fSJohn Snow        self.make_reference_backup(dr0bm0)
455fc6c796fSJohn Snow        self.make_reference_backup(dr1bm0)
456fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
457fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
458fc6c796fSJohn Snow
459fc6c796fSJohn Snow        # And the images should of course validate.
460fc6c796fSJohn Snow        self.vm.shutdown()
461fc6c796fSJohn Snow        self.check_backups()
462fc6c796fSJohn Snow
463fc6c796fSJohn Snow
4649f7264f5SJohn Snow    def test_sync_dirty_bitmap_missing(self):
4659f7264f5SJohn Snow        self.assert_no_active_block_jobs()
4669f7264f5SJohn Snow        self.files.append(self.err_img)
4679f7264f5SJohn Snow        result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
4684b80ab2bSJohn Snow                             sync='incremental', format=self.drives[0]['fmt'],
4699f7264f5SJohn Snow                             target=self.err_img)
4709f7264f5SJohn Snow        self.assert_qmp(result, 'error/class', 'GenericError')
4719f7264f5SJohn Snow
4729f7264f5SJohn Snow
4739f7264f5SJohn Snow    def test_sync_dirty_bitmap_not_found(self):
4749f7264f5SJohn Snow        self.assert_no_active_block_jobs()
4759f7264f5SJohn Snow        self.files.append(self.err_img)
4769f7264f5SJohn Snow        result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
4774b80ab2bSJohn Snow                             sync='incremental', bitmap='unknown',
4789f7264f5SJohn Snow                             format=self.drives[0]['fmt'], target=self.err_img)
4799f7264f5SJohn Snow        self.assert_qmp(result, 'error/class', 'GenericError')
4809f7264f5SJohn Snow
4819f7264f5SJohn Snow
48259fc5d84SJohn Snow    def test_sync_dirty_bitmap_bad_granularity(self):
48359fc5d84SJohn Snow        '''
48459fc5d84SJohn Snow        Test: Test what happens if we provide an improper granularity.
48559fc5d84SJohn Snow
48659fc5d84SJohn Snow        The granularity must always be a power of 2.
48759fc5d84SJohn Snow        '''
48859fc5d84SJohn Snow        self.assert_no_active_block_jobs()
48959fc5d84SJohn Snow        self.assertRaises(AssertionError, self.add_bitmap,
49059fc5d84SJohn Snow                          'bitmap0', self.drives[0],
49159fc5d84SJohn Snow                          granularity=64000)
49259fc5d84SJohn Snow
49359fc5d84SJohn Snow
494*ce2cbc49SJohn Snowclass TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
495*ce2cbc49SJohn Snow    '''Incremental backup tests that utilize a BlkDebug filter on drive0.'''
496*ce2cbc49SJohn Snow
497*ce2cbc49SJohn Snow    def test_incremental_failure(self):
498*ce2cbc49SJohn Snow        '''Test: Verify backups made after a failure are correct.
499*ce2cbc49SJohn Snow
500*ce2cbc49SJohn Snow        Simulate a failure during an incremental backup block job,
501*ce2cbc49SJohn Snow        emulate additional writes, then create another incremental backup
502*ce2cbc49SJohn Snow        afterwards and verify that the backup created is correct.
503*ce2cbc49SJohn Snow        '''
504*ce2cbc49SJohn Snow
505*ce2cbc49SJohn Snow        # Create a blkdebug interface to this img as 'drive1',
506*ce2cbc49SJohn Snow        # but don't actually create a new image.
507*ce2cbc49SJohn Snow        drive1 = self.add_node('drive1', self.drives[0]['fmt'],
508*ce2cbc49SJohn Snow                               path=self.drives[0]['file'],
509*ce2cbc49SJohn Snow                               backup=self.drives[0]['backup'])
510*ce2cbc49SJohn Snow        result = self.vm.qmp('blockdev-add', options={
511*ce2cbc49SJohn Snow            'id': drive1['id'],
512*ce2cbc49SJohn Snow            'driver': drive1['fmt'],
513*ce2cbc49SJohn Snow            'file': {
514*ce2cbc49SJohn Snow                'driver': 'blkdebug',
515*ce2cbc49SJohn Snow                'image': {
516*ce2cbc49SJohn Snow                    'driver': 'file',
517*ce2cbc49SJohn Snow                    'filename': drive1['file']
518*ce2cbc49SJohn Snow                },
519*ce2cbc49SJohn Snow                'set-state': [{
520*ce2cbc49SJohn Snow                    'event': 'flush_to_disk',
521*ce2cbc49SJohn Snow                    'state': 1,
522*ce2cbc49SJohn Snow                    'new_state': 2
523*ce2cbc49SJohn Snow                }],
524*ce2cbc49SJohn Snow                'inject-error': [{
525*ce2cbc49SJohn Snow                    'event': 'read_aio',
526*ce2cbc49SJohn Snow                    'errno': 5,
527*ce2cbc49SJohn Snow                    'state': 2,
528*ce2cbc49SJohn Snow                    'immediately': False,
529*ce2cbc49SJohn Snow                    'once': True
530*ce2cbc49SJohn Snow                }],
531*ce2cbc49SJohn Snow            }
532*ce2cbc49SJohn Snow        })
533*ce2cbc49SJohn Snow        self.assert_qmp(result, 'return', {})
534*ce2cbc49SJohn Snow
535*ce2cbc49SJohn Snow        self.create_anchor_backup(self.drives[0])
536*ce2cbc49SJohn Snow        self.add_bitmap('bitmap0', drive1)
537*ce2cbc49SJohn Snow        # Note: at this point, during a normal execution,
538*ce2cbc49SJohn Snow        # Assume that the VM resumes and begins issuing IO requests here.
539*ce2cbc49SJohn Snow
540*ce2cbc49SJohn Snow        self.hmp_io_writes(drive1['id'], (('0xab', 0, 512),
541*ce2cbc49SJohn Snow                                          ('0xfe', '16M', '256k'),
542*ce2cbc49SJohn Snow                                          ('0x64', '32736k', '64k')))
543*ce2cbc49SJohn Snow
544*ce2cbc49SJohn Snow        result = self.create_incremental(validate=False)
545*ce2cbc49SJohn Snow        self.assertFalse(result)
546*ce2cbc49SJohn Snow        self.hmp_io_writes(drive1['id'], (('0x9a', 0, 512),
547*ce2cbc49SJohn Snow                                          ('0x55', '8M', '352k'),
548*ce2cbc49SJohn Snow                                          ('0x78', '15872k', '1M')))
549*ce2cbc49SJohn Snow        self.create_incremental()
550*ce2cbc49SJohn Snow        self.vm.shutdown()
551*ce2cbc49SJohn Snow        self.check_backups()
552*ce2cbc49SJohn Snow
553*ce2cbc49SJohn Snow
5549f7264f5SJohn Snowif __name__ == '__main__':
5559f7264f5SJohn Snow    iotests.main(supported_fmts=['qcow2'])
556