xref: /qemu/tests/qemu-iotests/124 (revision 9dd003a9)
1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3
2*9dd003a9SVladimir Sementsov-Ogievskiy# group: rw backing
39f7264f5SJohn Snow#
49f7264f5SJohn Snow# Tests for incremental drive-backup
59f7264f5SJohn Snow#
69f7264f5SJohn Snow# Copyright (C) 2015 John Snow for Red Hat, Inc.
79f7264f5SJohn Snow#
89f7264f5SJohn Snow# Based on 056.
99f7264f5SJohn Snow#
109f7264f5SJohn Snow# This program is free software; you can redistribute it and/or modify
119f7264f5SJohn Snow# it under the terms of the GNU General Public License as published by
129f7264f5SJohn Snow# the Free Software Foundation; either version 2 of the License, or
139f7264f5SJohn Snow# (at your option) any later version.
149f7264f5SJohn Snow#
159f7264f5SJohn Snow# This program is distributed in the hope that it will be useful,
169f7264f5SJohn Snow# but WITHOUT ANY WARRANTY; without even the implied warranty of
179f7264f5SJohn Snow# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
189f7264f5SJohn Snow# GNU General Public License for more details.
199f7264f5SJohn Snow#
209f7264f5SJohn Snow# You should have received a copy of the GNU General Public License
219f7264f5SJohn Snow# along with this program.  If not, see <http://www.gnu.org/licenses/>.
229f7264f5SJohn Snow#
239f7264f5SJohn Snow
249f7264f5SJohn Snowimport os
259f7264f5SJohn Snowimport iotests
269f7264f5SJohn Snow
279f7264f5SJohn Snow
289f7264f5SJohn Snowdef io_write_patterns(img, patterns):
299f7264f5SJohn Snow    for pattern in patterns:
309f7264f5SJohn Snow        iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
319f7264f5SJohn Snow
329f7264f5SJohn Snow
33a3d71595SJohn Snowdef try_remove(img):
34a3d71595SJohn Snow    try:
35a3d71595SJohn Snow        os.remove(img)
36a3d71595SJohn Snow    except OSError:
37a3d71595SJohn Snow        pass
38a3d71595SJohn Snow
39a3d71595SJohn Snow
40749ad5e8SJohn Snowdef transaction_action(action, **kwargs):
41749ad5e8SJohn Snow    return {
42749ad5e8SJohn Snow        'type': action,
4368474776SMax Reitz        'data': dict((k.replace('_', '-'), v) for k, v in kwargs.items())
44749ad5e8SJohn Snow    }
45749ad5e8SJohn Snow
46749ad5e8SJohn Snow
47749ad5e8SJohn Snowdef transaction_bitmap_clear(node, name, **kwargs):
48749ad5e8SJohn Snow    return transaction_action('block-dirty-bitmap-clear',
49749ad5e8SJohn Snow                              node=node, name=name, **kwargs)
50749ad5e8SJohn Snow
51749ad5e8SJohn Snow
52749ad5e8SJohn Snowdef transaction_drive_backup(device, target, **kwargs):
53eed87583SKevin Wolf    return transaction_action('drive-backup', job_id=device, device=device,
54eed87583SKevin Wolf                              target=target, **kwargs)
55749ad5e8SJohn Snow
56749ad5e8SJohn Snow
57a3d71595SJohn Snowclass Bitmap:
58a3d71595SJohn Snow    def __init__(self, name, drive):
59a3d71595SJohn Snow        self.name = name
60a3d71595SJohn Snow        self.drive = drive
61a3d71595SJohn Snow        self.num = 0
62a3d71595SJohn Snow        self.backups = list()
63a3d71595SJohn Snow
64a3d71595SJohn Snow    def base_target(self):
65a3d71595SJohn Snow        return (self.drive['backup'], None)
66a3d71595SJohn Snow
67a3d71595SJohn Snow    def new_target(self, num=None):
68a3d71595SJohn Snow        if num is None:
69a3d71595SJohn Snow            num = self.num
70a3d71595SJohn Snow        self.num = num + 1
71a3d71595SJohn Snow        base = os.path.join(iotests.test_dir,
72a3d71595SJohn Snow                            "%s.%s." % (self.drive['id'], self.name))
73a3d71595SJohn Snow        suff = "%i.%s" % (num, self.drive['fmt'])
74a3d71595SJohn Snow        target = base + "inc" + suff
75a3d71595SJohn Snow        reference = base + "ref" + suff
76a3d71595SJohn Snow        self.backups.append((target, reference))
77a3d71595SJohn Snow        return (target, reference)
78a3d71595SJohn Snow
79a3d71595SJohn Snow    def last_target(self):
80a3d71595SJohn Snow        if self.backups:
81a3d71595SJohn Snow            return self.backups[-1]
82a3d71595SJohn Snow        return self.base_target()
83a3d71595SJohn Snow
84a3d71595SJohn Snow    def del_target(self):
85a3d71595SJohn Snow        for image in self.backups.pop():
86a3d71595SJohn Snow            try_remove(image)
87a3d71595SJohn Snow        self.num -= 1
88a3d71595SJohn Snow
89a3d71595SJohn Snow    def cleanup(self):
90a3d71595SJohn Snow        for backup in self.backups:
91a3d71595SJohn Snow            for image in backup:
92a3d71595SJohn Snow                try_remove(image)
93a3d71595SJohn Snow
94a3d71595SJohn Snow
951b19bb9dSJohn Snowclass TestIncrementalBackupBase(iotests.QMPTestCase):
961b19bb9dSJohn Snow    def __init__(self, *args):
971b19bb9dSJohn Snow        super(TestIncrementalBackupBase, self).__init__(*args)
989f7264f5SJohn Snow        self.bitmaps = list()
999f7264f5SJohn Snow        self.files = list()
1009f7264f5SJohn Snow        self.drives = list()
1019f7264f5SJohn Snow        self.vm = iotests.VM()
1029f7264f5SJohn Snow        self.err_img = os.path.join(iotests.test_dir, 'err.%s' % iotests.imgfmt)
1039f7264f5SJohn Snow
1041b19bb9dSJohn Snow
1051b19bb9dSJohn Snow    def setUp(self):
1069f7264f5SJohn Snow        # Create a base image with a distinctive patterning
1079f7264f5SJohn Snow        drive0 = self.add_node('drive0')
1089f7264f5SJohn Snow        self.img_create(drive0['file'], drive0['fmt'])
1095c4343b8SVladimir Sementsov-Ogievskiy        self.vm.add_drive(drive0['file'], opts='node-name=node0')
1101b19bb9dSJohn Snow        self.write_default_pattern(drive0['file'])
1111b19bb9dSJohn Snow        self.vm.launch()
1121b19bb9dSJohn Snow
1131b19bb9dSJohn Snow
1141b19bb9dSJohn Snow    def write_default_pattern(self, target):
1151b19bb9dSJohn Snow        io_write_patterns(target, (('0x41', 0, 512),
1169f7264f5SJohn Snow                                   ('0xd5', '1M', '32k'),
1179f7264f5SJohn Snow                                   ('0xdc', '32M', '124k')))
1189f7264f5SJohn Snow
1199f7264f5SJohn Snow
1209f7264f5SJohn Snow    def add_node(self, node_id, fmt=iotests.imgfmt, path=None, backup=None):
1219f7264f5SJohn Snow        if path is None:
1229f7264f5SJohn Snow            path = os.path.join(iotests.test_dir, '%s.%s' % (node_id, fmt))
1239f7264f5SJohn Snow        if backup is None:
1249f7264f5SJohn Snow            backup = os.path.join(iotests.test_dir,
1259f7264f5SJohn Snow                                  '%s.full.backup.%s' % (node_id, fmt))
1269f7264f5SJohn Snow
1279f7264f5SJohn Snow        self.drives.append({
1289f7264f5SJohn Snow            'id': node_id,
1299f7264f5SJohn Snow            'file': path,
1309f7264f5SJohn Snow            'backup': backup,
1319f7264f5SJohn Snow            'fmt': fmt })
1329f7264f5SJohn Snow        return self.drives[-1]
1339f7264f5SJohn Snow
1349f7264f5SJohn Snow
1359f7264f5SJohn Snow    def img_create(self, img, fmt=iotests.imgfmt, size='64M',
136cc199b16SJohn Snow                   parent=None, parentFormat=None, **kwargs):
137cc199b16SJohn Snow        optargs = []
13868474776SMax Reitz        for k,v in kwargs.items():
139cc199b16SJohn Snow            optargs = optargs + ['-o', '%s=%s' % (k,v)]
140cc199b16SJohn Snow        args = ['create', '-f', fmt] + optargs + [img, size]
1419f7264f5SJohn Snow        if parent:
1429f7264f5SJohn Snow            if parentFormat is None:
1439f7264f5SJohn Snow                parentFormat = fmt
144cc199b16SJohn Snow            args = args + ['-b', parent, '-F', parentFormat]
145cc199b16SJohn Snow        iotests.qemu_img(*args)
1469f7264f5SJohn Snow        self.files.append(img)
1479f7264f5SJohn Snow
148a3d71595SJohn Snow
149a3d71595SJohn Snow    def do_qmp_backup(self, error='Input/output error', **kwargs):
150a3d71595SJohn Snow        res = self.vm.qmp('drive-backup', **kwargs)
151a3d71595SJohn Snow        self.assert_qmp(res, 'return', {})
152fc6c796fSJohn Snow        return self.wait_qmp_backup(kwargs['device'], error)
153a3d71595SJohn Snow
154fc6c796fSJohn Snow
1551dac83f1SKevin Wolf    def ignore_job_status_change_events(self):
1561dac83f1SKevin Wolf        while True:
1571dac83f1SKevin Wolf            e = self.vm.event_wait(name="JOB_STATUS_CHANGE")
1581dac83f1SKevin Wolf            if e['data']['status'] == 'null':
1591dac83f1SKevin Wolf                break
1601dac83f1SKevin Wolf
161fc6c796fSJohn Snow    def wait_qmp_backup(self, device, error='Input/output error'):
162a3d71595SJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
163fc6c796fSJohn Snow                                   match={'data': {'device': device}})
164ff793890SJohn Snow        self.assertNotEqual(event, None)
1651dac83f1SKevin Wolf        self.ignore_job_status_change_events()
166a3d71595SJohn Snow
167a3d71595SJohn Snow        try:
168a3d71595SJohn Snow            failure = self.dictpath(event, 'data/error')
169a3d71595SJohn Snow        except AssertionError:
170a3d71595SJohn Snow            # Backup succeeded.
171a3d71595SJohn Snow            self.assert_qmp(event, 'data/offset', event['data']['len'])
172a3d71595SJohn Snow            return True
173a3d71595SJohn Snow        else:
174a3d71595SJohn Snow            # Backup failed.
175a3d71595SJohn Snow            self.assert_qmp(event, 'data/error', error)
176a3d71595SJohn Snow            return False
177a3d71595SJohn Snow
178a3d71595SJohn Snow
179fc6c796fSJohn Snow    def wait_qmp_backup_cancelled(self, device):
180fc6c796fSJohn Snow        event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED',
181fc6c796fSJohn Snow                                   match={'data': {'device': device}})
182fc6c796fSJohn Snow        self.assertNotEqual(event, None)
1831dac83f1SKevin Wolf        self.ignore_job_status_change_events()
184fc6c796fSJohn Snow
185fc6c796fSJohn Snow
186a3d71595SJohn Snow    def create_anchor_backup(self, drive=None):
187a3d71595SJohn Snow        if drive is None:
188a3d71595SJohn Snow            drive = self.drives[-1]
189eed87583SKevin Wolf        res = self.do_qmp_backup(job_id=drive['id'],
190eed87583SKevin Wolf                                 device=drive['id'], sync='full',
191a3d71595SJohn Snow                                 format=drive['fmt'], target=drive['backup'])
192a3d71595SJohn Snow        self.assertTrue(res)
193a3d71595SJohn Snow        self.files.append(drive['backup'])
194a3d71595SJohn Snow        return drive['backup']
195a3d71595SJohn Snow
196a3d71595SJohn Snow
197a3d71595SJohn Snow    def make_reference_backup(self, bitmap=None):
198a3d71595SJohn Snow        if bitmap is None:
199a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
200a3d71595SJohn Snow        _, reference = bitmap.last_target()
201eed87583SKevin Wolf        res = self.do_qmp_backup(job_id=bitmap.drive['id'],
202eed87583SKevin Wolf                                 device=bitmap.drive['id'], sync='full',
203a3d71595SJohn Snow                                 format=bitmap.drive['fmt'], target=reference)
204a3d71595SJohn Snow        self.assertTrue(res)
205a3d71595SJohn Snow
206a3d71595SJohn Snow
20759fc5d84SJohn Snow    def add_bitmap(self, name, drive, **kwargs):
208a3d71595SJohn Snow        bitmap = Bitmap(name, drive)
209a3d71595SJohn Snow        self.bitmaps.append(bitmap)
210a3d71595SJohn Snow        result = self.vm.qmp('block-dirty-bitmap-add', node=drive['id'],
21159fc5d84SJohn Snow                             name=bitmap.name, **kwargs)
212a3d71595SJohn Snow        self.assert_qmp(result, 'return', {})
213a3d71595SJohn Snow        return bitmap
214a3d71595SJohn Snow
215a3d71595SJohn Snow
2168a9cb864SMax Reitz    def prepare_backup(self, bitmap=None, parent=None, **kwargs):
217a3d71595SJohn Snow        if bitmap is None:
218a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
219a3d71595SJohn Snow        if parent is None:
220a3d71595SJohn Snow            parent, _ = bitmap.last_target()
221a3d71595SJohn Snow
222a3d71595SJohn Snow        target, _ = bitmap.new_target()
2238a9cb864SMax Reitz        self.img_create(target, bitmap.drive['fmt'], parent=parent,
2248a9cb864SMax Reitz                        **kwargs)
225a3d71595SJohn Snow        return target
226a3d71595SJohn Snow
227a3d71595SJohn Snow
228a3d71595SJohn Snow    def create_incremental(self, bitmap=None, parent=None,
2298a9cb864SMax Reitz                           parentFormat=None, validate=True,
2308a9cb864SMax Reitz                           target=None):
231a3d71595SJohn Snow        if bitmap is None:
232a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
233a3d71595SJohn Snow        if parent is None:
234a3d71595SJohn Snow            parent, _ = bitmap.last_target()
235a3d71595SJohn Snow
2368a9cb864SMax Reitz        if target is None:
237a3d71595SJohn Snow            target = self.prepare_backup(bitmap, parent)
238eed87583SKevin Wolf        res = self.do_qmp_backup(job_id=bitmap.drive['id'],
239eed87583SKevin Wolf                                 device=bitmap.drive['id'],
2404b80ab2bSJohn Snow                                 sync='incremental', bitmap=bitmap.name,
241a3d71595SJohn Snow                                 format=bitmap.drive['fmt'], target=target,
242a3d71595SJohn Snow                                 mode='existing')
243a3d71595SJohn Snow        if not res:
244a3d71595SJohn Snow            bitmap.del_target();
245a3d71595SJohn Snow            self.assertFalse(validate)
246a3d71595SJohn Snow        else:
247a3d71595SJohn Snow            self.make_reference_backup(bitmap)
248a3d71595SJohn Snow        return res
249a3d71595SJohn Snow
250a3d71595SJohn Snow
251a3d71595SJohn Snow    def check_backups(self):
252a3d71595SJohn Snow        for bitmap in self.bitmaps:
253a3d71595SJohn Snow            for incremental, reference in bitmap.backups:
254a3d71595SJohn Snow                self.assertTrue(iotests.compare_images(incremental, reference))
255a3d71595SJohn Snow            last = bitmap.last_target()[0]
256a3d71595SJohn Snow            self.assertTrue(iotests.compare_images(last, bitmap.drive['file']))
257a3d71595SJohn Snow
258a3d71595SJohn Snow
259a3d71595SJohn Snow    def hmp_io_writes(self, drive, patterns):
260a3d71595SJohn Snow        for pattern in patterns:
261a3d71595SJohn Snow            self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
262a3d71595SJohn Snow        self.vm.hmp_qemu_io(drive, 'flush')
263a3d71595SJohn Snow
264a3d71595SJohn Snow
26559fc5d84SJohn Snow    def do_incremental_simple(self, **kwargs):
266a3d71595SJohn Snow        self.create_anchor_backup()
26759fc5d84SJohn Snow        self.add_bitmap('bitmap0', self.drives[0], **kwargs)
268a3d71595SJohn Snow
269a3d71595SJohn Snow        # Sanity: Create a "hollow" incremental backup
270a3d71595SJohn Snow        self.create_incremental()
271a3d71595SJohn Snow        # Three writes: One complete overwrite, one new segment,
272a3d71595SJohn Snow        # and one partial overlap.
273a3d71595SJohn Snow        self.hmp_io_writes(self.drives[0]['id'], (('0xab', 0, 512),
274a3d71595SJohn Snow                                                  ('0xfe', '16M', '256k'),
275a3d71595SJohn Snow                                                  ('0x64', '32736k', '64k')))
276a3d71595SJohn Snow        self.create_incremental()
277a3d71595SJohn Snow        # Three more writes, one of each kind, like above
278a3d71595SJohn Snow        self.hmp_io_writes(self.drives[0]['id'], (('0x9a', 0, 512),
279a3d71595SJohn Snow                                                  ('0x55', '8M', '352k'),
280a3d71595SJohn Snow                                                  ('0x78', '15872k', '1M')))
281a3d71595SJohn Snow        self.create_incremental()
282a3d71595SJohn Snow        self.vm.shutdown()
283a3d71595SJohn Snow        self.check_backups()
284a3d71595SJohn Snow
285a3d71595SJohn Snow
2861b19bb9dSJohn Snow    def tearDown(self):
2871b19bb9dSJohn Snow        self.vm.shutdown()
2881b19bb9dSJohn Snow        for bitmap in self.bitmaps:
2891b19bb9dSJohn Snow            bitmap.cleanup()
2901b19bb9dSJohn Snow        for filename in self.files:
2911b19bb9dSJohn Snow            try_remove(filename)
2921b19bb9dSJohn Snow
2931b19bb9dSJohn Snow
2941b19bb9dSJohn Snow
2951b19bb9dSJohn Snowclass TestIncrementalBackup(TestIncrementalBackupBase):
29659fc5d84SJohn Snow    def test_incremental_simple(self):
29759fc5d84SJohn Snow        '''
29859fc5d84SJohn Snow        Test: Create and verify three incremental backups.
29959fc5d84SJohn Snow
30059fc5d84SJohn Snow        Create a bitmap and a full backup before VM execution begins,
30159fc5d84SJohn Snow        then create a series of three incremental backups "during execution,"
30259fc5d84SJohn Snow        i.e.; after IO requests begin modifying the drive.
30359fc5d84SJohn Snow        '''
30459fc5d84SJohn Snow        return self.do_incremental_simple()
30559fc5d84SJohn Snow
30659fc5d84SJohn Snow
30759fc5d84SJohn Snow    def test_small_granularity(self):
30859fc5d84SJohn Snow        '''
30959fc5d84SJohn Snow        Test: Create and verify backups made with a small granularity bitmap.
31059fc5d84SJohn Snow
31159fc5d84SJohn Snow        Perform the same test as test_incremental_simple, but with a granularity
31259fc5d84SJohn Snow        of only 32KiB instead of the present default of 64KiB.
31359fc5d84SJohn Snow        '''
31459fc5d84SJohn Snow        return self.do_incremental_simple(granularity=32768)
31559fc5d84SJohn Snow
31659fc5d84SJohn Snow
31759fc5d84SJohn Snow    def test_large_granularity(self):
31859fc5d84SJohn Snow        '''
31959fc5d84SJohn Snow        Test: Create and verify backups made with a large granularity bitmap.
32059fc5d84SJohn Snow
32159fc5d84SJohn Snow        Perform the same test as test_incremental_simple, but with a granularity
32259fc5d84SJohn Snow        of 128KiB instead of the present default of 64KiB.
32359fc5d84SJohn Snow        '''
32459fc5d84SJohn Snow        return self.do_incremental_simple(granularity=131072)
32559fc5d84SJohn Snow
32659fc5d84SJohn Snow
327cc199b16SJohn Snow    def test_larger_cluster_target(self):
328cc199b16SJohn Snow        '''
329cc199b16SJohn Snow        Test: Create and verify backups made to a larger cluster size target.
330cc199b16SJohn Snow
331cc199b16SJohn Snow        With a default granularity of 64KiB, verify that backups made to a
332cc199b16SJohn Snow        larger cluster size target of 128KiB without a backing file works.
333cc199b16SJohn Snow        '''
334cc199b16SJohn Snow        drive0 = self.drives[0]
335cc199b16SJohn Snow
336cc199b16SJohn Snow        # Create a cluster_size=128k full backup / "anchor" backup
337cc199b16SJohn Snow        self.img_create(drive0['backup'], cluster_size='128k')
338cc199b16SJohn Snow        self.assertTrue(self.do_qmp_backup(device=drive0['id'], sync='full',
339cc199b16SJohn Snow                                           format=drive0['fmt'],
340cc199b16SJohn Snow                                           target=drive0['backup'],
341cc199b16SJohn Snow                                           mode='existing'))
342cc199b16SJohn Snow
343cc199b16SJohn Snow        # Create bitmap and dirty it with some new writes.
344cc199b16SJohn Snow        # overwrite [32736, 32799] which will dirty bitmap clusters at
345cc199b16SJohn Snow        # 32M-64K and 32M. 32M+64K will be left undirtied.
346cc199b16SJohn Snow        bitmap0 = self.add_bitmap('bitmap0', drive0)
347cc199b16SJohn Snow        self.hmp_io_writes(drive0['id'],
348cc199b16SJohn Snow                           (('0xab', 0, 512),
349cc199b16SJohn Snow                            ('0xfe', '16M', '256k'),
350cc199b16SJohn Snow                            ('0x64', '32736k', '64k')))
3511e2b1f64SEric Blake        # Check the dirty bitmap stats
3525c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
3535c4343b8SVladimir Sementsov-Ogievskiy            'node0', bitmap0.name, {
3545c4343b8SVladimir Sementsov-Ogievskiy                'name': 'bitmap0',
3555c4343b8SVladimir Sementsov-Ogievskiy                'count': 458752,
3565c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
3575c4343b8SVladimir Sementsov-Ogievskiy                'status': 'active',
3585c4343b8SVladimir Sementsov-Ogievskiy                'persistent': False
3595c4343b8SVladimir Sementsov-Ogievskiy            }))
360cc199b16SJohn Snow
361cc199b16SJohn Snow        # Prepare a cluster_size=128k backup target without a backing file.
362cc199b16SJohn Snow        (target, _) = bitmap0.new_target()
363cc199b16SJohn Snow        self.img_create(target, bitmap0.drive['fmt'], cluster_size='128k')
364cc199b16SJohn Snow
365cc199b16SJohn Snow        # Perform Incremental Backup
366cc199b16SJohn Snow        self.assertTrue(self.do_qmp_backup(device=bitmap0.drive['id'],
367cc199b16SJohn Snow                                           sync='incremental',
368cc199b16SJohn Snow                                           bitmap=bitmap0.name,
369cc199b16SJohn Snow                                           format=bitmap0.drive['fmt'],
370cc199b16SJohn Snow                                           target=target,
371cc199b16SJohn Snow                                           mode='existing'))
372cc199b16SJohn Snow        self.make_reference_backup(bitmap0)
373cc199b16SJohn Snow
374cc199b16SJohn Snow        # Add the backing file, then compare and exit.
375cc199b16SJohn Snow        iotests.qemu_img('rebase', '-f', drive0['fmt'], '-u', '-b',
376cc199b16SJohn Snow                         drive0['backup'], '-F', drive0['fmt'], target)
377cc199b16SJohn Snow        self.vm.shutdown()
378cc199b16SJohn Snow        self.check_backups()
379cc199b16SJohn Snow
380cc199b16SJohn Snow
381749ad5e8SJohn Snow    def test_incremental_transaction(self):
382749ad5e8SJohn Snow        '''Test: Verify backups made from transactionally created bitmaps.
383749ad5e8SJohn Snow
384749ad5e8SJohn Snow        Create a bitmap "before" VM execution begins, then create a second
385749ad5e8SJohn Snow        bitmap AFTER writes have already occurred. Use transactions to create
386749ad5e8SJohn Snow        a full backup and synchronize both bitmaps to this backup.
387749ad5e8SJohn Snow        Create an incremental backup through both bitmaps and verify that
388749ad5e8SJohn Snow        both backups match the current drive0 image.
389749ad5e8SJohn Snow        '''
390749ad5e8SJohn Snow
391749ad5e8SJohn Snow        drive0 = self.drives[0]
392749ad5e8SJohn Snow        bitmap0 = self.add_bitmap('bitmap0', drive0)
393749ad5e8SJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
394749ad5e8SJohn Snow                                          ('0xfe', '16M', '256k'),
395749ad5e8SJohn Snow                                          ('0x64', '32736k', '64k')))
396749ad5e8SJohn Snow        bitmap1 = self.add_bitmap('bitmap1', drive0)
397749ad5e8SJohn Snow
398749ad5e8SJohn Snow        result = self.vm.qmp('transaction', actions=[
399749ad5e8SJohn Snow            transaction_bitmap_clear(bitmap0.drive['id'], bitmap0.name),
400749ad5e8SJohn Snow            transaction_bitmap_clear(bitmap1.drive['id'], bitmap1.name),
401749ad5e8SJohn Snow            transaction_drive_backup(drive0['id'], drive0['backup'],
402749ad5e8SJohn Snow                                     sync='full', format=drive0['fmt'])
403749ad5e8SJohn Snow        ])
404749ad5e8SJohn Snow        self.assert_qmp(result, 'return', {})
405749ad5e8SJohn Snow        self.wait_until_completed(drive0['id'])
406749ad5e8SJohn Snow        self.files.append(drive0['backup'])
407749ad5e8SJohn Snow
408749ad5e8SJohn Snow        self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512),
409749ad5e8SJohn Snow                                          ('0x55', '8M', '352k'),
410749ad5e8SJohn Snow                                          ('0x78', '15872k', '1M')))
411749ad5e8SJohn Snow        # Both bitmaps should be correctly in sync.
412749ad5e8SJohn Snow        self.create_incremental(bitmap0)
413749ad5e8SJohn Snow        self.create_incremental(bitmap1)
414749ad5e8SJohn Snow        self.vm.shutdown()
415749ad5e8SJohn Snow        self.check_backups()
416749ad5e8SJohn Snow
417749ad5e8SJohn Snow
4180aef09b9SJohn Snow    def do_transaction_failure_test(self, race=False):
419fc6c796fSJohn Snow        # Create a second drive, with pattern:
420fc6c796fSJohn Snow        drive1 = self.add_node('drive1')
421fc6c796fSJohn Snow        self.img_create(drive1['file'], drive1['fmt'])
422fc6c796fSJohn Snow        io_write_patterns(drive1['file'], (('0x14', 0, 512),
423fc6c796fSJohn Snow                                           ('0x5d', '1M', '32k'),
424fc6c796fSJohn Snow                                           ('0xcd', '32M', '124k')))
425fc6c796fSJohn Snow
426fc6c796fSJohn Snow        # Create a blkdebug interface to this img as 'drive1'
4270153d2f5SKevin Wolf        result = self.vm.qmp('blockdev-add',
4280153d2f5SKevin Wolf            node_name=drive1['id'],
4290153d2f5SKevin Wolf            driver=drive1['fmt'],
4300153d2f5SKevin Wolf            file={
431fc6c796fSJohn Snow                'driver': 'blkdebug',
432fc6c796fSJohn Snow                'image': {
433fc6c796fSJohn Snow                    'driver': 'file',
434fc6c796fSJohn Snow                    'filename': drive1['file']
435fc6c796fSJohn Snow                },
436fc6c796fSJohn Snow                'set-state': [{
437fc6c796fSJohn Snow                    'event': 'flush_to_disk',
438fc6c796fSJohn Snow                    'state': 1,
439fc6c796fSJohn Snow                    'new_state': 2
440fc6c796fSJohn Snow                }],
441fc6c796fSJohn Snow                'inject-error': [{
442fc6c796fSJohn Snow                    'event': 'read_aio',
443fc6c796fSJohn Snow                    'errno': 5,
444fc6c796fSJohn Snow                    'state': 2,
445fc6c796fSJohn Snow                    'immediately': False,
446fc6c796fSJohn Snow                    'once': True
447fc6c796fSJohn Snow                }],
448fc6c796fSJohn Snow            }
4490153d2f5SKevin Wolf        )
450fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
451fc6c796fSJohn Snow
452fc6c796fSJohn Snow        # Create bitmaps and full backups for both drives
453fc6c796fSJohn Snow        drive0 = self.drives[0]
454fc6c796fSJohn Snow        dr0bm0 = self.add_bitmap('bitmap0', drive0)
455fc6c796fSJohn Snow        dr1bm0 = self.add_bitmap('bitmap0', drive1)
456fc6c796fSJohn Snow        self.create_anchor_backup(drive0)
457fc6c796fSJohn Snow        self.create_anchor_backup(drive1)
458fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
459fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
460fc6c796fSJohn Snow
461fc6c796fSJohn Snow        # Emulate some writes
4620aef09b9SJohn Snow        if not race:
463fc6c796fSJohn Snow            self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
464fc6c796fSJohn Snow                                              ('0xfe', '16M', '256k'),
465fc6c796fSJohn Snow                                              ('0x64', '32736k', '64k')))
466fc6c796fSJohn Snow        self.hmp_io_writes(drive1['id'], (('0xba', 0, 512),
467fc6c796fSJohn Snow                                          ('0xef', '16M', '256k'),
468fc6c796fSJohn Snow                                          ('0x46', '32736k', '64k')))
469fc6c796fSJohn Snow
470fc6c796fSJohn Snow        # Create incremental backup targets
471fc6c796fSJohn Snow        target0 = self.prepare_backup(dr0bm0)
472fc6c796fSJohn Snow        target1 = self.prepare_backup(dr1bm0)
473fc6c796fSJohn Snow
474fc6c796fSJohn Snow        # Ask for a new incremental backup per-each drive,
4750aef09b9SJohn Snow        # expecting drive1's backup to fail. In the 'race' test,
4760aef09b9SJohn Snow        # we expect drive1 to attempt to cancel the empty drive0 job.
477fc6c796fSJohn Snow        transaction = [
478fc6c796fSJohn Snow            transaction_drive_backup(drive0['id'], target0, sync='incremental',
479fc6c796fSJohn Snow                                     format=drive0['fmt'], mode='existing',
480fc6c796fSJohn Snow                                     bitmap=dr0bm0.name),
481fc6c796fSJohn Snow            transaction_drive_backup(drive1['id'], target1, sync='incremental',
482fc6c796fSJohn Snow                                     format=drive1['fmt'], mode='existing',
483fc6c796fSJohn Snow                                     bitmap=dr1bm0.name)
484fc6c796fSJohn Snow        ]
485fc6c796fSJohn Snow        result = self.vm.qmp('transaction', actions=transaction,
486fc6c796fSJohn Snow                             properties={'completion-mode': 'grouped'} )
487fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
488fc6c796fSJohn Snow
489fc6c796fSJohn Snow        # Observe that drive0's backup is cancelled and drive1 completes with
490fc6c796fSJohn Snow        # an error.
491fc6c796fSJohn Snow        self.wait_qmp_backup_cancelled(drive0['id'])
492fc6c796fSJohn Snow        self.assertFalse(self.wait_qmp_backup(drive1['id']))
493fc6c796fSJohn Snow        error = self.vm.event_wait('BLOCK_JOB_ERROR')
494fc6c796fSJohn Snow        self.assert_qmp(error, 'data', {'device': drive1['id'],
495fc6c796fSJohn Snow                                        'action': 'report',
496fc6c796fSJohn Snow                                        'operation': 'read'})
497fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
498fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
499fc6c796fSJohn Snow
500fc6c796fSJohn Snow        # Delete drive0's successful target and eliminate our record of the
5010aef09b9SJohn Snow        # unsuccessful drive1 target.
502fc6c796fSJohn Snow        dr0bm0.del_target()
503fc6c796fSJohn Snow        dr1bm0.del_target()
5040aef09b9SJohn Snow        if race:
5050aef09b9SJohn Snow            # Don't re-run the transaction, we only wanted to test the race.
5060aef09b9SJohn Snow            self.vm.shutdown()
5070aef09b9SJohn Snow            return
5080aef09b9SJohn Snow
5090aef09b9SJohn Snow        # Re-run the same transaction:
510fc6c796fSJohn Snow        target0 = self.prepare_backup(dr0bm0)
511fc6c796fSJohn Snow        target1 = self.prepare_backup(dr1bm0)
512fc6c796fSJohn Snow
513fc6c796fSJohn Snow        # Re-run the exact same transaction.
514fc6c796fSJohn Snow        result = self.vm.qmp('transaction', actions=transaction,
515fc6c796fSJohn Snow                             properties={'completion-mode':'grouped'})
516fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
517fc6c796fSJohn Snow
518fc6c796fSJohn Snow        # Both should complete successfully this time.
519fc6c796fSJohn Snow        self.assertTrue(self.wait_qmp_backup(drive0['id']))
520fc6c796fSJohn Snow        self.assertTrue(self.wait_qmp_backup(drive1['id']))
521fc6c796fSJohn Snow        self.make_reference_backup(dr0bm0)
522fc6c796fSJohn Snow        self.make_reference_backup(dr1bm0)
523fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
524fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
525fc6c796fSJohn Snow
526fc6c796fSJohn Snow        # And the images should of course validate.
527fc6c796fSJohn Snow        self.vm.shutdown()
528fc6c796fSJohn Snow        self.check_backups()
529fc6c796fSJohn Snow
5300aef09b9SJohn Snow    def test_transaction_failure(self):
5310aef09b9SJohn Snow        '''Test: Verify backups made from a transaction that partially fails.
5320aef09b9SJohn Snow
5330aef09b9SJohn Snow        Add a second drive with its own unique pattern, and add a bitmap to each
5340aef09b9SJohn Snow        drive. Use blkdebug to interfere with the backup on just one drive and
5350aef09b9SJohn Snow        attempt to create a coherent incremental backup across both drives.
5360aef09b9SJohn Snow
5370aef09b9SJohn Snow        verify a failure in one but not both, then delete the failed stubs and
5380aef09b9SJohn Snow        re-run the same transaction.
5390aef09b9SJohn Snow
5400aef09b9SJohn Snow        verify that both incrementals are created successfully.
5410aef09b9SJohn Snow        '''
5420aef09b9SJohn Snow        self.do_transaction_failure_test()
5430aef09b9SJohn Snow
5440aef09b9SJohn Snow    def test_transaction_failure_race(self):
5450aef09b9SJohn Snow        '''Test: Verify that transactions with jobs that have no data to
5460aef09b9SJohn Snow        transfer do not cause race conditions in the cancellation of the entire
5470aef09b9SJohn Snow        transaction job group.
5480aef09b9SJohn Snow        '''
5490aef09b9SJohn Snow        self.do_transaction_failure_test(race=True)
5500aef09b9SJohn Snow
551fc6c796fSJohn Snow
5529f7264f5SJohn Snow    def test_sync_dirty_bitmap_missing(self):
5539f7264f5SJohn Snow        self.assert_no_active_block_jobs()
5549f7264f5SJohn Snow        self.files.append(self.err_img)
5559f7264f5SJohn Snow        result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
5564b80ab2bSJohn Snow                             sync='incremental', format=self.drives[0]['fmt'],
5579f7264f5SJohn Snow                             target=self.err_img)
5589f7264f5SJohn Snow        self.assert_qmp(result, 'error/class', 'GenericError')
5599f7264f5SJohn Snow
5609f7264f5SJohn Snow
5619f7264f5SJohn Snow    def test_sync_dirty_bitmap_not_found(self):
5629f7264f5SJohn Snow        self.assert_no_active_block_jobs()
5639f7264f5SJohn Snow        self.files.append(self.err_img)
5649f7264f5SJohn Snow        result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
5654b80ab2bSJohn Snow                             sync='incremental', bitmap='unknown',
5669f7264f5SJohn Snow                             format=self.drives[0]['fmt'], target=self.err_img)
5679f7264f5SJohn Snow        self.assert_qmp(result, 'error/class', 'GenericError')
5689f7264f5SJohn Snow
5699f7264f5SJohn Snow
57059fc5d84SJohn Snow    def test_sync_dirty_bitmap_bad_granularity(self):
57159fc5d84SJohn Snow        '''
57259fc5d84SJohn Snow        Test: Test what happens if we provide an improper granularity.
57359fc5d84SJohn Snow
57459fc5d84SJohn Snow        The granularity must always be a power of 2.
57559fc5d84SJohn Snow        '''
57659fc5d84SJohn Snow        self.assert_no_active_block_jobs()
57759fc5d84SJohn Snow        self.assertRaises(AssertionError, self.add_bitmap,
57859fc5d84SJohn Snow                          'bitmap0', self.drives[0],
57959fc5d84SJohn Snow                          granularity=64000)
58059fc5d84SJohn Snow
5818a9cb864SMax Reitz    def test_growing_before_backup(self):
5828a9cb864SMax Reitz        '''
5838a9cb864SMax Reitz        Test: Add a bitmap, truncate the image, write past the old
5848a9cb864SMax Reitz              end, do a backup.
5858a9cb864SMax Reitz
5868a9cb864SMax Reitz        Incremental backup should not ignore dirty bits past the old
5878a9cb864SMax Reitz        image end.
5888a9cb864SMax Reitz        '''
5898a9cb864SMax Reitz        self.assert_no_active_block_jobs()
5908a9cb864SMax Reitz
5918a9cb864SMax Reitz        self.create_anchor_backup()
5928a9cb864SMax Reitz
5938a9cb864SMax Reitz        self.add_bitmap('bitmap0', self.drives[0])
5948a9cb864SMax Reitz
5958a9cb864SMax Reitz        res = self.vm.qmp('block_resize', device=self.drives[0]['id'],
5968a9cb864SMax Reitz                          size=(65 * 1048576))
5978a9cb864SMax Reitz        self.assert_qmp(res, 'return', {})
5988a9cb864SMax Reitz
5998a9cb864SMax Reitz        # Dirty the image past the old end
6008a9cb864SMax Reitz        self.vm.hmp_qemu_io(self.drives[0]['id'], 'write 64M 64k')
6018a9cb864SMax Reitz
6028a9cb864SMax Reitz        target = self.prepare_backup(size='65M')
6038a9cb864SMax Reitz        self.create_incremental(target=target)
6048a9cb864SMax Reitz
6058a9cb864SMax Reitz        self.vm.shutdown()
6068a9cb864SMax Reitz        self.check_backups()
6078a9cb864SMax Reitz
60859fc5d84SJohn Snow
609ce2cbc49SJohn Snowclass TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
610ce2cbc49SJohn Snow    '''Incremental backup tests that utilize a BlkDebug filter on drive0.'''
611ce2cbc49SJohn Snow
61235cea223SJohn Snow    def setUp(self):
61335cea223SJohn Snow        drive0 = self.add_node('drive0')
61435cea223SJohn Snow        self.img_create(drive0['file'], drive0['fmt'])
61535cea223SJohn Snow        self.write_default_pattern(drive0['file'])
61635cea223SJohn Snow        self.vm.launch()
61735cea223SJohn Snow
618ce2cbc49SJohn Snow    def test_incremental_failure(self):
619ce2cbc49SJohn Snow        '''Test: Verify backups made after a failure are correct.
620ce2cbc49SJohn Snow
621ce2cbc49SJohn Snow        Simulate a failure during an incremental backup block job,
622ce2cbc49SJohn Snow        emulate additional writes, then create another incremental backup
623ce2cbc49SJohn Snow        afterwards and verify that the backup created is correct.
624ce2cbc49SJohn Snow        '''
625ce2cbc49SJohn Snow
62635cea223SJohn Snow        drive0 = self.drives[0]
6270153d2f5SKevin Wolf        result = self.vm.qmp('blockdev-add',
6280153d2f5SKevin Wolf            node_name=drive0['id'],
6290153d2f5SKevin Wolf            driver=drive0['fmt'],
6300153d2f5SKevin Wolf            file={
631ce2cbc49SJohn Snow                'driver': 'blkdebug',
632ce2cbc49SJohn Snow                'image': {
633ce2cbc49SJohn Snow                    'driver': 'file',
63435cea223SJohn Snow                    'filename': drive0['file']
635ce2cbc49SJohn Snow                },
636ce2cbc49SJohn Snow                'set-state': [{
637ce2cbc49SJohn Snow                    'event': 'flush_to_disk',
638ce2cbc49SJohn Snow                    'state': 1,
639ce2cbc49SJohn Snow                    'new_state': 2
640ce2cbc49SJohn Snow                }],
641ce2cbc49SJohn Snow                'inject-error': [{
642ce2cbc49SJohn Snow                    'event': 'read_aio',
643ce2cbc49SJohn Snow                    'errno': 5,
644ce2cbc49SJohn Snow                    'state': 2,
645ce2cbc49SJohn Snow                    'immediately': False,
646ce2cbc49SJohn Snow                    'once': True
647ce2cbc49SJohn Snow                }],
648ce2cbc49SJohn Snow            }
6490153d2f5SKevin Wolf        )
650ce2cbc49SJohn Snow        self.assert_qmp(result, 'return', {})
651ce2cbc49SJohn Snow
65235cea223SJohn Snow        self.create_anchor_backup(drive0)
65335cea223SJohn Snow        self.add_bitmap('bitmap0', drive0)
654ce2cbc49SJohn Snow        # Note: at this point, during a normal execution,
655ce2cbc49SJohn Snow        # Assume that the VM resumes and begins issuing IO requests here.
656ce2cbc49SJohn Snow
65735cea223SJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
658ce2cbc49SJohn Snow                                          ('0xfe', '16M', '256k'),
659ce2cbc49SJohn Snow                                          ('0x64', '32736k', '64k')))
660ce2cbc49SJohn Snow
661ce2cbc49SJohn Snow        result = self.create_incremental(validate=False)
662ce2cbc49SJohn Snow        self.assertFalse(result)
66335cea223SJohn Snow        self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512),
664ce2cbc49SJohn Snow                                          ('0x55', '8M', '352k'),
665ce2cbc49SJohn Snow                                          ('0x78', '15872k', '1M')))
666ce2cbc49SJohn Snow        self.create_incremental()
667ce2cbc49SJohn Snow        self.vm.shutdown()
668ce2cbc49SJohn Snow        self.check_backups()
669ce2cbc49SJohn Snow
670c61b198bSJohn Snow    def test_incremental_pause(self):
671c61b198bSJohn Snow        """
672c61b198bSJohn Snow        Test an incremental backup that errors into a pause and is resumed.
673c61b198bSJohn Snow        """
674c61b198bSJohn Snow
675c61b198bSJohn Snow        drive0 = self.drives[0]
6765c4343b8SVladimir Sementsov-Ogievskiy        # NB: The blkdebug script here looks for a "flush, read" pattern.
6775c4343b8SVladimir Sementsov-Ogievskiy        # The flush occurs in hmp_io_writes, and the read during the block job.
678c61b198bSJohn Snow        result = self.vm.qmp('blockdev-add',
679c61b198bSJohn Snow                             node_name=drive0['id'],
680c61b198bSJohn Snow                             driver=drive0['fmt'],
681c61b198bSJohn Snow                             file={
682c61b198bSJohn Snow                                 'driver': 'blkdebug',
683c61b198bSJohn Snow                                 'image': {
684c61b198bSJohn Snow                                     'driver': 'file',
685c61b198bSJohn Snow                                     'filename': drive0['file']
686c61b198bSJohn Snow                                 },
687c61b198bSJohn Snow                                 'set-state': [{
688c61b198bSJohn Snow                                     'event': 'flush_to_disk',
689c61b198bSJohn Snow                                     'state': 1,
690c61b198bSJohn Snow                                     'new_state': 2
691c61b198bSJohn Snow                                 }],
692c61b198bSJohn Snow                                 'inject-error': [{
693c61b198bSJohn Snow                                     'event': 'read_aio',
694c61b198bSJohn Snow                                     'errno': 5,
6955c4343b8SVladimir Sementsov-Ogievskiy                                     'state': 2,
696c61b198bSJohn Snow                                     'immediately': False,
697c61b198bSJohn Snow                                     'once': True
698c61b198bSJohn Snow                                 }],
699c61b198bSJohn Snow                             })
700c61b198bSJohn Snow        self.assert_qmp(result, 'return', {})
701c61b198bSJohn Snow        self.create_anchor_backup(drive0)
702c61b198bSJohn Snow        bitmap = self.add_bitmap('bitmap0', drive0)
703c61b198bSJohn Snow
704c61b198bSJohn Snow        # Emulate guest activity
705c61b198bSJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
706c61b198bSJohn Snow                                          ('0xfe', '16M', '256k'),
707c61b198bSJohn Snow                                          ('0x64', '32736k', '64k')))
708c61b198bSJohn Snow
709c61b198bSJohn Snow        # Bitmap Status Check
7105c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
7115c4343b8SVladimir Sementsov-Ogievskiy            drive0['id'], bitmap.name, {
7125c4343b8SVladimir Sementsov-Ogievskiy                'count': 458752,
7135c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
7145c4343b8SVladimir Sementsov-Ogievskiy                'status': 'active',
7155c4343b8SVladimir Sementsov-Ogievskiy                'busy': False,
7165c4343b8SVladimir Sementsov-Ogievskiy                'recording': True
7175c4343b8SVladimir Sementsov-Ogievskiy            }))
718c61b198bSJohn Snow
719c61b198bSJohn Snow        # Start backup
720c61b198bSJohn Snow        parent, _ = bitmap.last_target()
721c61b198bSJohn Snow        target = self.prepare_backup(bitmap, parent)
722c61b198bSJohn Snow        res = self.vm.qmp('drive-backup',
723c61b198bSJohn Snow                          job_id=bitmap.drive['id'],
724c61b198bSJohn Snow                          device=bitmap.drive['id'],
725c61b198bSJohn Snow                          sync='incremental',
726c61b198bSJohn Snow                          bitmap=bitmap.name,
727c61b198bSJohn Snow                          format=bitmap.drive['fmt'],
728c61b198bSJohn Snow                          target=target,
729c61b198bSJohn Snow                          mode='existing',
730c61b198bSJohn Snow                          on_source_error='stop')
731c61b198bSJohn Snow        self.assert_qmp(res, 'return', {})
732c61b198bSJohn Snow
733c61b198bSJohn Snow        # Wait for the error
734c61b198bSJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
735c61b198bSJohn Snow                                   match={"data":{"device":bitmap.drive['id']}})
736c61b198bSJohn Snow        self.assert_qmp(event, 'data', {'device': bitmap.drive['id'],
737c61b198bSJohn Snow                                        'action': 'stop',
738c61b198bSJohn Snow                                        'operation': 'read'})
739c61b198bSJohn Snow
740c61b198bSJohn Snow        # Bitmap Status Check
7415c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
7425c4343b8SVladimir Sementsov-Ogievskiy            drive0['id'], bitmap.name, {
7435c4343b8SVladimir Sementsov-Ogievskiy                'count': 458752,
7445c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
7455c4343b8SVladimir Sementsov-Ogievskiy                'status': 'frozen',
7465c4343b8SVladimir Sementsov-Ogievskiy                'busy': True,
7475c4343b8SVladimir Sementsov-Ogievskiy                'recording': True
7485c4343b8SVladimir Sementsov-Ogievskiy            }))
749c61b198bSJohn Snow
750c61b198bSJohn Snow        # Resume and check incremental backup for consistency
751c61b198bSJohn Snow        res = self.vm.qmp('block-job-resume', device=bitmap.drive['id'])
752c61b198bSJohn Snow        self.assert_qmp(res, 'return', {})
753c61b198bSJohn Snow        self.wait_qmp_backup(bitmap.drive['id'])
754c61b198bSJohn Snow
755c61b198bSJohn Snow        # Bitmap Status Check
7565c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
7575c4343b8SVladimir Sementsov-Ogievskiy            drive0['id'], bitmap.name, {
7585c4343b8SVladimir Sementsov-Ogievskiy                'count': 0,
7595c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
7605c4343b8SVladimir Sementsov-Ogievskiy                'status': 'active',
7615c4343b8SVladimir Sementsov-Ogievskiy                'busy': False,
7625c4343b8SVladimir Sementsov-Ogievskiy                'recording': True
7635c4343b8SVladimir Sementsov-Ogievskiy            }))
764c61b198bSJohn Snow
765c61b198bSJohn Snow        # Finalize / Cleanup
766c61b198bSJohn Snow        self.make_reference_backup(bitmap)
767c61b198bSJohn Snow        self.vm.shutdown()
768c61b198bSJohn Snow        self.check_backups()
769c61b198bSJohn Snow
770ce2cbc49SJohn Snow
7719f7264f5SJohn Snowif __name__ == '__main__':
772103cbc77SMax Reitz    iotests.main(supported_fmts=['qcow2'],
773103cbc77SMax Reitz                 supported_protocols=['file'])
774