1a44be033SVladimir Sementsov-Ogievskiy#!/usr/bin/env python3 2a44be033SVladimir Sementsov-Ogievskiy# group: rw migration 3a44be033SVladimir Sementsov-Ogievskiy# 4a44be033SVladimir Sementsov-Ogievskiy# Tests for dirty bitmaps migration. 5a44be033SVladimir Sementsov-Ogievskiy# 6a44be033SVladimir Sementsov-Ogievskiy# Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved. 7a44be033SVladimir Sementsov-Ogievskiy# 8a44be033SVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 9a44be033SVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 10a44be033SVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 11a44be033SVladimir Sementsov-Ogievskiy# (at your option) any later version. 12a44be033SVladimir Sementsov-Ogievskiy# 13a44be033SVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 14a44be033SVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 15a44be033SVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16a44be033SVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 17a44be033SVladimir Sementsov-Ogievskiy# 18a44be033SVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 19a44be033SVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 20a44be033SVladimir Sementsov-Ogievskiy# 21a44be033SVladimir Sementsov-Ogievskiy 22a44be033SVladimir Sementsov-Ogievskiyimport itertools 23a44be033SVladimir Sementsov-Ogievskiyimport operator 2422968996SJohn Snowimport os 25a44be033SVladimir Sementsov-Ogievskiyimport re 2622968996SJohn Snow 27d8c2e47dSHanna Reitzimport iotests 28a44be033SVladimir Sementsov-Ogievskiyfrom iotests import qemu_img, qemu_img_create, Timeout 29a44be033SVladimir Sementsov-Ogievskiy 30a44be033SVladimir Sementsov-Ogievskiy 31a44be033SVladimir Sementsov-Ogievskiydisk_a = os.path.join(iotests.test_dir, 'disk_a') 32a44be033SVladimir Sementsov-Ogievskiydisk_b = os.path.join(iotests.test_dir, 'disk_b') 33a44be033SVladimir Sementsov-Ogievskiybase_a = os.path.join(iotests.test_dir, 'base_a') 34a44be033SVladimir Sementsov-Ogievskiysize = '1M' 35a44be033SVladimir Sementsov-Ogievskiymig_file = os.path.join(iotests.test_dir, 'mig_file') 36a44be033SVladimir Sementsov-Ogievskiymig_cmd = 'exec: cat > ' + mig_file 37a44be033SVladimir Sementsov-Ogievskiyincoming_cmd = 'exec: cat ' + mig_file 38a44be033SVladimir Sementsov-Ogievskiy 39a44be033SVladimir Sementsov-Ogievskiy 40d8c2e47dSHanna Reitzdef get_bitmap_hash(vm): 41d8c2e47dSHanna Reitz result = vm.qmp('x-debug-block-dirty-bitmap-sha256', 42d8c2e47dSHanna Reitz node='drive0', name='bitmap0') 43d8c2e47dSHanna Reitz return result['return']['sha256'] 44d8c2e47dSHanna Reitz 45d8c2e47dSHanna Reitz 46a44be033SVladimir Sementsov-Ogievskiyclass TestDirtyBitmapMigration(iotests.QMPTestCase): 47a44be033SVladimir Sementsov-Ogievskiy def tearDown(self): 48a44be033SVladimir Sementsov-Ogievskiy self.vm_a.shutdown() 49a44be033SVladimir Sementsov-Ogievskiy self.vm_b.shutdown() 50a44be033SVladimir Sementsov-Ogievskiy os.remove(disk_a) 51a44be033SVladimir Sementsov-Ogievskiy os.remove(disk_b) 52a44be033SVladimir Sementsov-Ogievskiy os.remove(mig_file) 53a44be033SVladimir Sementsov-Ogievskiy 54a44be033SVladimir Sementsov-Ogievskiy def setUp(self): 55a44be033SVladimir Sementsov-Ogievskiy qemu_img('create', '-f', iotests.imgfmt, disk_a, size) 56a44be033SVladimir Sementsov-Ogievskiy qemu_img('create', '-f', iotests.imgfmt, disk_b, size) 57a44be033SVladimir Sementsov-Ogievskiy 58a44be033SVladimir Sementsov-Ogievskiy self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a) 59a44be033SVladimir Sementsov-Ogievskiy self.vm_a.launch() 60a44be033SVladimir Sementsov-Ogievskiy 61a44be033SVladimir Sementsov-Ogievskiy self.vm_b = iotests.VM(path_suffix='b') 62a44be033SVladimir Sementsov-Ogievskiy 63a44be033SVladimir Sementsov-Ogievskiy def add_bitmap(self, vm, granularity, persistent): 64a44be033SVladimir Sementsov-Ogievskiy params = {'node': 'drive0', 65a44be033SVladimir Sementsov-Ogievskiy 'name': 'bitmap0', 66a44be033SVladimir Sementsov-Ogievskiy 'granularity': granularity} 67a44be033SVladimir Sementsov-Ogievskiy if persistent: 68a44be033SVladimir Sementsov-Ogievskiy params['persistent'] = True 69a44be033SVladimir Sementsov-Ogievskiy 70*b6aed193SVladimir Sementsov-Ogievskiy vm.cmd('block-dirty-bitmap-add', params) 71a44be033SVladimir Sementsov-Ogievskiy 72a44be033SVladimir Sementsov-Ogievskiy def check_bitmap(self, vm, sha256): 73a44be033SVladimir Sementsov-Ogievskiy result = vm.qmp('x-debug-block-dirty-bitmap-sha256', 74a44be033SVladimir Sementsov-Ogievskiy node='drive0', name='bitmap0') 75a44be033SVladimir Sementsov-Ogievskiy if sha256: 76d8c2e47dSHanna Reitz self.assert_qmp(result, 'return/sha256', sha256) 77a44be033SVladimir Sementsov-Ogievskiy else: 78a44be033SVladimir Sementsov-Ogievskiy self.assert_qmp(result, 'error/desc', 79d8c2e47dSHanna Reitz "Dirty bitmap 'bitmap0' not found") 80a44be033SVladimir Sementsov-Ogievskiy 81a44be033SVladimir Sementsov-Ogievskiy def do_test_migration_resume_source(self, persistent, migrate_bitmaps): 82a44be033SVladimir Sementsov-Ogievskiy granularity = 512 83a44be033SVladimir Sementsov-Ogievskiy 84a44be033SVladimir Sementsov-Ogievskiy # regions = ((start, count), ...) 85a44be033SVladimir Sementsov-Ogievskiy regions = ((0, 0x10000), 86a44be033SVladimir Sementsov-Ogievskiy (0xf0000, 0x10000), 87a44be033SVladimir Sementsov-Ogievskiy (0xa0201, 0x1000)) 88a44be033SVladimir Sementsov-Ogievskiy 89a44be033SVladimir Sementsov-Ogievskiy mig_caps = [{'capability': 'events', 'state': True}] 90a44be033SVladimir Sementsov-Ogievskiy if migrate_bitmaps: 91a44be033SVladimir Sementsov-Ogievskiy mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) 92a44be033SVladimir Sementsov-Ogievskiy 93*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('migrate-set-capabilities', 94a44be033SVladimir Sementsov-Ogievskiy capabilities=mig_caps) 95a44be033SVladimir Sementsov-Ogievskiy 96a44be033SVladimir Sementsov-Ogievskiy self.add_bitmap(self.vm_a, granularity, persistent) 97a44be033SVladimir Sementsov-Ogievskiy for r in regions: 98a44be033SVladimir Sementsov-Ogievskiy self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) 99d8c2e47dSHanna Reitz sha256 = get_bitmap_hash(self.vm_a) 100a44be033SVladimir Sementsov-Ogievskiy 101*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('migrate', uri=mig_cmd) 102a44be033SVladimir Sementsov-Ogievskiy while True: 103a44be033SVladimir Sementsov-Ogievskiy event = self.vm_a.event_wait('MIGRATION') 104a44be033SVladimir Sementsov-Ogievskiy if event['data']['status'] == 'completed': 105a44be033SVladimir Sementsov-Ogievskiy break 106a44be033SVladimir Sementsov-Ogievskiy while True: 107a44be033SVladimir Sementsov-Ogievskiy result = self.vm_a.qmp('query-status') 108d8c2e47dSHanna Reitz if result['return']['status'] == 'postmigrate': 109a44be033SVladimir Sementsov-Ogievskiy break 110a44be033SVladimir Sementsov-Ogievskiy 111a44be033SVladimir Sementsov-Ogievskiy # test that bitmap is still here 112a44be033SVladimir Sementsov-Ogievskiy removed = (not migrate_bitmaps) and persistent 113a44be033SVladimir Sementsov-Ogievskiy self.check_bitmap(self.vm_a, False if removed else sha256) 114a44be033SVladimir Sementsov-Ogievskiy 115*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('cont') 116a44be033SVladimir Sementsov-Ogievskiy 117a44be033SVladimir Sementsov-Ogievskiy # test that bitmap is still here after invalidation 118a44be033SVladimir Sementsov-Ogievskiy self.check_bitmap(self.vm_a, sha256) 119a44be033SVladimir Sementsov-Ogievskiy 120a44be033SVladimir Sementsov-Ogievskiy # shutdown and check that invalidation didn't fail 121a44be033SVladimir Sementsov-Ogievskiy self.vm_a.shutdown() 122a44be033SVladimir Sementsov-Ogievskiy 123a44be033SVladimir Sementsov-Ogievskiy # catch 'Could not reopen qcow2 layer: Bitmap already exists' 124a44be033SVladimir Sementsov-Ogievskiy # possible error 125a44be033SVladimir Sementsov-Ogievskiy log = self.vm_a.get_log() 126a44be033SVladimir Sementsov-Ogievskiy log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) 127a44be033SVladimir Sementsov-Ogievskiy log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', 128a44be033SVladimir Sementsov-Ogievskiy '', log) 129a44be033SVladimir Sementsov-Ogievskiy log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) 130a44be033SVladimir Sementsov-Ogievskiy self.assertEqual(log, '') 131a44be033SVladimir Sementsov-Ogievskiy 132a44be033SVladimir Sementsov-Ogievskiy # test that bitmap is still persistent 133a44be033SVladimir Sementsov-Ogievskiy self.vm_a.launch() 134a44be033SVladimir Sementsov-Ogievskiy self.check_bitmap(self.vm_a, sha256 if persistent else False) 135a44be033SVladimir Sementsov-Ogievskiy 136a44be033SVladimir Sementsov-Ogievskiy def do_test_migration(self, persistent, migrate_bitmaps, online, 137a44be033SVladimir Sementsov-Ogievskiy shared_storage, pre_shutdown): 138a44be033SVladimir Sementsov-Ogievskiy granularity = 512 139a44be033SVladimir Sementsov-Ogievskiy 140a44be033SVladimir Sementsov-Ogievskiy # regions = ((start, count), ...) 141a44be033SVladimir Sementsov-Ogievskiy regions = ((0, 0x10000), 142a44be033SVladimir Sementsov-Ogievskiy (0xf0000, 0x10000), 143a44be033SVladimir Sementsov-Ogievskiy (0xa0201, 0x1000)) 144a44be033SVladimir Sementsov-Ogievskiy 145a44be033SVladimir Sementsov-Ogievskiy should_migrate = \ 146a44be033SVladimir Sementsov-Ogievskiy (migrate_bitmaps and (persistent or not pre_shutdown)) or \ 147a44be033SVladimir Sementsov-Ogievskiy (persistent and shared_storage) 148a44be033SVladimir Sementsov-Ogievskiy mig_caps = [{'capability': 'events', 'state': True}] 149a44be033SVladimir Sementsov-Ogievskiy if migrate_bitmaps: 150a44be033SVladimir Sementsov-Ogievskiy mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) 151a44be033SVladimir Sementsov-Ogievskiy 152a44be033SVladimir Sementsov-Ogievskiy self.vm_b.add_incoming(incoming_cmd if online else "defer") 153a44be033SVladimir Sementsov-Ogievskiy self.vm_b.add_drive(disk_a if shared_storage else disk_b) 154a44be033SVladimir Sementsov-Ogievskiy 155a44be033SVladimir Sementsov-Ogievskiy if online: 156a44be033SVladimir Sementsov-Ogievskiy os.mkfifo(mig_file) 157a44be033SVladimir Sementsov-Ogievskiy self.vm_b.launch() 158*b6aed193SVladimir Sementsov-Ogievskiy self.vm_b.cmd('migrate-set-capabilities', 159a44be033SVladimir Sementsov-Ogievskiy capabilities=mig_caps) 160a44be033SVladimir Sementsov-Ogievskiy 161a44be033SVladimir Sementsov-Ogievskiy self.add_bitmap(self.vm_a, granularity, persistent) 162a44be033SVladimir Sementsov-Ogievskiy for r in regions: 163a44be033SVladimir Sementsov-Ogievskiy self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) 164d8c2e47dSHanna Reitz sha256 = get_bitmap_hash(self.vm_a) 165a44be033SVladimir Sementsov-Ogievskiy 166a44be033SVladimir Sementsov-Ogievskiy if pre_shutdown: 167a44be033SVladimir Sementsov-Ogievskiy self.vm_a.shutdown() 168a44be033SVladimir Sementsov-Ogievskiy self.vm_a.launch() 169a44be033SVladimir Sementsov-Ogievskiy 170*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('migrate-set-capabilities', 171a44be033SVladimir Sementsov-Ogievskiy capabilities=mig_caps) 172a44be033SVladimir Sementsov-Ogievskiy 173*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('migrate', uri=mig_cmd) 174a44be033SVladimir Sementsov-Ogievskiy while True: 175a44be033SVladimir Sementsov-Ogievskiy event = self.vm_a.event_wait('MIGRATION') 176a44be033SVladimir Sementsov-Ogievskiy if event['data']['status'] == 'completed': 177a44be033SVladimir Sementsov-Ogievskiy break 178a44be033SVladimir Sementsov-Ogievskiy 179a44be033SVladimir Sementsov-Ogievskiy if not online: 180a44be033SVladimir Sementsov-Ogievskiy self.vm_a.shutdown() 181a44be033SVladimir Sementsov-Ogievskiy self.vm_b.launch() 182*b6aed193SVladimir Sementsov-Ogievskiy self.vm_b.cmd('migrate-set-capabilities', 183a44be033SVladimir Sementsov-Ogievskiy capabilities=mig_caps) 184*b6aed193SVladimir Sementsov-Ogievskiy self.vm_b.cmd('migrate-incoming', uri=incoming_cmd) 185a44be033SVladimir Sementsov-Ogievskiy 186a44be033SVladimir Sementsov-Ogievskiy while True: 187a44be033SVladimir Sementsov-Ogievskiy event = self.vm_b.event_wait('MIGRATION') 188a44be033SVladimir Sementsov-Ogievskiy if event['data']['status'] == 'completed': 189a44be033SVladimir Sementsov-Ogievskiy break 190a44be033SVladimir Sementsov-Ogievskiy 191a44be033SVladimir Sementsov-Ogievskiy self.check_bitmap(self.vm_b, sha256 if should_migrate else False) 192a44be033SVladimir Sementsov-Ogievskiy 193a44be033SVladimir Sementsov-Ogievskiy if should_migrate: 194a44be033SVladimir Sementsov-Ogievskiy self.vm_b.shutdown() 195a44be033SVladimir Sementsov-Ogievskiy 196a44be033SVladimir Sementsov-Ogievskiy # catch 'Could not reopen qcow2 layer: Bitmap already exists' 197a44be033SVladimir Sementsov-Ogievskiy # possible error 198a44be033SVladimir Sementsov-Ogievskiy log = self.vm_b.get_log() 199a44be033SVladimir Sementsov-Ogievskiy log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) 200a44be033SVladimir Sementsov-Ogievskiy log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) 201a44be033SVladimir Sementsov-Ogievskiy self.assertEqual(log, '') 202a44be033SVladimir Sementsov-Ogievskiy 203a44be033SVladimir Sementsov-Ogievskiy # recreate vm_b, as we don't want -incoming option (this will lead 204a44be033SVladimir Sementsov-Ogievskiy # to "cat" process left alive after test finish) 205a44be033SVladimir Sementsov-Ogievskiy self.vm_b = iotests.VM(path_suffix='b') 206a44be033SVladimir Sementsov-Ogievskiy self.vm_b.add_drive(disk_a if shared_storage else disk_b) 207a44be033SVladimir Sementsov-Ogievskiy self.vm_b.launch() 208a44be033SVladimir Sementsov-Ogievskiy self.check_bitmap(self.vm_b, sha256 if persistent else False) 209a44be033SVladimir Sementsov-Ogievskiy 210a44be033SVladimir Sementsov-Ogievskiy 211d8c2e47dSHanna Reitzdef inject_test_case(klass, suffix, method, *args, **kwargs): 212a44be033SVladimir Sementsov-Ogievskiy mc = operator.methodcaller(method, *args, **kwargs) 213d8c2e47dSHanna Reitz # We want to add a function attribute to `klass`, so that it is 214d8c2e47dSHanna Reitz # correctly converted to a method on instantiation. The 215d8c2e47dSHanna Reitz # methodcaller object `mc` is a callable, not a function, so we 216d8c2e47dSHanna Reitz # need the lambda to turn it into a function. 217d8c2e47dSHanna Reitz # pylint: disable=unnecessary-lambda 218d8c2e47dSHanna Reitz setattr(klass, 'test_' + method + suffix, lambda self: mc(self)) 219d8c2e47dSHanna Reitz 220a44be033SVladimir Sementsov-Ogievskiy 221a44be033SVladimir Sementsov-Ogievskiyclass TestDirtyBitmapBackingMigration(iotests.QMPTestCase): 222a44be033SVladimir Sementsov-Ogievskiy def setUp(self): 223a44be033SVladimir Sementsov-Ogievskiy qemu_img_create('-f', iotests.imgfmt, base_a, size) 224a44be033SVladimir Sementsov-Ogievskiy qemu_img_create('-f', iotests.imgfmt, '-F', iotests.imgfmt, 225a44be033SVladimir Sementsov-Ogievskiy '-b', base_a, disk_a, size) 226a44be033SVladimir Sementsov-Ogievskiy 227a44be033SVladimir Sementsov-Ogievskiy for f in (disk_a, base_a): 228a44be033SVladimir Sementsov-Ogievskiy qemu_img('bitmap', '--add', f, 'bmap0') 229a44be033SVladimir Sementsov-Ogievskiy 230a44be033SVladimir Sementsov-Ogievskiy blockdev = { 231a44be033SVladimir Sementsov-Ogievskiy 'node-name': 'node0', 232a44be033SVladimir Sementsov-Ogievskiy 'driver': iotests.imgfmt, 233a44be033SVladimir Sementsov-Ogievskiy 'file': { 234a44be033SVladimir Sementsov-Ogievskiy 'driver': 'file', 235a44be033SVladimir Sementsov-Ogievskiy 'filename': disk_a 236a44be033SVladimir Sementsov-Ogievskiy }, 237a44be033SVladimir Sementsov-Ogievskiy 'backing': { 238a44be033SVladimir Sementsov-Ogievskiy 'node-name': 'node0-base', 239a44be033SVladimir Sementsov-Ogievskiy 'driver': iotests.imgfmt, 240a44be033SVladimir Sementsov-Ogievskiy 'file': { 241a44be033SVladimir Sementsov-Ogievskiy 'driver': 'file', 242a44be033SVladimir Sementsov-Ogievskiy 'filename': base_a 243a44be033SVladimir Sementsov-Ogievskiy } 244a44be033SVladimir Sementsov-Ogievskiy } 245a44be033SVladimir Sementsov-Ogievskiy } 246a44be033SVladimir Sementsov-Ogievskiy 247a44be033SVladimir Sementsov-Ogievskiy self.vm = iotests.VM() 248a44be033SVladimir Sementsov-Ogievskiy self.vm.launch() 249a44be033SVladimir Sementsov-Ogievskiy 250*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('blockdev-add', blockdev) 251a44be033SVladimir Sementsov-Ogievskiy 252a44be033SVladimir Sementsov-Ogievskiy # Check that the bitmaps are there 253d8c2e47dSHanna Reitz nodes = self.vm.qmp('query-named-block-nodes', flat=True)['return'] 254d8c2e47dSHanna Reitz for node in nodes: 255a44be033SVladimir Sementsov-Ogievskiy if 'node0' in node['node-name']: 256a44be033SVladimir Sementsov-Ogievskiy self.assert_qmp(node, 'dirty-bitmaps[0]/name', 'bmap0') 257a44be033SVladimir Sementsov-Ogievskiy 258a44be033SVladimir Sementsov-Ogievskiy caps = [{'capability': 'events', 'state': True}] 259*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('migrate-set-capabilities', capabilities=caps) 260a44be033SVladimir Sementsov-Ogievskiy 261a44be033SVladimir Sementsov-Ogievskiy def tearDown(self): 262a44be033SVladimir Sementsov-Ogievskiy self.vm.shutdown() 263a44be033SVladimir Sementsov-Ogievskiy for f in (disk_a, base_a): 264a44be033SVladimir Sementsov-Ogievskiy os.remove(f) 265a44be033SVladimir Sementsov-Ogievskiy 266a44be033SVladimir Sementsov-Ogievskiy def test_cont_on_source(self): 267a44be033SVladimir Sementsov-Ogievskiy """ 268a44be033SVladimir Sementsov-Ogievskiy Continue the source after migration. 269a44be033SVladimir Sementsov-Ogievskiy """ 270*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('migrate', uri='exec: cat > /dev/null') 271a44be033SVladimir Sementsov-Ogievskiy 272a44be033SVladimir Sementsov-Ogievskiy with Timeout(10, 'Migration timeout'): 273a44be033SVladimir Sementsov-Ogievskiy self.vm.wait_migration('postmigrate') 274a44be033SVladimir Sementsov-Ogievskiy 275*b6aed193SVladimir Sementsov-Ogievskiy self.vm.cmd('cont') 276a44be033SVladimir Sementsov-Ogievskiy 277a44be033SVladimir Sementsov-Ogievskiy 27822968996SJohn Snowdef main() -> None: 27922968996SJohn Snow for cmb in list(itertools.product((True, False), repeat=5)): 28022968996SJohn Snow name = ('_' if cmb[0] else '_not_') + 'persistent_' 28122968996SJohn Snow name += ('_' if cmb[1] else '_not_') + 'migbitmap_' 28222968996SJohn Snow name += '_online' if cmb[2] else '_offline' 28322968996SJohn Snow name += '_shared' if cmb[3] else '_nonshared' 28422968996SJohn Snow if cmb[4]: 28522968996SJohn Snow name += '__pre_shutdown' 28622968996SJohn Snow 28722968996SJohn Snow inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', 28822968996SJohn Snow *list(cmb)) 28922968996SJohn Snow 29022968996SJohn Snow for cmb in list(itertools.product((True, False), repeat=2)): 29122968996SJohn Snow name = ('_' if cmb[0] else '_not_') + 'persistent_' 29222968996SJohn Snow name += ('_' if cmb[1] else '_not_') + 'migbitmap' 29322968996SJohn Snow 29422968996SJohn Snow inject_test_case(TestDirtyBitmapMigration, name, 29522968996SJohn Snow 'do_test_migration_resume_source', *list(cmb)) 29622968996SJohn Snow 29722968996SJohn Snow iotests.main( 29822968996SJohn Snow supported_fmts=['qcow2'], 299b30b8077SVladimir Sementsov-Ogievskiy supported_protocols=['file'], 300b30b8077SVladimir Sementsov-Ogievskiy unsupported_imgopts=['compat'] 30122968996SJohn Snow ) 30222968996SJohn Snow 30322968996SJohn Snow 304a44be033SVladimir Sementsov-Ogievskiyif __name__ == '__main__': 30522968996SJohn Snow main() 306