xref: /openbmc/qemu/tests/qemu-iotests/124 (revision c5ff5a3caa16f18bea05db4e2326e9162e7a0ba9)
1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3
29dd003a9SVladimir 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
26*c5ff5a3cSMax Reitzfrom iotests import try_remove
279f7264f5SJohn Snow
289f7264f5SJohn Snow
299f7264f5SJohn Snowdef io_write_patterns(img, patterns):
309f7264f5SJohn Snow    for pattern in patterns:
319f7264f5SJohn Snow        iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
329f7264f5SJohn Snow
339f7264f5SJohn Snow
34749ad5e8SJohn Snowdef transaction_action(action, **kwargs):
35749ad5e8SJohn Snow    return {
36749ad5e8SJohn Snow        'type': action,
3768474776SMax Reitz        'data': dict((k.replace('_', '-'), v) for k, v in kwargs.items())
38749ad5e8SJohn Snow    }
39749ad5e8SJohn Snow
40749ad5e8SJohn Snow
41749ad5e8SJohn Snowdef transaction_bitmap_clear(node, name, **kwargs):
42749ad5e8SJohn Snow    return transaction_action('block-dirty-bitmap-clear',
43749ad5e8SJohn Snow                              node=node, name=name, **kwargs)
44749ad5e8SJohn Snow
45749ad5e8SJohn Snow
46749ad5e8SJohn Snowdef transaction_drive_backup(device, target, **kwargs):
47eed87583SKevin Wolf    return transaction_action('drive-backup', job_id=device, device=device,
48eed87583SKevin Wolf                              target=target, **kwargs)
49749ad5e8SJohn Snow
50749ad5e8SJohn Snow
51a3d71595SJohn Snowclass Bitmap:
52a3d71595SJohn Snow    def __init__(self, name, drive):
53a3d71595SJohn Snow        self.name = name
54a3d71595SJohn Snow        self.drive = drive
55a3d71595SJohn Snow        self.num = 0
56a3d71595SJohn Snow        self.backups = list()
57a3d71595SJohn Snow
58a3d71595SJohn Snow    def base_target(self):
59a3d71595SJohn Snow        return (self.drive['backup'], None)
60a3d71595SJohn Snow
61a3d71595SJohn Snow    def new_target(self, num=None):
62a3d71595SJohn Snow        if num is None:
63a3d71595SJohn Snow            num = self.num
64a3d71595SJohn Snow        self.num = num + 1
65a3d71595SJohn Snow        base = os.path.join(iotests.test_dir,
66a3d71595SJohn Snow                            "%s.%s." % (self.drive['id'], self.name))
67a3d71595SJohn Snow        suff = "%i.%s" % (num, self.drive['fmt'])
68a3d71595SJohn Snow        target = base + "inc" + suff
69a3d71595SJohn Snow        reference = base + "ref" + suff
70a3d71595SJohn Snow        self.backups.append((target, reference))
71a3d71595SJohn Snow        return (target, reference)
72a3d71595SJohn Snow
73a3d71595SJohn Snow    def last_target(self):
74a3d71595SJohn Snow        if self.backups:
75a3d71595SJohn Snow            return self.backups[-1]
76a3d71595SJohn Snow        return self.base_target()
77a3d71595SJohn Snow
78a3d71595SJohn Snow    def del_target(self):
79a3d71595SJohn Snow        for image in self.backups.pop():
80a3d71595SJohn Snow            try_remove(image)
81a3d71595SJohn Snow        self.num -= 1
82a3d71595SJohn Snow
83a3d71595SJohn Snow    def cleanup(self):
84a3d71595SJohn Snow        for backup in self.backups:
85a3d71595SJohn Snow            for image in backup:
86a3d71595SJohn Snow                try_remove(image)
87a3d71595SJohn Snow
88a3d71595SJohn Snow
891b19bb9dSJohn Snowclass TestIncrementalBackupBase(iotests.QMPTestCase):
901b19bb9dSJohn Snow    def __init__(self, *args):
911b19bb9dSJohn Snow        super(TestIncrementalBackupBase, self).__init__(*args)
929f7264f5SJohn Snow        self.bitmaps = list()
939f7264f5SJohn Snow        self.files = list()
949f7264f5SJohn Snow        self.drives = list()
959f7264f5SJohn Snow        self.vm = iotests.VM()
969f7264f5SJohn Snow        self.err_img = os.path.join(iotests.test_dir, 'err.%s' % iotests.imgfmt)
979f7264f5SJohn Snow
981b19bb9dSJohn Snow
991b19bb9dSJohn Snow    def setUp(self):
1009f7264f5SJohn Snow        # Create a base image with a distinctive patterning
1019f7264f5SJohn Snow        drive0 = self.add_node('drive0')
1029f7264f5SJohn Snow        self.img_create(drive0['file'], drive0['fmt'])
1035c4343b8SVladimir Sementsov-Ogievskiy        self.vm.add_drive(drive0['file'], opts='node-name=node0')
1041b19bb9dSJohn Snow        self.write_default_pattern(drive0['file'])
1051b19bb9dSJohn Snow        self.vm.launch()
1061b19bb9dSJohn Snow
1071b19bb9dSJohn Snow
1081b19bb9dSJohn Snow    def write_default_pattern(self, target):
1091b19bb9dSJohn Snow        io_write_patterns(target, (('0x41', 0, 512),
1109f7264f5SJohn Snow                                   ('0xd5', '1M', '32k'),
1119f7264f5SJohn Snow                                   ('0xdc', '32M', '124k')))
1129f7264f5SJohn Snow
1139f7264f5SJohn Snow
1149f7264f5SJohn Snow    def add_node(self, node_id, fmt=iotests.imgfmt, path=None, backup=None):
1159f7264f5SJohn Snow        if path is None:
1169f7264f5SJohn Snow            path = os.path.join(iotests.test_dir, '%s.%s' % (node_id, fmt))
1179f7264f5SJohn Snow        if backup is None:
1189f7264f5SJohn Snow            backup = os.path.join(iotests.test_dir,
1199f7264f5SJohn Snow                                  '%s.full.backup.%s' % (node_id, fmt))
1209f7264f5SJohn Snow
1219f7264f5SJohn Snow        self.drives.append({
1229f7264f5SJohn Snow            'id': node_id,
1239f7264f5SJohn Snow            'file': path,
1249f7264f5SJohn Snow            'backup': backup,
1259f7264f5SJohn Snow            'fmt': fmt })
1269f7264f5SJohn Snow        return self.drives[-1]
1279f7264f5SJohn Snow
1289f7264f5SJohn Snow
1299f7264f5SJohn Snow    def img_create(self, img, fmt=iotests.imgfmt, size='64M',
130cc199b16SJohn Snow                   parent=None, parentFormat=None, **kwargs):
131cc199b16SJohn Snow        optargs = []
13268474776SMax Reitz        for k,v in kwargs.items():
133cc199b16SJohn Snow            optargs = optargs + ['-o', '%s=%s' % (k,v)]
134cc199b16SJohn Snow        args = ['create', '-f', fmt] + optargs + [img, size]
1359f7264f5SJohn Snow        if parent:
1369f7264f5SJohn Snow            if parentFormat is None:
1379f7264f5SJohn Snow                parentFormat = fmt
138cc199b16SJohn Snow            args = args + ['-b', parent, '-F', parentFormat]
139cc199b16SJohn Snow        iotests.qemu_img(*args)
1409f7264f5SJohn Snow        self.files.append(img)
1419f7264f5SJohn Snow
142a3d71595SJohn Snow
143a3d71595SJohn Snow    def do_qmp_backup(self, error='Input/output error', **kwargs):
144a3d71595SJohn Snow        res = self.vm.qmp('drive-backup', **kwargs)
145a3d71595SJohn Snow        self.assert_qmp(res, 'return', {})
146fc6c796fSJohn Snow        return self.wait_qmp_backup(kwargs['device'], error)
147a3d71595SJohn Snow
148fc6c796fSJohn Snow
1491dac83f1SKevin Wolf    def ignore_job_status_change_events(self):
1501dac83f1SKevin Wolf        while True:
1511dac83f1SKevin Wolf            e = self.vm.event_wait(name="JOB_STATUS_CHANGE")
1521dac83f1SKevin Wolf            if e['data']['status'] == 'null':
1531dac83f1SKevin Wolf                break
1541dac83f1SKevin Wolf
155fc6c796fSJohn Snow    def wait_qmp_backup(self, device, error='Input/output error'):
156a3d71595SJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
157fc6c796fSJohn Snow                                   match={'data': {'device': device}})
158ff793890SJohn Snow        self.assertNotEqual(event, None)
1591dac83f1SKevin Wolf        self.ignore_job_status_change_events()
160a3d71595SJohn Snow
161a3d71595SJohn Snow        try:
162a3d71595SJohn Snow            failure = self.dictpath(event, 'data/error')
163a3d71595SJohn Snow        except AssertionError:
164a3d71595SJohn Snow            # Backup succeeded.
165a3d71595SJohn Snow            self.assert_qmp(event, 'data/offset', event['data']['len'])
166a3d71595SJohn Snow            return True
167a3d71595SJohn Snow        else:
168a3d71595SJohn Snow            # Backup failed.
169a3d71595SJohn Snow            self.assert_qmp(event, 'data/error', error)
170a3d71595SJohn Snow            return False
171a3d71595SJohn Snow
172a3d71595SJohn Snow
173fc6c796fSJohn Snow    def wait_qmp_backup_cancelled(self, device):
174fc6c796fSJohn Snow        event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED',
175fc6c796fSJohn Snow                                   match={'data': {'device': device}})
176fc6c796fSJohn Snow        self.assertNotEqual(event, None)
1771dac83f1SKevin Wolf        self.ignore_job_status_change_events()
178fc6c796fSJohn Snow
179fc6c796fSJohn Snow
180a3d71595SJohn Snow    def create_anchor_backup(self, drive=None):
181a3d71595SJohn Snow        if drive is None:
182a3d71595SJohn Snow            drive = self.drives[-1]
183eed87583SKevin Wolf        res = self.do_qmp_backup(job_id=drive['id'],
184eed87583SKevin Wolf                                 device=drive['id'], sync='full',
185a3d71595SJohn Snow                                 format=drive['fmt'], target=drive['backup'])
186a3d71595SJohn Snow        self.assertTrue(res)
187a3d71595SJohn Snow        self.files.append(drive['backup'])
188a3d71595SJohn Snow        return drive['backup']
189a3d71595SJohn Snow
190a3d71595SJohn Snow
191a3d71595SJohn Snow    def make_reference_backup(self, bitmap=None):
192a3d71595SJohn Snow        if bitmap is None:
193a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
194a3d71595SJohn Snow        _, reference = bitmap.last_target()
195eed87583SKevin Wolf        res = self.do_qmp_backup(job_id=bitmap.drive['id'],
196eed87583SKevin Wolf                                 device=bitmap.drive['id'], sync='full',
197a3d71595SJohn Snow                                 format=bitmap.drive['fmt'], target=reference)
198a3d71595SJohn Snow        self.assertTrue(res)
199a3d71595SJohn Snow
200a3d71595SJohn Snow
20159fc5d84SJohn Snow    def add_bitmap(self, name, drive, **kwargs):
202a3d71595SJohn Snow        bitmap = Bitmap(name, drive)
203a3d71595SJohn Snow        self.bitmaps.append(bitmap)
204a3d71595SJohn Snow        result = self.vm.qmp('block-dirty-bitmap-add', node=drive['id'],
20559fc5d84SJohn Snow                             name=bitmap.name, **kwargs)
206a3d71595SJohn Snow        self.assert_qmp(result, 'return', {})
207a3d71595SJohn Snow        return bitmap
208a3d71595SJohn Snow
209a3d71595SJohn Snow
2108a9cb864SMax Reitz    def prepare_backup(self, bitmap=None, parent=None, **kwargs):
211a3d71595SJohn Snow        if bitmap is None:
212a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
213a3d71595SJohn Snow        if parent is None:
214a3d71595SJohn Snow            parent, _ = bitmap.last_target()
215a3d71595SJohn Snow
216a3d71595SJohn Snow        target, _ = bitmap.new_target()
2178a9cb864SMax Reitz        self.img_create(target, bitmap.drive['fmt'], parent=parent,
2188a9cb864SMax Reitz                        **kwargs)
219a3d71595SJohn Snow        return target
220a3d71595SJohn Snow
221a3d71595SJohn Snow
222a3d71595SJohn Snow    def create_incremental(self, bitmap=None, parent=None,
2238a9cb864SMax Reitz                           parentFormat=None, validate=True,
2248a9cb864SMax Reitz                           target=None):
225a3d71595SJohn Snow        if bitmap is None:
226a3d71595SJohn Snow            bitmap = self.bitmaps[-1]
227a3d71595SJohn Snow        if parent is None:
228a3d71595SJohn Snow            parent, _ = bitmap.last_target()
229a3d71595SJohn Snow
2308a9cb864SMax Reitz        if target is None:
231a3d71595SJohn Snow            target = self.prepare_backup(bitmap, parent)
232eed87583SKevin Wolf        res = self.do_qmp_backup(job_id=bitmap.drive['id'],
233eed87583SKevin Wolf                                 device=bitmap.drive['id'],
2344b80ab2bSJohn Snow                                 sync='incremental', bitmap=bitmap.name,
235a3d71595SJohn Snow                                 format=bitmap.drive['fmt'], target=target,
236a3d71595SJohn Snow                                 mode='existing')
237a3d71595SJohn Snow        if not res:
238a3d71595SJohn Snow            bitmap.del_target();
239a3d71595SJohn Snow            self.assertFalse(validate)
240a3d71595SJohn Snow        else:
241a3d71595SJohn Snow            self.make_reference_backup(bitmap)
242a3d71595SJohn Snow        return res
243a3d71595SJohn Snow
244a3d71595SJohn Snow
245a3d71595SJohn Snow    def check_backups(self):
246a3d71595SJohn Snow        for bitmap in self.bitmaps:
247a3d71595SJohn Snow            for incremental, reference in bitmap.backups:
248a3d71595SJohn Snow                self.assertTrue(iotests.compare_images(incremental, reference))
249a3d71595SJohn Snow            last = bitmap.last_target()[0]
250a3d71595SJohn Snow            self.assertTrue(iotests.compare_images(last, bitmap.drive['file']))
251a3d71595SJohn Snow
252a3d71595SJohn Snow
253a3d71595SJohn Snow    def hmp_io_writes(self, drive, patterns):
254a3d71595SJohn Snow        for pattern in patterns:
255a3d71595SJohn Snow            self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
256a3d71595SJohn Snow        self.vm.hmp_qemu_io(drive, 'flush')
257a3d71595SJohn Snow
258a3d71595SJohn Snow
25959fc5d84SJohn Snow    def do_incremental_simple(self, **kwargs):
260a3d71595SJohn Snow        self.create_anchor_backup()
26159fc5d84SJohn Snow        self.add_bitmap('bitmap0', self.drives[0], **kwargs)
262a3d71595SJohn Snow
263a3d71595SJohn Snow        # Sanity: Create a "hollow" incremental backup
264a3d71595SJohn Snow        self.create_incremental()
265a3d71595SJohn Snow        # Three writes: One complete overwrite, one new segment,
266a3d71595SJohn Snow        # and one partial overlap.
267a3d71595SJohn Snow        self.hmp_io_writes(self.drives[0]['id'], (('0xab', 0, 512),
268a3d71595SJohn Snow                                                  ('0xfe', '16M', '256k'),
269a3d71595SJohn Snow                                                  ('0x64', '32736k', '64k')))
270a3d71595SJohn Snow        self.create_incremental()
271a3d71595SJohn Snow        # Three more writes, one of each kind, like above
272a3d71595SJohn Snow        self.hmp_io_writes(self.drives[0]['id'], (('0x9a', 0, 512),
273a3d71595SJohn Snow                                                  ('0x55', '8M', '352k'),
274a3d71595SJohn Snow                                                  ('0x78', '15872k', '1M')))
275a3d71595SJohn Snow        self.create_incremental()
276a3d71595SJohn Snow        self.vm.shutdown()
277a3d71595SJohn Snow        self.check_backups()
278a3d71595SJohn Snow
279a3d71595SJohn Snow
2801b19bb9dSJohn Snow    def tearDown(self):
2811b19bb9dSJohn Snow        self.vm.shutdown()
2821b19bb9dSJohn Snow        for bitmap in self.bitmaps:
2831b19bb9dSJohn Snow            bitmap.cleanup()
2841b19bb9dSJohn Snow        for filename in self.files:
2851b19bb9dSJohn Snow            try_remove(filename)
2861b19bb9dSJohn Snow
2871b19bb9dSJohn Snow
2881b19bb9dSJohn Snow
2891b19bb9dSJohn Snowclass TestIncrementalBackup(TestIncrementalBackupBase):
29059fc5d84SJohn Snow    def test_incremental_simple(self):
29159fc5d84SJohn Snow        '''
29259fc5d84SJohn Snow        Test: Create and verify three incremental backups.
29359fc5d84SJohn Snow
29459fc5d84SJohn Snow        Create a bitmap and a full backup before VM execution begins,
29559fc5d84SJohn Snow        then create a series of three incremental backups "during execution,"
29659fc5d84SJohn Snow        i.e.; after IO requests begin modifying the drive.
29759fc5d84SJohn Snow        '''
29859fc5d84SJohn Snow        return self.do_incremental_simple()
29959fc5d84SJohn Snow
30059fc5d84SJohn Snow
30159fc5d84SJohn Snow    def test_small_granularity(self):
30259fc5d84SJohn Snow        '''
30359fc5d84SJohn Snow        Test: Create and verify backups made with a small granularity bitmap.
30459fc5d84SJohn Snow
30559fc5d84SJohn Snow        Perform the same test as test_incremental_simple, but with a granularity
30659fc5d84SJohn Snow        of only 32KiB instead of the present default of 64KiB.
30759fc5d84SJohn Snow        '''
30859fc5d84SJohn Snow        return self.do_incremental_simple(granularity=32768)
30959fc5d84SJohn Snow
31059fc5d84SJohn Snow
31159fc5d84SJohn Snow    def test_large_granularity(self):
31259fc5d84SJohn Snow        '''
31359fc5d84SJohn Snow        Test: Create and verify backups made with a large granularity bitmap.
31459fc5d84SJohn Snow
31559fc5d84SJohn Snow        Perform the same test as test_incremental_simple, but with a granularity
31659fc5d84SJohn Snow        of 128KiB instead of the present default of 64KiB.
31759fc5d84SJohn Snow        '''
31859fc5d84SJohn Snow        return self.do_incremental_simple(granularity=131072)
31959fc5d84SJohn Snow
32059fc5d84SJohn Snow
321cc199b16SJohn Snow    def test_larger_cluster_target(self):
322cc199b16SJohn Snow        '''
323cc199b16SJohn Snow        Test: Create and verify backups made to a larger cluster size target.
324cc199b16SJohn Snow
325cc199b16SJohn Snow        With a default granularity of 64KiB, verify that backups made to a
326cc199b16SJohn Snow        larger cluster size target of 128KiB without a backing file works.
327cc199b16SJohn Snow        '''
328cc199b16SJohn Snow        drive0 = self.drives[0]
329cc199b16SJohn Snow
330cc199b16SJohn Snow        # Create a cluster_size=128k full backup / "anchor" backup
331cc199b16SJohn Snow        self.img_create(drive0['backup'], cluster_size='128k')
332cc199b16SJohn Snow        self.assertTrue(self.do_qmp_backup(device=drive0['id'], sync='full',
333cc199b16SJohn Snow                                           format=drive0['fmt'],
334cc199b16SJohn Snow                                           target=drive0['backup'],
335cc199b16SJohn Snow                                           mode='existing'))
336cc199b16SJohn Snow
337cc199b16SJohn Snow        # Create bitmap and dirty it with some new writes.
338cc199b16SJohn Snow        # overwrite [32736, 32799] which will dirty bitmap clusters at
339cc199b16SJohn Snow        # 32M-64K and 32M. 32M+64K will be left undirtied.
340cc199b16SJohn Snow        bitmap0 = self.add_bitmap('bitmap0', drive0)
341cc199b16SJohn Snow        self.hmp_io_writes(drive0['id'],
342cc199b16SJohn Snow                           (('0xab', 0, 512),
343cc199b16SJohn Snow                            ('0xfe', '16M', '256k'),
344cc199b16SJohn Snow                            ('0x64', '32736k', '64k')))
3451e2b1f64SEric Blake        # Check the dirty bitmap stats
3465c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
3475c4343b8SVladimir Sementsov-Ogievskiy            'node0', bitmap0.name, {
3485c4343b8SVladimir Sementsov-Ogievskiy                'name': 'bitmap0',
3495c4343b8SVladimir Sementsov-Ogievskiy                'count': 458752,
3505c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
3515c4343b8SVladimir Sementsov-Ogievskiy                'status': 'active',
3525c4343b8SVladimir Sementsov-Ogievskiy                'persistent': False
3535c4343b8SVladimir Sementsov-Ogievskiy            }))
354cc199b16SJohn Snow
355cc199b16SJohn Snow        # Prepare a cluster_size=128k backup target without a backing file.
356cc199b16SJohn Snow        (target, _) = bitmap0.new_target()
357cc199b16SJohn Snow        self.img_create(target, bitmap0.drive['fmt'], cluster_size='128k')
358cc199b16SJohn Snow
359cc199b16SJohn Snow        # Perform Incremental Backup
360cc199b16SJohn Snow        self.assertTrue(self.do_qmp_backup(device=bitmap0.drive['id'],
361cc199b16SJohn Snow                                           sync='incremental',
362cc199b16SJohn Snow                                           bitmap=bitmap0.name,
363cc199b16SJohn Snow                                           format=bitmap0.drive['fmt'],
364cc199b16SJohn Snow                                           target=target,
365cc199b16SJohn Snow                                           mode='existing'))
366cc199b16SJohn Snow        self.make_reference_backup(bitmap0)
367cc199b16SJohn Snow
368cc199b16SJohn Snow        # Add the backing file, then compare and exit.
369cc199b16SJohn Snow        iotests.qemu_img('rebase', '-f', drive0['fmt'], '-u', '-b',
370cc199b16SJohn Snow                         drive0['backup'], '-F', drive0['fmt'], target)
371cc199b16SJohn Snow        self.vm.shutdown()
372cc199b16SJohn Snow        self.check_backups()
373cc199b16SJohn Snow
374cc199b16SJohn Snow
375749ad5e8SJohn Snow    def test_incremental_transaction(self):
376749ad5e8SJohn Snow        '''Test: Verify backups made from transactionally created bitmaps.
377749ad5e8SJohn Snow
378749ad5e8SJohn Snow        Create a bitmap "before" VM execution begins, then create a second
379749ad5e8SJohn Snow        bitmap AFTER writes have already occurred. Use transactions to create
380749ad5e8SJohn Snow        a full backup and synchronize both bitmaps to this backup.
381749ad5e8SJohn Snow        Create an incremental backup through both bitmaps and verify that
382749ad5e8SJohn Snow        both backups match the current drive0 image.
383749ad5e8SJohn Snow        '''
384749ad5e8SJohn Snow
385749ad5e8SJohn Snow        drive0 = self.drives[0]
386749ad5e8SJohn Snow        bitmap0 = self.add_bitmap('bitmap0', drive0)
387749ad5e8SJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
388749ad5e8SJohn Snow                                          ('0xfe', '16M', '256k'),
389749ad5e8SJohn Snow                                          ('0x64', '32736k', '64k')))
390749ad5e8SJohn Snow        bitmap1 = self.add_bitmap('bitmap1', drive0)
391749ad5e8SJohn Snow
392749ad5e8SJohn Snow        result = self.vm.qmp('transaction', actions=[
393749ad5e8SJohn Snow            transaction_bitmap_clear(bitmap0.drive['id'], bitmap0.name),
394749ad5e8SJohn Snow            transaction_bitmap_clear(bitmap1.drive['id'], bitmap1.name),
395749ad5e8SJohn Snow            transaction_drive_backup(drive0['id'], drive0['backup'],
396749ad5e8SJohn Snow                                     sync='full', format=drive0['fmt'])
397749ad5e8SJohn Snow        ])
398749ad5e8SJohn Snow        self.assert_qmp(result, 'return', {})
399749ad5e8SJohn Snow        self.wait_until_completed(drive0['id'])
400749ad5e8SJohn Snow        self.files.append(drive0['backup'])
401749ad5e8SJohn Snow
402749ad5e8SJohn Snow        self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512),
403749ad5e8SJohn Snow                                          ('0x55', '8M', '352k'),
404749ad5e8SJohn Snow                                          ('0x78', '15872k', '1M')))
405749ad5e8SJohn Snow        # Both bitmaps should be correctly in sync.
406749ad5e8SJohn Snow        self.create_incremental(bitmap0)
407749ad5e8SJohn Snow        self.create_incremental(bitmap1)
408749ad5e8SJohn Snow        self.vm.shutdown()
409749ad5e8SJohn Snow        self.check_backups()
410749ad5e8SJohn Snow
411749ad5e8SJohn Snow
4120aef09b9SJohn Snow    def do_transaction_failure_test(self, race=False):
413fc6c796fSJohn Snow        # Create a second drive, with pattern:
414fc6c796fSJohn Snow        drive1 = self.add_node('drive1')
415fc6c796fSJohn Snow        self.img_create(drive1['file'], drive1['fmt'])
416fc6c796fSJohn Snow        io_write_patterns(drive1['file'], (('0x14', 0, 512),
417fc6c796fSJohn Snow                                           ('0x5d', '1M', '32k'),
418fc6c796fSJohn Snow                                           ('0xcd', '32M', '124k')))
419fc6c796fSJohn Snow
420fc6c796fSJohn Snow        # Create a blkdebug interface to this img as 'drive1'
4210153d2f5SKevin Wolf        result = self.vm.qmp('blockdev-add',
4220153d2f5SKevin Wolf            node_name=drive1['id'],
4230153d2f5SKevin Wolf            driver=drive1['fmt'],
4240153d2f5SKevin Wolf            file={
425fc6c796fSJohn Snow                'driver': 'blkdebug',
426fc6c796fSJohn Snow                'image': {
427fc6c796fSJohn Snow                    'driver': 'file',
428fc6c796fSJohn Snow                    'filename': drive1['file']
429fc6c796fSJohn Snow                },
430fc6c796fSJohn Snow                'set-state': [{
431fc6c796fSJohn Snow                    'event': 'flush_to_disk',
432fc6c796fSJohn Snow                    'state': 1,
433fc6c796fSJohn Snow                    'new_state': 2
434fc6c796fSJohn Snow                }],
435fc6c796fSJohn Snow                'inject-error': [{
436fc6c796fSJohn Snow                    'event': 'read_aio',
437fc6c796fSJohn Snow                    'errno': 5,
438fc6c796fSJohn Snow                    'state': 2,
439fc6c796fSJohn Snow                    'immediately': False,
440fc6c796fSJohn Snow                    'once': True
441fc6c796fSJohn Snow                }],
442fc6c796fSJohn Snow            }
4430153d2f5SKevin Wolf        )
444fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
445fc6c796fSJohn Snow
446fc6c796fSJohn Snow        # Create bitmaps and full backups for both drives
447fc6c796fSJohn Snow        drive0 = self.drives[0]
448fc6c796fSJohn Snow        dr0bm0 = self.add_bitmap('bitmap0', drive0)
449fc6c796fSJohn Snow        dr1bm0 = self.add_bitmap('bitmap0', drive1)
450fc6c796fSJohn Snow        self.create_anchor_backup(drive0)
451fc6c796fSJohn Snow        self.create_anchor_backup(drive1)
452fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
453fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
454fc6c796fSJohn Snow
455fc6c796fSJohn Snow        # Emulate some writes
4560aef09b9SJohn Snow        if not race:
457fc6c796fSJohn Snow            self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
458fc6c796fSJohn Snow                                              ('0xfe', '16M', '256k'),
459fc6c796fSJohn Snow                                              ('0x64', '32736k', '64k')))
460fc6c796fSJohn Snow        self.hmp_io_writes(drive1['id'], (('0xba', 0, 512),
461fc6c796fSJohn Snow                                          ('0xef', '16M', '256k'),
462fc6c796fSJohn Snow                                          ('0x46', '32736k', '64k')))
463fc6c796fSJohn Snow
464fc6c796fSJohn Snow        # Create incremental backup targets
465fc6c796fSJohn Snow        target0 = self.prepare_backup(dr0bm0)
466fc6c796fSJohn Snow        target1 = self.prepare_backup(dr1bm0)
467fc6c796fSJohn Snow
468fc6c796fSJohn Snow        # Ask for a new incremental backup per-each drive,
4690aef09b9SJohn Snow        # expecting drive1's backup to fail. In the 'race' test,
4700aef09b9SJohn Snow        # we expect drive1 to attempt to cancel the empty drive0 job.
471fc6c796fSJohn Snow        transaction = [
472fc6c796fSJohn Snow            transaction_drive_backup(drive0['id'], target0, sync='incremental',
473fc6c796fSJohn Snow                                     format=drive0['fmt'], mode='existing',
474fc6c796fSJohn Snow                                     bitmap=dr0bm0.name),
475fc6c796fSJohn Snow            transaction_drive_backup(drive1['id'], target1, sync='incremental',
476fc6c796fSJohn Snow                                     format=drive1['fmt'], mode='existing',
477fc6c796fSJohn Snow                                     bitmap=dr1bm0.name)
478fc6c796fSJohn Snow        ]
479fc6c796fSJohn Snow        result = self.vm.qmp('transaction', actions=transaction,
480fc6c796fSJohn Snow                             properties={'completion-mode': 'grouped'} )
481fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
482fc6c796fSJohn Snow
483fc6c796fSJohn Snow        # Observe that drive0's backup is cancelled and drive1 completes with
484fc6c796fSJohn Snow        # an error.
485fc6c796fSJohn Snow        self.wait_qmp_backup_cancelled(drive0['id'])
486fc6c796fSJohn Snow        self.assertFalse(self.wait_qmp_backup(drive1['id']))
487fc6c796fSJohn Snow        error = self.vm.event_wait('BLOCK_JOB_ERROR')
488fc6c796fSJohn Snow        self.assert_qmp(error, 'data', {'device': drive1['id'],
489fc6c796fSJohn Snow                                        'action': 'report',
490fc6c796fSJohn Snow                                        'operation': 'read'})
491fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
492fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
493fc6c796fSJohn Snow
494fc6c796fSJohn Snow        # Delete drive0's successful target and eliminate our record of the
4950aef09b9SJohn Snow        # unsuccessful drive1 target.
496fc6c796fSJohn Snow        dr0bm0.del_target()
497fc6c796fSJohn Snow        dr1bm0.del_target()
4980aef09b9SJohn Snow        if race:
4990aef09b9SJohn Snow            # Don't re-run the transaction, we only wanted to test the race.
5000aef09b9SJohn Snow            self.vm.shutdown()
5010aef09b9SJohn Snow            return
5020aef09b9SJohn Snow
5030aef09b9SJohn Snow        # Re-run the same transaction:
504fc6c796fSJohn Snow        target0 = self.prepare_backup(dr0bm0)
505fc6c796fSJohn Snow        target1 = self.prepare_backup(dr1bm0)
506fc6c796fSJohn Snow
507fc6c796fSJohn Snow        # Re-run the exact same transaction.
508fc6c796fSJohn Snow        result = self.vm.qmp('transaction', actions=transaction,
509fc6c796fSJohn Snow                             properties={'completion-mode':'grouped'})
510fc6c796fSJohn Snow        self.assert_qmp(result, 'return', {})
511fc6c796fSJohn Snow
512fc6c796fSJohn Snow        # Both should complete successfully this time.
513fc6c796fSJohn Snow        self.assertTrue(self.wait_qmp_backup(drive0['id']))
514fc6c796fSJohn Snow        self.assertTrue(self.wait_qmp_backup(drive1['id']))
515fc6c796fSJohn Snow        self.make_reference_backup(dr0bm0)
516fc6c796fSJohn Snow        self.make_reference_backup(dr1bm0)
517fc6c796fSJohn Snow        self.assertFalse(self.vm.get_qmp_events(wait=False))
518fc6c796fSJohn Snow        self.assert_no_active_block_jobs()
519fc6c796fSJohn Snow
520fc6c796fSJohn Snow        # And the images should of course validate.
521fc6c796fSJohn Snow        self.vm.shutdown()
522fc6c796fSJohn Snow        self.check_backups()
523fc6c796fSJohn Snow
5240aef09b9SJohn Snow    def test_transaction_failure(self):
5250aef09b9SJohn Snow        '''Test: Verify backups made from a transaction that partially fails.
5260aef09b9SJohn Snow
5270aef09b9SJohn Snow        Add a second drive with its own unique pattern, and add a bitmap to each
5280aef09b9SJohn Snow        drive. Use blkdebug to interfere with the backup on just one drive and
5290aef09b9SJohn Snow        attempt to create a coherent incremental backup across both drives.
5300aef09b9SJohn Snow
5310aef09b9SJohn Snow        verify a failure in one but not both, then delete the failed stubs and
5320aef09b9SJohn Snow        re-run the same transaction.
5330aef09b9SJohn Snow
5340aef09b9SJohn Snow        verify that both incrementals are created successfully.
5350aef09b9SJohn Snow        '''
5360aef09b9SJohn Snow        self.do_transaction_failure_test()
5370aef09b9SJohn Snow
5380aef09b9SJohn Snow    def test_transaction_failure_race(self):
5390aef09b9SJohn Snow        '''Test: Verify that transactions with jobs that have no data to
5400aef09b9SJohn Snow        transfer do not cause race conditions in the cancellation of the entire
5410aef09b9SJohn Snow        transaction job group.
5420aef09b9SJohn Snow        '''
5430aef09b9SJohn Snow        self.do_transaction_failure_test(race=True)
5440aef09b9SJohn Snow
545fc6c796fSJohn Snow
5469f7264f5SJohn Snow    def test_sync_dirty_bitmap_missing(self):
5479f7264f5SJohn Snow        self.assert_no_active_block_jobs()
5489f7264f5SJohn Snow        self.files.append(self.err_img)
5499f7264f5SJohn Snow        result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
5504b80ab2bSJohn Snow                             sync='incremental', format=self.drives[0]['fmt'],
5519f7264f5SJohn Snow                             target=self.err_img)
5529f7264f5SJohn Snow        self.assert_qmp(result, 'error/class', 'GenericError')
5539f7264f5SJohn Snow
5549f7264f5SJohn Snow
5559f7264f5SJohn Snow    def test_sync_dirty_bitmap_not_found(self):
5569f7264f5SJohn Snow        self.assert_no_active_block_jobs()
5579f7264f5SJohn Snow        self.files.append(self.err_img)
5589f7264f5SJohn Snow        result = self.vm.qmp('drive-backup', device=self.drives[0]['id'],
5594b80ab2bSJohn Snow                             sync='incremental', bitmap='unknown',
5609f7264f5SJohn Snow                             format=self.drives[0]['fmt'], target=self.err_img)
5619f7264f5SJohn Snow        self.assert_qmp(result, 'error/class', 'GenericError')
5629f7264f5SJohn Snow
5639f7264f5SJohn Snow
56459fc5d84SJohn Snow    def test_sync_dirty_bitmap_bad_granularity(self):
56559fc5d84SJohn Snow        '''
56659fc5d84SJohn Snow        Test: Test what happens if we provide an improper granularity.
56759fc5d84SJohn Snow
56859fc5d84SJohn Snow        The granularity must always be a power of 2.
56959fc5d84SJohn Snow        '''
57059fc5d84SJohn Snow        self.assert_no_active_block_jobs()
57159fc5d84SJohn Snow        self.assertRaises(AssertionError, self.add_bitmap,
57259fc5d84SJohn Snow                          'bitmap0', self.drives[0],
57359fc5d84SJohn Snow                          granularity=64000)
57459fc5d84SJohn Snow
5758a9cb864SMax Reitz    def test_growing_before_backup(self):
5768a9cb864SMax Reitz        '''
5778a9cb864SMax Reitz        Test: Add a bitmap, truncate the image, write past the old
5788a9cb864SMax Reitz              end, do a backup.
5798a9cb864SMax Reitz
5808a9cb864SMax Reitz        Incremental backup should not ignore dirty bits past the old
5818a9cb864SMax Reitz        image end.
5828a9cb864SMax Reitz        '''
5838a9cb864SMax Reitz        self.assert_no_active_block_jobs()
5848a9cb864SMax Reitz
5858a9cb864SMax Reitz        self.create_anchor_backup()
5868a9cb864SMax Reitz
5878a9cb864SMax Reitz        self.add_bitmap('bitmap0', self.drives[0])
5888a9cb864SMax Reitz
5898a9cb864SMax Reitz        res = self.vm.qmp('block_resize', device=self.drives[0]['id'],
5908a9cb864SMax Reitz                          size=(65 * 1048576))
5918a9cb864SMax Reitz        self.assert_qmp(res, 'return', {})
5928a9cb864SMax Reitz
5938a9cb864SMax Reitz        # Dirty the image past the old end
5948a9cb864SMax Reitz        self.vm.hmp_qemu_io(self.drives[0]['id'], 'write 64M 64k')
5958a9cb864SMax Reitz
5968a9cb864SMax Reitz        target = self.prepare_backup(size='65M')
5978a9cb864SMax Reitz        self.create_incremental(target=target)
5988a9cb864SMax Reitz
5998a9cb864SMax Reitz        self.vm.shutdown()
6008a9cb864SMax Reitz        self.check_backups()
6018a9cb864SMax Reitz
60259fc5d84SJohn Snow
603ce2cbc49SJohn Snowclass TestIncrementalBackupBlkdebug(TestIncrementalBackupBase):
604ce2cbc49SJohn Snow    '''Incremental backup tests that utilize a BlkDebug filter on drive0.'''
605ce2cbc49SJohn Snow
60635cea223SJohn Snow    def setUp(self):
60735cea223SJohn Snow        drive0 = self.add_node('drive0')
60835cea223SJohn Snow        self.img_create(drive0['file'], drive0['fmt'])
60935cea223SJohn Snow        self.write_default_pattern(drive0['file'])
61035cea223SJohn Snow        self.vm.launch()
61135cea223SJohn Snow
612ce2cbc49SJohn Snow    def test_incremental_failure(self):
613ce2cbc49SJohn Snow        '''Test: Verify backups made after a failure are correct.
614ce2cbc49SJohn Snow
615ce2cbc49SJohn Snow        Simulate a failure during an incremental backup block job,
616ce2cbc49SJohn Snow        emulate additional writes, then create another incremental backup
617ce2cbc49SJohn Snow        afterwards and verify that the backup created is correct.
618ce2cbc49SJohn Snow        '''
619ce2cbc49SJohn Snow
62035cea223SJohn Snow        drive0 = self.drives[0]
6210153d2f5SKevin Wolf        result = self.vm.qmp('blockdev-add',
6220153d2f5SKevin Wolf            node_name=drive0['id'],
6230153d2f5SKevin Wolf            driver=drive0['fmt'],
6240153d2f5SKevin Wolf            file={
625ce2cbc49SJohn Snow                'driver': 'blkdebug',
626ce2cbc49SJohn Snow                'image': {
627ce2cbc49SJohn Snow                    'driver': 'file',
62835cea223SJohn Snow                    'filename': drive0['file']
629ce2cbc49SJohn Snow                },
630ce2cbc49SJohn Snow                'set-state': [{
631ce2cbc49SJohn Snow                    'event': 'flush_to_disk',
632ce2cbc49SJohn Snow                    'state': 1,
633ce2cbc49SJohn Snow                    'new_state': 2
634ce2cbc49SJohn Snow                }],
635ce2cbc49SJohn Snow                'inject-error': [{
636ce2cbc49SJohn Snow                    'event': 'read_aio',
637ce2cbc49SJohn Snow                    'errno': 5,
638ce2cbc49SJohn Snow                    'state': 2,
639ce2cbc49SJohn Snow                    'immediately': False,
640ce2cbc49SJohn Snow                    'once': True
641ce2cbc49SJohn Snow                }],
642ce2cbc49SJohn Snow            }
6430153d2f5SKevin Wolf        )
644ce2cbc49SJohn Snow        self.assert_qmp(result, 'return', {})
645ce2cbc49SJohn Snow
64635cea223SJohn Snow        self.create_anchor_backup(drive0)
64735cea223SJohn Snow        self.add_bitmap('bitmap0', drive0)
648ce2cbc49SJohn Snow        # Note: at this point, during a normal execution,
649ce2cbc49SJohn Snow        # Assume that the VM resumes and begins issuing IO requests here.
650ce2cbc49SJohn Snow
65135cea223SJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
652ce2cbc49SJohn Snow                                          ('0xfe', '16M', '256k'),
653ce2cbc49SJohn Snow                                          ('0x64', '32736k', '64k')))
654ce2cbc49SJohn Snow
655ce2cbc49SJohn Snow        result = self.create_incremental(validate=False)
656ce2cbc49SJohn Snow        self.assertFalse(result)
65735cea223SJohn Snow        self.hmp_io_writes(drive0['id'], (('0x9a', 0, 512),
658ce2cbc49SJohn Snow                                          ('0x55', '8M', '352k'),
659ce2cbc49SJohn Snow                                          ('0x78', '15872k', '1M')))
660ce2cbc49SJohn Snow        self.create_incremental()
661ce2cbc49SJohn Snow        self.vm.shutdown()
662ce2cbc49SJohn Snow        self.check_backups()
663ce2cbc49SJohn Snow
664c61b198bSJohn Snow    def test_incremental_pause(self):
665c61b198bSJohn Snow        """
666c61b198bSJohn Snow        Test an incremental backup that errors into a pause and is resumed.
667c61b198bSJohn Snow        """
668c61b198bSJohn Snow
669c61b198bSJohn Snow        drive0 = self.drives[0]
6705c4343b8SVladimir Sementsov-Ogievskiy        # NB: The blkdebug script here looks for a "flush, read" pattern.
6715c4343b8SVladimir Sementsov-Ogievskiy        # The flush occurs in hmp_io_writes, and the read during the block job.
672c61b198bSJohn Snow        result = self.vm.qmp('blockdev-add',
673c61b198bSJohn Snow                             node_name=drive0['id'],
674c61b198bSJohn Snow                             driver=drive0['fmt'],
675c61b198bSJohn Snow                             file={
676c61b198bSJohn Snow                                 'driver': 'blkdebug',
677c61b198bSJohn Snow                                 'image': {
678c61b198bSJohn Snow                                     'driver': 'file',
679c61b198bSJohn Snow                                     'filename': drive0['file']
680c61b198bSJohn Snow                                 },
681c61b198bSJohn Snow                                 'set-state': [{
682c61b198bSJohn Snow                                     'event': 'flush_to_disk',
683c61b198bSJohn Snow                                     'state': 1,
684c61b198bSJohn Snow                                     'new_state': 2
685c61b198bSJohn Snow                                 }],
686c61b198bSJohn Snow                                 'inject-error': [{
687c61b198bSJohn Snow                                     'event': 'read_aio',
688c61b198bSJohn Snow                                     'errno': 5,
6895c4343b8SVladimir Sementsov-Ogievskiy                                     'state': 2,
690c61b198bSJohn Snow                                     'immediately': False,
691c61b198bSJohn Snow                                     'once': True
692c61b198bSJohn Snow                                 }],
693c61b198bSJohn Snow                             })
694c61b198bSJohn Snow        self.assert_qmp(result, 'return', {})
695c61b198bSJohn Snow        self.create_anchor_backup(drive0)
696c61b198bSJohn Snow        bitmap = self.add_bitmap('bitmap0', drive0)
697c61b198bSJohn Snow
698c61b198bSJohn Snow        # Emulate guest activity
699c61b198bSJohn Snow        self.hmp_io_writes(drive0['id'], (('0xab', 0, 512),
700c61b198bSJohn Snow                                          ('0xfe', '16M', '256k'),
701c61b198bSJohn Snow                                          ('0x64', '32736k', '64k')))
702c61b198bSJohn Snow
703c61b198bSJohn Snow        # Bitmap Status Check
7045c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
7055c4343b8SVladimir Sementsov-Ogievskiy            drive0['id'], bitmap.name, {
7065c4343b8SVladimir Sementsov-Ogievskiy                'count': 458752,
7075c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
7085c4343b8SVladimir Sementsov-Ogievskiy                'status': 'active',
7095c4343b8SVladimir Sementsov-Ogievskiy                'busy': False,
7105c4343b8SVladimir Sementsov-Ogievskiy                'recording': True
7115c4343b8SVladimir Sementsov-Ogievskiy            }))
712c61b198bSJohn Snow
713c61b198bSJohn Snow        # Start backup
714c61b198bSJohn Snow        parent, _ = bitmap.last_target()
715c61b198bSJohn Snow        target = self.prepare_backup(bitmap, parent)
716c61b198bSJohn Snow        res = self.vm.qmp('drive-backup',
717c61b198bSJohn Snow                          job_id=bitmap.drive['id'],
718c61b198bSJohn Snow                          device=bitmap.drive['id'],
719c61b198bSJohn Snow                          sync='incremental',
720c61b198bSJohn Snow                          bitmap=bitmap.name,
721c61b198bSJohn Snow                          format=bitmap.drive['fmt'],
722c61b198bSJohn Snow                          target=target,
723c61b198bSJohn Snow                          mode='existing',
724c61b198bSJohn Snow                          on_source_error='stop')
725c61b198bSJohn Snow        self.assert_qmp(res, 'return', {})
726c61b198bSJohn Snow
727c61b198bSJohn Snow        # Wait for the error
728c61b198bSJohn Snow        event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
729c61b198bSJohn Snow                                   match={"data":{"device":bitmap.drive['id']}})
730c61b198bSJohn Snow        self.assert_qmp(event, 'data', {'device': bitmap.drive['id'],
731c61b198bSJohn Snow                                        'action': 'stop',
732c61b198bSJohn Snow                                        'operation': 'read'})
733c61b198bSJohn Snow
734c61b198bSJohn Snow        # Bitmap Status Check
7355c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
7365c4343b8SVladimir Sementsov-Ogievskiy            drive0['id'], bitmap.name, {
7375c4343b8SVladimir Sementsov-Ogievskiy                'count': 458752,
7385c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
7395c4343b8SVladimir Sementsov-Ogievskiy                'status': 'frozen',
7405c4343b8SVladimir Sementsov-Ogievskiy                'busy': True,
7415c4343b8SVladimir Sementsov-Ogievskiy                'recording': True
7425c4343b8SVladimir Sementsov-Ogievskiy            }))
743c61b198bSJohn Snow
744c61b198bSJohn Snow        # Resume and check incremental backup for consistency
745c61b198bSJohn Snow        res = self.vm.qmp('block-job-resume', device=bitmap.drive['id'])
746c61b198bSJohn Snow        self.assert_qmp(res, 'return', {})
747c61b198bSJohn Snow        self.wait_qmp_backup(bitmap.drive['id'])
748c61b198bSJohn Snow
749c61b198bSJohn Snow        # Bitmap Status Check
7505c4343b8SVladimir Sementsov-Ogievskiy        self.assertTrue(self.vm.check_bitmap_status(
7515c4343b8SVladimir Sementsov-Ogievskiy            drive0['id'], bitmap.name, {
7525c4343b8SVladimir Sementsov-Ogievskiy                'count': 0,
7535c4343b8SVladimir Sementsov-Ogievskiy                'granularity': 65536,
7545c4343b8SVladimir Sementsov-Ogievskiy                'status': 'active',
7555c4343b8SVladimir Sementsov-Ogievskiy                'busy': False,
7565c4343b8SVladimir Sementsov-Ogievskiy                'recording': True
7575c4343b8SVladimir Sementsov-Ogievskiy            }))
758c61b198bSJohn Snow
759c61b198bSJohn Snow        # Finalize / Cleanup
760c61b198bSJohn Snow        self.make_reference_backup(bitmap)
761c61b198bSJohn Snow        self.vm.shutdown()
762c61b198bSJohn Snow        self.check_backups()
763c61b198bSJohn Snow
764ce2cbc49SJohn Snow
7659f7264f5SJohn Snowif __name__ == '__main__':
766103cbc77SMax Reitz    iotests.main(supported_fmts=['qcow2'],
767103cbc77SMax Reitz                 supported_protocols=['file'])
768