1cb5c6cd2SMax Reitz#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: migration 3cb5c6cd2SMax Reitz# 4cb5c6cd2SMax Reitz# Copyright (C) 2020 Red Hat, Inc. 5cb5c6cd2SMax Reitz# 6cb5c6cd2SMax Reitz# Tests for dirty bitmaps migration with node aliases 7cb5c6cd2SMax Reitz# 8cb5c6cd2SMax Reitz# This program is free software; you can redistribute it and/or modify 9cb5c6cd2SMax Reitz# it under the terms of the GNU General Public License as published by 10cb5c6cd2SMax Reitz# the Free Software Foundation; either version 2 of the License, or 11cb5c6cd2SMax Reitz# (at your option) any later version. 12cb5c6cd2SMax Reitz# 13cb5c6cd2SMax Reitz# This program is distributed in the hope that it will be useful, 14cb5c6cd2SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 15cb5c6cd2SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16cb5c6cd2SMax Reitz# GNU General Public License for more details. 17cb5c6cd2SMax Reitz# 18cb5c6cd2SMax Reitz# You should have received a copy of the GNU General Public License 19cb5c6cd2SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 20cb5c6cd2SMax Reitz# 21cb5c6cd2SMax Reitz 22cb5c6cd2SMax Reitzimport os 23cb5c6cd2SMax Reitzimport random 24cb5c6cd2SMax Reitzimport re 254aa6fc69SEric Blakefrom typing import Dict, List, Optional 2659c9466dSMax Reitz 27cb5c6cd2SMax Reitzimport iotests 2859c9466dSMax Reitz 29cb5c6cd2SMax Reitz 304aa6fc69SEric BlakeBlockBitmapMapping = List[Dict[str, object]] 31cb5c6cd2SMax Reitz 32cb5c6cd2SMax Reitzmig_sock = os.path.join(iotests.sock_dir, 'mig_sock') 33cb5c6cd2SMax Reitz 34cb5c6cd2SMax Reitz 35cb5c6cd2SMax Reitzclass TestDirtyBitmapMigration(iotests.QMPTestCase): 36cb5c6cd2SMax Reitz src_node_name: str = '' 37cb5c6cd2SMax Reitz dst_node_name: str = '' 38cb5c6cd2SMax Reitz src_bmap_name: str = '' 39cb5c6cd2SMax Reitz dst_bmap_name: str = '' 40cb5c6cd2SMax Reitz 41cb5c6cd2SMax Reitz def setUp(self) -> None: 42cb5c6cd2SMax Reitz self.vm_a = iotests.VM(path_suffix='-a') 43cb5c6cd2SMax Reitz self.vm_a.add_blockdev(f'node-name={self.src_node_name},' 44cb5c6cd2SMax Reitz 'driver=null-co') 45cb5c6cd2SMax Reitz self.vm_a.launch() 46cb5c6cd2SMax Reitz 47cb5c6cd2SMax Reitz self.vm_b = iotests.VM(path_suffix='-b') 48cb5c6cd2SMax Reitz self.vm_b.add_blockdev(f'node-name={self.dst_node_name},' 49cb5c6cd2SMax Reitz 'driver=null-co') 50cb5c6cd2SMax Reitz self.vm_b.add_incoming(f'unix:{mig_sock}') 51cb5c6cd2SMax Reitz self.vm_b.launch() 52cb5c6cd2SMax Reitz 53*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('block-dirty-bitmap-add', 54cb5c6cd2SMax Reitz node=self.src_node_name, 55cb5c6cd2SMax Reitz name=self.src_bmap_name) 56cb5c6cd2SMax Reitz 57cb5c6cd2SMax Reitz # Dirty some random megabytes 58cb5c6cd2SMax Reitz for _ in range(9): 59cb5c6cd2SMax Reitz mb_ofs = random.randrange(1024) 60cb5c6cd2SMax Reitz self.vm_a.hmp_qemu_io(self.src_node_name, f'discard {mb_ofs}M 1M') 61cb5c6cd2SMax Reitz 62cb5c6cd2SMax Reitz result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256', 63cb5c6cd2SMax Reitz node=self.src_node_name, 64cb5c6cd2SMax Reitz name=self.src_bmap_name) 65cb5c6cd2SMax Reitz self.bitmap_hash_reference = result['return']['sha256'] 66cb5c6cd2SMax Reitz 67cb5c6cd2SMax Reitz caps = [{'capability': name, 'state': True} 68cb5c6cd2SMax Reitz for name in ('dirty-bitmaps', 'events')] 69cb5c6cd2SMax Reitz 70cb5c6cd2SMax Reitz for vm in (self.vm_a, self.vm_b): 71*b6aed193SVladimir Sementsov-Ogievskiy vm.cmd('migrate-set-capabilities', capabilities=caps) 72cb5c6cd2SMax Reitz 73cb5c6cd2SMax Reitz def tearDown(self) -> None: 74cb5c6cd2SMax Reitz self.vm_a.shutdown() 75cb5c6cd2SMax Reitz self.vm_b.shutdown() 76cb5c6cd2SMax Reitz try: 77cb5c6cd2SMax Reitz os.remove(mig_sock) 78cb5c6cd2SMax Reitz except OSError: 79cb5c6cd2SMax Reitz pass 80cb5c6cd2SMax Reitz 81cb5c6cd2SMax Reitz def check_bitmap(self, bitmap_name_valid: bool) -> None: 82cb5c6cd2SMax Reitz result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256', 83cb5c6cd2SMax Reitz node=self.dst_node_name, 84cb5c6cd2SMax Reitz name=self.dst_bmap_name) 85cb5c6cd2SMax Reitz if bitmap_name_valid: 86cb5c6cd2SMax Reitz self.assert_qmp(result, 'return/sha256', 87cb5c6cd2SMax Reitz self.bitmap_hash_reference) 88cb5c6cd2SMax Reitz else: 89cb5c6cd2SMax Reitz self.assert_qmp(result, 'error/desc', 90cb5c6cd2SMax Reitz f"Dirty bitmap '{self.dst_bmap_name}' not found") 91cb5c6cd2SMax Reitz 92cb5c6cd2SMax Reitz def migrate(self, bitmap_name_valid: bool = True, 93cb5c6cd2SMax Reitz migration_success: bool = True) -> None: 94*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('migrate', uri=f'unix:{mig_sock}') 95cb5c6cd2SMax Reitz 96cb5c6cd2SMax Reitz with iotests.Timeout(5, 'Timeout waiting for migration to complete'): 97cb5c6cd2SMax Reitz self.assertEqual(self.vm_a.wait_migration('postmigrate'), 98cb5c6cd2SMax Reitz migration_success) 99cb5c6cd2SMax Reitz self.assertEqual(self.vm_b.wait_migration('running'), 100cb5c6cd2SMax Reitz migration_success) 101cb5c6cd2SMax Reitz 102cb5c6cd2SMax Reitz if migration_success: 103cb5c6cd2SMax Reitz self.check_bitmap(bitmap_name_valid) 104cb5c6cd2SMax Reitz 105cb5c6cd2SMax Reitz def verify_dest_error(self, msg: Optional[str]) -> None: 106cb5c6cd2SMax Reitz """ 107cb5c6cd2SMax Reitz Check whether the given error message is present in vm_b's log. 108cb5c6cd2SMax Reitz (vm_b is shut down to do so.) 109cb5c6cd2SMax Reitz If @msg is None, check that there has not been any error. 110cb5c6cd2SMax Reitz """ 111cb5c6cd2SMax Reitz self.vm_b.shutdown() 11259c9466dSMax Reitz 11359c9466dSMax Reitz log = self.vm_b.get_log() 11459c9466dSMax Reitz assert log is not None # Loaded after shutdown 11559c9466dSMax Reitz 116cb5c6cd2SMax Reitz if msg is None: 11759c9466dSMax Reitz self.assertNotIn('qemu-system-', log) 118cb5c6cd2SMax Reitz else: 11959c9466dSMax Reitz self.assertIn(msg, log) 120cb5c6cd2SMax Reitz 121cb5c6cd2SMax Reitz @staticmethod 122cb5c6cd2SMax Reitz def mapping(node_name: str, node_alias: str, 123cb5c6cd2SMax Reitz bitmap_name: str, bitmap_alias: str) -> BlockBitmapMapping: 124cb5c6cd2SMax Reitz return [{ 125cb5c6cd2SMax Reitz 'node-name': node_name, 126cb5c6cd2SMax Reitz 'alias': node_alias, 127cb5c6cd2SMax Reitz 'bitmaps': [{ 128cb5c6cd2SMax Reitz 'name': bitmap_name, 129cb5c6cd2SMax Reitz 'alias': bitmap_alias 130cb5c6cd2SMax Reitz }] 131cb5c6cd2SMax Reitz }] 132cb5c6cd2SMax Reitz 133cb5c6cd2SMax Reitz def set_mapping(self, vm: iotests.VM, mapping: BlockBitmapMapping, 134cb5c6cd2SMax Reitz error: Optional[str] = None) -> None: 135cb5c6cd2SMax Reitz """ 136cb5c6cd2SMax Reitz Invoke migrate-set-parameters on @vm to set the given @mapping. 137cb5c6cd2SMax Reitz Check for success if @error is None, or verify the error message 138cb5c6cd2SMax Reitz if it is not. 139cb5c6cd2SMax Reitz On success, verify that "info migrate_parameters" on HMP returns 140cb5c6cd2SMax Reitz our mapping. (Just to check its formatting code.) 141cb5c6cd2SMax Reitz """ 142cb5c6cd2SMax Reitz result = vm.qmp('migrate-set-parameters', 143cb5c6cd2SMax Reitz block_bitmap_mapping=mapping) 144cb5c6cd2SMax Reitz 145cb5c6cd2SMax Reitz if error is None: 146cb5c6cd2SMax Reitz self.assert_qmp(result, 'return', {}) 147cb5c6cd2SMax Reitz 148cb5c6cd2SMax Reitz result = vm.qmp('human-monitor-command', 149cb5c6cd2SMax Reitz command_line='info migrate_parameters') 150cb5c6cd2SMax Reitz 151cb5c6cd2SMax Reitz m = re.search(r'^block-bitmap-mapping:\r?(\n .*)*\n', 152cb5c6cd2SMax Reitz result['return'], flags=re.MULTILINE) 153cb5c6cd2SMax Reitz hmp_mapping = m.group(0).replace('\r', '') if m else None 154cb5c6cd2SMax Reitz 155cb5c6cd2SMax Reitz self.assertEqual(hmp_mapping, self.to_hmp_mapping(mapping)) 156cb5c6cd2SMax Reitz else: 157cb5c6cd2SMax Reitz self.assert_qmp(result, 'error/desc', error) 158cb5c6cd2SMax Reitz 159cb5c6cd2SMax Reitz @staticmethod 160cb5c6cd2SMax Reitz def to_hmp_mapping(mapping: BlockBitmapMapping) -> str: 161cb5c6cd2SMax Reitz result = 'block-bitmap-mapping:\n' 162cb5c6cd2SMax Reitz 163cb5c6cd2SMax Reitz for node in mapping: 164cb5c6cd2SMax Reitz result += f" '{node['node-name']}' -> '{node['alias']}'\n" 165cb5c6cd2SMax Reitz 166cb5c6cd2SMax Reitz assert isinstance(node['bitmaps'], list) 167cb5c6cd2SMax Reitz for bitmap in node['bitmaps']: 168cb5c6cd2SMax Reitz result += f" '{bitmap['name']}' -> '{bitmap['alias']}'\n" 169cb5c6cd2SMax Reitz 170cb5c6cd2SMax Reitz return result 171cb5c6cd2SMax Reitz 172cb5c6cd2SMax Reitz 173cb5c6cd2SMax Reitzclass TestAliasMigration(TestDirtyBitmapMigration): 174cb5c6cd2SMax Reitz src_node_name = 'node0' 175cb5c6cd2SMax Reitz dst_node_name = 'node0' 176cb5c6cd2SMax Reitz src_bmap_name = 'bmap0' 177cb5c6cd2SMax Reitz dst_bmap_name = 'bmap0' 178cb5c6cd2SMax Reitz 179cb5c6cd2SMax Reitz def test_migration_without_alias(self) -> None: 180cb5c6cd2SMax Reitz self.migrate(self.src_node_name == self.dst_node_name and 181cb5c6cd2SMax Reitz self.src_bmap_name == self.dst_bmap_name) 182cb5c6cd2SMax Reitz 183cb5c6cd2SMax Reitz # Check for error message on the destination 184cb5c6cd2SMax Reitz if self.src_node_name != self.dst_node_name: 185cb5c6cd2SMax Reitz self.verify_dest_error(f"Cannot find " 186785ec4b1SConnor Kuehl f"device='{self.src_node_name}' nor " 187785ec4b1SConnor Kuehl f"node-name='{self.src_node_name}'") 188cb5c6cd2SMax Reitz else: 189cb5c6cd2SMax Reitz self.verify_dest_error(None) 190cb5c6cd2SMax Reitz 191cb5c6cd2SMax Reitz def test_alias_on_src_migration(self) -> None: 192cb5c6cd2SMax Reitz mapping = self.mapping(self.src_node_name, self.dst_node_name, 193cb5c6cd2SMax Reitz self.src_bmap_name, self.dst_bmap_name) 194cb5c6cd2SMax Reitz 195cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping) 196cb5c6cd2SMax Reitz self.migrate() 197cb5c6cd2SMax Reitz self.verify_dest_error(None) 198cb5c6cd2SMax Reitz 199cb5c6cd2SMax Reitz def test_alias_on_dst_migration(self) -> None: 200cb5c6cd2SMax Reitz mapping = self.mapping(self.dst_node_name, self.src_node_name, 201cb5c6cd2SMax Reitz self.dst_bmap_name, self.src_bmap_name) 202cb5c6cd2SMax Reitz 203cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, mapping) 204cb5c6cd2SMax Reitz self.migrate() 205cb5c6cd2SMax Reitz self.verify_dest_error(None) 206cb5c6cd2SMax Reitz 207cb5c6cd2SMax Reitz def test_alias_on_both_migration(self) -> None: 208cb5c6cd2SMax Reitz src_map = self.mapping(self.src_node_name, 'node-alias', 209cb5c6cd2SMax Reitz self.src_bmap_name, 'bmap-alias') 210cb5c6cd2SMax Reitz 211cb5c6cd2SMax Reitz dst_map = self.mapping(self.dst_node_name, 'node-alias', 212cb5c6cd2SMax Reitz self.dst_bmap_name, 'bmap-alias') 213cb5c6cd2SMax Reitz 214cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, src_map) 215cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, dst_map) 216cb5c6cd2SMax Reitz self.migrate() 217cb5c6cd2SMax Reitz self.verify_dest_error(None) 218cb5c6cd2SMax Reitz 219cb5c6cd2SMax Reitz 220cb5c6cd2SMax Reitzclass TestNodeAliasMigration(TestAliasMigration): 221cb5c6cd2SMax Reitz src_node_name = 'node-src' 222cb5c6cd2SMax Reitz dst_node_name = 'node-dst' 223cb5c6cd2SMax Reitz 224cb5c6cd2SMax Reitz 225cb5c6cd2SMax Reitzclass TestBitmapAliasMigration(TestAliasMigration): 226cb5c6cd2SMax Reitz src_bmap_name = 'bmap-src' 227cb5c6cd2SMax Reitz dst_bmap_name = 'bmap-dst' 228cb5c6cd2SMax Reitz 229cb5c6cd2SMax Reitz 230cb5c6cd2SMax Reitzclass TestFullAliasMigration(TestAliasMigration): 231cb5c6cd2SMax Reitz src_node_name = 'node-src' 232cb5c6cd2SMax Reitz dst_node_name = 'node-dst' 233cb5c6cd2SMax Reitz src_bmap_name = 'bmap-src' 234cb5c6cd2SMax Reitz dst_bmap_name = 'bmap-dst' 235cb5c6cd2SMax Reitz 236cb5c6cd2SMax Reitz 237cb5c6cd2SMax Reitzclass TestLongBitmapNames(TestAliasMigration): 238cb5c6cd2SMax Reitz # Giving long bitmap names is OK, as long as there is a short alias for 239cb5c6cd2SMax Reitz # migration 240cb5c6cd2SMax Reitz src_bmap_name = 'a' * 512 241cb5c6cd2SMax Reitz dst_bmap_name = 'b' * 512 242cb5c6cd2SMax Reitz 243cb5c6cd2SMax Reitz # Skip all tests that do not use the intermediate alias 244cb5c6cd2SMax Reitz def test_migration_without_alias(self) -> None: 245cb5c6cd2SMax Reitz pass 246cb5c6cd2SMax Reitz 247cb5c6cd2SMax Reitz def test_alias_on_src_migration(self) -> None: 248cb5c6cd2SMax Reitz pass 249cb5c6cd2SMax Reitz 250cb5c6cd2SMax Reitz def test_alias_on_dst_migration(self) -> None: 251cb5c6cd2SMax Reitz pass 252cb5c6cd2SMax Reitz 253cb5c6cd2SMax Reitz 254cb5c6cd2SMax Reitzclass TestBlockBitmapMappingErrors(TestDirtyBitmapMigration): 255cb5c6cd2SMax Reitz src_node_name = 'node0' 256cb5c6cd2SMax Reitz dst_node_name = 'node0' 257cb5c6cd2SMax Reitz src_bmap_name = 'bmap0' 258cb5c6cd2SMax Reitz dst_bmap_name = 'bmap0' 259cb5c6cd2SMax Reitz 260cb5c6cd2SMax Reitz """ 261cb5c6cd2SMax Reitz Note that mapping nodes or bitmaps that do not exist is not an error. 262cb5c6cd2SMax Reitz """ 263cb5c6cd2SMax Reitz 264cb5c6cd2SMax Reitz def test_non_injective_node_mapping(self) -> None: 265cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [ 266cb5c6cd2SMax Reitz { 267cb5c6cd2SMax Reitz 'node-name': 'node0', 268cb5c6cd2SMax Reitz 'alias': 'common-alias', 269cb5c6cd2SMax Reitz 'bitmaps': [{ 270cb5c6cd2SMax Reitz 'name': 'bmap0', 271cb5c6cd2SMax Reitz 'alias': 'bmap-alias0' 272cb5c6cd2SMax Reitz }] 273cb5c6cd2SMax Reitz }, 274cb5c6cd2SMax Reitz { 275cb5c6cd2SMax Reitz 'node-name': 'node1', 276cb5c6cd2SMax Reitz 'alias': 'common-alias', 277cb5c6cd2SMax Reitz 'bitmaps': [{ 278cb5c6cd2SMax Reitz 'name': 'bmap1', 279cb5c6cd2SMax Reitz 'alias': 'bmap-alias1' 280cb5c6cd2SMax Reitz }] 281cb5c6cd2SMax Reitz } 282cb5c6cd2SMax Reitz ] 283cb5c6cd2SMax Reitz 284cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 285cb5c6cd2SMax Reitz "Invalid mapping given for block-bitmap-mapping: " 286cb5c6cd2SMax Reitz "The node alias 'common-alias' is used twice") 287cb5c6cd2SMax Reitz 288cb5c6cd2SMax Reitz def test_non_injective_bitmap_mapping(self) -> None: 289cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [{ 290cb5c6cd2SMax Reitz 'node-name': 'node0', 291cb5c6cd2SMax Reitz 'alias': 'node-alias0', 292cb5c6cd2SMax Reitz 'bitmaps': [ 293cb5c6cd2SMax Reitz { 294cb5c6cd2SMax Reitz 'name': 'bmap0', 295cb5c6cd2SMax Reitz 'alias': 'common-alias' 296cb5c6cd2SMax Reitz }, 297cb5c6cd2SMax Reitz { 298cb5c6cd2SMax Reitz 'name': 'bmap1', 299cb5c6cd2SMax Reitz 'alias': 'common-alias' 300cb5c6cd2SMax Reitz } 301cb5c6cd2SMax Reitz ] 302cb5c6cd2SMax Reitz }] 303cb5c6cd2SMax Reitz 304cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 305cb5c6cd2SMax Reitz "Invalid mapping given for block-bitmap-mapping: " 306cb5c6cd2SMax Reitz "The bitmap alias 'node-alias0'/'common-alias' is " 307cb5c6cd2SMax Reitz "used twice") 308cb5c6cd2SMax Reitz 309cb5c6cd2SMax Reitz def test_ambiguous_node_mapping(self) -> None: 310cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [ 311cb5c6cd2SMax Reitz { 312cb5c6cd2SMax Reitz 'node-name': 'node0', 313cb5c6cd2SMax Reitz 'alias': 'node-alias0', 314cb5c6cd2SMax Reitz 'bitmaps': [{ 315cb5c6cd2SMax Reitz 'name': 'bmap0', 316cb5c6cd2SMax Reitz 'alias': 'bmap-alias0' 317cb5c6cd2SMax Reitz }] 318cb5c6cd2SMax Reitz }, 319cb5c6cd2SMax Reitz { 320cb5c6cd2SMax Reitz 'node-name': 'node0', 321cb5c6cd2SMax Reitz 'alias': 'node-alias1', 322cb5c6cd2SMax Reitz 'bitmaps': [{ 323cb5c6cd2SMax Reitz 'name': 'bmap0', 324cb5c6cd2SMax Reitz 'alias': 'bmap-alias0' 325cb5c6cd2SMax Reitz }] 326cb5c6cd2SMax Reitz } 327cb5c6cd2SMax Reitz ] 328cb5c6cd2SMax Reitz 329cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 330cb5c6cd2SMax Reitz "Invalid mapping given for block-bitmap-mapping: " 331cb5c6cd2SMax Reitz "The node name 'node0' is mapped twice") 332cb5c6cd2SMax Reitz 333cb5c6cd2SMax Reitz def test_ambiguous_bitmap_mapping(self) -> None: 334cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [{ 335cb5c6cd2SMax Reitz 'node-name': 'node0', 336cb5c6cd2SMax Reitz 'alias': 'node-alias0', 337cb5c6cd2SMax Reitz 'bitmaps': [ 338cb5c6cd2SMax Reitz { 339cb5c6cd2SMax Reitz 'name': 'bmap0', 340cb5c6cd2SMax Reitz 'alias': 'bmap-alias0' 341cb5c6cd2SMax Reitz }, 342cb5c6cd2SMax Reitz { 343cb5c6cd2SMax Reitz 'name': 'bmap0', 344cb5c6cd2SMax Reitz 'alias': 'bmap-alias1' 345cb5c6cd2SMax Reitz } 346cb5c6cd2SMax Reitz ] 347cb5c6cd2SMax Reitz }] 348cb5c6cd2SMax Reitz 349cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 350cb5c6cd2SMax Reitz "Invalid mapping given for block-bitmap-mapping: " 351cb5c6cd2SMax Reitz "The bitmap 'node0'/'bmap0' is mapped twice") 352cb5c6cd2SMax Reitz 353cb5c6cd2SMax Reitz def test_migratee_node_is_not_mapped_on_src(self) -> None: 354cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, []) 355cb5c6cd2SMax Reitz # Should just ignore all bitmaps on unmapped nodes 356cb5c6cd2SMax Reitz self.migrate(False) 357cb5c6cd2SMax Reitz self.verify_dest_error(None) 358cb5c6cd2SMax Reitz 359cb5c6cd2SMax Reitz def test_migratee_node_is_not_mapped_on_dst(self) -> None: 360cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, []) 361cb5c6cd2SMax Reitz self.migrate(False) 362cb5c6cd2SMax Reitz self.verify_dest_error(f"Unknown node alias '{self.src_node_name}'") 363cb5c6cd2SMax Reitz 364cb5c6cd2SMax Reitz def test_migratee_bitmap_is_not_mapped_on_src(self) -> None: 365cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [{ 366cb5c6cd2SMax Reitz 'node-name': self.src_node_name, 367cb5c6cd2SMax Reitz 'alias': self.dst_node_name, 368cb5c6cd2SMax Reitz 'bitmaps': [] 369cb5c6cd2SMax Reitz }] 370cb5c6cd2SMax Reitz 371cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping) 372cb5c6cd2SMax Reitz # Should just ignore all unmapped bitmaps 373cb5c6cd2SMax Reitz self.migrate(False) 374cb5c6cd2SMax Reitz self.verify_dest_error(None) 375cb5c6cd2SMax Reitz 376cb5c6cd2SMax Reitz def test_migratee_bitmap_is_not_mapped_on_dst(self) -> None: 377cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [{ 378cb5c6cd2SMax Reitz 'node-name': self.dst_node_name, 379cb5c6cd2SMax Reitz 'alias': self.src_node_name, 380cb5c6cd2SMax Reitz 'bitmaps': [] 381cb5c6cd2SMax Reitz }] 382cb5c6cd2SMax Reitz 383cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, mapping) 384cb5c6cd2SMax Reitz self.migrate(False) 385cb5c6cd2SMax Reitz self.verify_dest_error(f"Unknown bitmap alias " 386cb5c6cd2SMax Reitz f"'{self.src_bmap_name}' " 387cb5c6cd2SMax Reitz f"on node '{self.dst_node_name}' " 388cb5c6cd2SMax Reitz f"(alias '{self.src_node_name}')") 389cb5c6cd2SMax Reitz 390cb5c6cd2SMax Reitz def test_unused_mapping_on_dst(self) -> None: 391cb5c6cd2SMax Reitz # Let the source not send any bitmaps 392cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, []) 393cb5c6cd2SMax Reitz 394cb5c6cd2SMax Reitz # Establish some mapping on the destination 395cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, []) 396cb5c6cd2SMax Reitz 397cb5c6cd2SMax Reitz # The fact that there is a mapping on B without any bitmaps 398cb5c6cd2SMax Reitz # being received should be fine, not fatal 399cb5c6cd2SMax Reitz self.migrate(False) 400cb5c6cd2SMax Reitz self.verify_dest_error(None) 401cb5c6cd2SMax Reitz 402cb5c6cd2SMax Reitz def test_non_wellformed_node_alias(self) -> None: 403cb5c6cd2SMax Reitz alias = '123-foo' 404cb5c6cd2SMax Reitz 405cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [{ 406cb5c6cd2SMax Reitz 'node-name': self.src_node_name, 407cb5c6cd2SMax Reitz 'alias': alias, 408cb5c6cd2SMax Reitz 'bitmaps': [] 409cb5c6cd2SMax Reitz }] 410cb5c6cd2SMax Reitz 411cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 412cb5c6cd2SMax Reitz f"Invalid mapping given for block-bitmap-mapping: " 413cb5c6cd2SMax Reitz f"The node alias '{alias}' is not well-formed") 414cb5c6cd2SMax Reitz 415cb5c6cd2SMax Reitz def test_node_alias_too_long(self) -> None: 416cb5c6cd2SMax Reitz alias = 'a' * 256 417cb5c6cd2SMax Reitz 418cb5c6cd2SMax Reitz mapping: BlockBitmapMapping = [{ 419cb5c6cd2SMax Reitz 'node-name': self.src_node_name, 420cb5c6cd2SMax Reitz 'alias': alias, 421cb5c6cd2SMax Reitz 'bitmaps': [] 422cb5c6cd2SMax Reitz }] 423cb5c6cd2SMax Reitz 424cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 425cb5c6cd2SMax Reitz f"Invalid mapping given for block-bitmap-mapping: " 426cb5c6cd2SMax Reitz f"The node alias '{alias}' is longer than 255 bytes") 427cb5c6cd2SMax Reitz 428cb5c6cd2SMax Reitz def test_bitmap_alias_too_long(self) -> None: 429cb5c6cd2SMax Reitz alias = 'a' * 256 430cb5c6cd2SMax Reitz 431cb5c6cd2SMax Reitz mapping = self.mapping(self.src_node_name, self.dst_node_name, 432cb5c6cd2SMax Reitz self.src_bmap_name, alias) 433cb5c6cd2SMax Reitz 434cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, mapping, 435cb5c6cd2SMax Reitz f"Invalid mapping given for block-bitmap-mapping: " 436cb5c6cd2SMax Reitz f"The bitmap alias '{alias}' is longer than 255 " 437cb5c6cd2SMax Reitz f"bytes") 438cb5c6cd2SMax Reitz 439cb5c6cd2SMax Reitz def test_bitmap_name_too_long(self) -> None: 440cb5c6cd2SMax Reitz name = 'a' * 256 441cb5c6cd2SMax Reitz 442*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('block-dirty-bitmap-add', 443cb5c6cd2SMax Reitz node=self.src_node_name, 444cb5c6cd2SMax Reitz name=name) 445cb5c6cd2SMax Reitz 446cb5c6cd2SMax Reitz self.migrate(False, False) 447cb5c6cd2SMax Reitz 448cb5c6cd2SMax Reitz # Check for the error in the source's log 449cb5c6cd2SMax Reitz self.vm_a.shutdown() 45059c9466dSMax Reitz 45159c9466dSMax Reitz log = self.vm_a.get_log() 45259c9466dSMax Reitz assert log is not None # Loaded after shutdown 45359c9466dSMax Reitz 454cb5c6cd2SMax Reitz self.assertIn(f"Cannot migrate bitmap '{name}' on node " 455cb5c6cd2SMax Reitz f"'{self.src_node_name}': Name is longer than 255 bytes", 45659c9466dSMax Reitz log) 457cb5c6cd2SMax Reitz 4588f05aee5SJohn Snow # Destination VM will terminate w/ error of its own accord 4598f05aee5SJohn Snow # due to the failed migration. 4608f05aee5SJohn Snow self.vm_b.wait() 4618f05aee5SJohn Snow rc = self.vm_b.exitcode() 4628f05aee5SJohn Snow assert rc is not None and rc > 0 463cb5c6cd2SMax Reitz 464cb5c6cd2SMax Reitz def test_aliased_bitmap_name_too_long(self) -> None: 465cb5c6cd2SMax Reitz # Longer than the maximum for bitmap names 466cb5c6cd2SMax Reitz self.dst_bmap_name = 'a' * 1024 467cb5c6cd2SMax Reitz 468cb5c6cd2SMax Reitz mapping = self.mapping(self.dst_node_name, self.src_node_name, 469cb5c6cd2SMax Reitz self.dst_bmap_name, self.src_bmap_name) 470cb5c6cd2SMax Reitz 471cb5c6cd2SMax Reitz # We would have to create this bitmap during migration, and 472cb5c6cd2SMax Reitz # that would fail, because the name is too long. Better to 473cb5c6cd2SMax Reitz # catch it early. 474cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, mapping, 475cb5c6cd2SMax Reitz f"Invalid mapping given for block-bitmap-mapping: " 476cb5c6cd2SMax Reitz f"The bitmap name '{self.dst_bmap_name}' is longer " 477cb5c6cd2SMax Reitz f"than 1023 bytes") 478cb5c6cd2SMax Reitz 479cb5c6cd2SMax Reitz def test_node_name_too_long(self) -> None: 480cb5c6cd2SMax Reitz # Longer than the maximum for node names 481cb5c6cd2SMax Reitz self.dst_node_name = 'a' * 32 482cb5c6cd2SMax Reitz 483cb5c6cd2SMax Reitz mapping = self.mapping(self.dst_node_name, self.src_node_name, 484cb5c6cd2SMax Reitz self.dst_bmap_name, self.src_bmap_name) 485cb5c6cd2SMax Reitz 486cb5c6cd2SMax Reitz # During migration, this would appear simply as a node that 487cb5c6cd2SMax Reitz # cannot be found. Still better to catch impossible node 488cb5c6cd2SMax Reitz # names early (similar to test_non_wellformed_node_alias). 489cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, mapping, 490cb5c6cd2SMax Reitz f"Invalid mapping given for block-bitmap-mapping: " 491cb5c6cd2SMax Reitz f"The node name '{self.dst_node_name}' is longer " 492cb5c6cd2SMax Reitz f"than 31 bytes") 493cb5c6cd2SMax Reitz 494cb5c6cd2SMax Reitz 495cb5c6cd2SMax Reitzclass TestCrossAliasMigration(TestDirtyBitmapMigration): 496cb5c6cd2SMax Reitz """ 497cb5c6cd2SMax Reitz Swap aliases, both to see that qemu does not get confused, and 498cb5c6cd2SMax Reitz that we can migrate multiple things at once. 499cb5c6cd2SMax Reitz 500cb5c6cd2SMax Reitz So we migrate this: 501cb5c6cd2SMax Reitz node-a.bmap-a -> node-b.bmap-b 502cb5c6cd2SMax Reitz node-a.bmap-b -> node-b.bmap-a 503cb5c6cd2SMax Reitz node-b.bmap-a -> node-a.bmap-b 504cb5c6cd2SMax Reitz node-b.bmap-b -> node-a.bmap-a 505cb5c6cd2SMax Reitz """ 506cb5c6cd2SMax Reitz 507cb5c6cd2SMax Reitz src_node_name = 'node-a' 508cb5c6cd2SMax Reitz dst_node_name = 'node-b' 509cb5c6cd2SMax Reitz src_bmap_name = 'bmap-a' 510cb5c6cd2SMax Reitz dst_bmap_name = 'bmap-b' 511cb5c6cd2SMax Reitz 512cb5c6cd2SMax Reitz def setUp(self) -> None: 513cb5c6cd2SMax Reitz TestDirtyBitmapMigration.setUp(self) 514cb5c6cd2SMax Reitz 515cb5c6cd2SMax Reitz # Now create another block device and let both have two bitmaps each 516*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('blockdev-add', 517cb5c6cd2SMax Reitz node_name='node-b', driver='null-co') 518cb5c6cd2SMax Reitz 519*b6aed193SVladimir Sementsov-Ogievskiy self.vm_b.cmd('blockdev-add', 520cb5c6cd2SMax Reitz node_name='node-a', driver='null-co') 521cb5c6cd2SMax Reitz 522cb5c6cd2SMax Reitz bmaps_to_add = (('node-a', 'bmap-b'), 523cb5c6cd2SMax Reitz ('node-b', 'bmap-a'), 524cb5c6cd2SMax Reitz ('node-b', 'bmap-b')) 525cb5c6cd2SMax Reitz 526cb5c6cd2SMax Reitz for (node, bmap) in bmaps_to_add: 527*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('block-dirty-bitmap-add', 528cb5c6cd2SMax Reitz node=node, name=bmap) 529cb5c6cd2SMax Reitz 530cb5c6cd2SMax Reitz @staticmethod 531cb5c6cd2SMax Reitz def cross_mapping() -> BlockBitmapMapping: 532cb5c6cd2SMax Reitz return [ 533cb5c6cd2SMax Reitz { 534cb5c6cd2SMax Reitz 'node-name': 'node-a', 535cb5c6cd2SMax Reitz 'alias': 'node-b', 536cb5c6cd2SMax Reitz 'bitmaps': [ 537cb5c6cd2SMax Reitz { 538cb5c6cd2SMax Reitz 'name': 'bmap-a', 539cb5c6cd2SMax Reitz 'alias': 'bmap-b' 540cb5c6cd2SMax Reitz }, 541cb5c6cd2SMax Reitz { 542cb5c6cd2SMax Reitz 'name': 'bmap-b', 543cb5c6cd2SMax Reitz 'alias': 'bmap-a' 544cb5c6cd2SMax Reitz } 545cb5c6cd2SMax Reitz ] 546cb5c6cd2SMax Reitz }, 547cb5c6cd2SMax Reitz { 548cb5c6cd2SMax Reitz 'node-name': 'node-b', 549cb5c6cd2SMax Reitz 'alias': 'node-a', 550cb5c6cd2SMax Reitz 'bitmaps': [ 551cb5c6cd2SMax Reitz { 552cb5c6cd2SMax Reitz 'name': 'bmap-b', 553cb5c6cd2SMax Reitz 'alias': 'bmap-a' 554cb5c6cd2SMax Reitz }, 555cb5c6cd2SMax Reitz { 556cb5c6cd2SMax Reitz 'name': 'bmap-a', 557cb5c6cd2SMax Reitz 'alias': 'bmap-b' 558cb5c6cd2SMax Reitz } 559cb5c6cd2SMax Reitz ] 560cb5c6cd2SMax Reitz } 561cb5c6cd2SMax Reitz ] 562cb5c6cd2SMax Reitz 563cb5c6cd2SMax Reitz def verify_dest_has_all_bitmaps(self) -> None: 564cb5c6cd2SMax Reitz bitmaps = self.vm_b.query_bitmaps() 565cb5c6cd2SMax Reitz 566cb5c6cd2SMax Reitz # Extract and sort bitmap names 567cb5c6cd2SMax Reitz for node in bitmaps: 568cb5c6cd2SMax Reitz bitmaps[node] = sorted((bmap['name'] for bmap in bitmaps[node])) 569cb5c6cd2SMax Reitz 570cb5c6cd2SMax Reitz self.assertEqual(bitmaps, 571cb5c6cd2SMax Reitz {'node-a': ['bmap-a', 'bmap-b'], 572cb5c6cd2SMax Reitz 'node-b': ['bmap-a', 'bmap-b']}) 573cb5c6cd2SMax Reitz 574cb5c6cd2SMax Reitz def test_alias_on_src(self) -> None: 575cb5c6cd2SMax Reitz self.set_mapping(self.vm_a, self.cross_mapping()) 576cb5c6cd2SMax Reitz 577cb5c6cd2SMax Reitz # Checks that node-a.bmap-a was migrated to node-b.bmap-b, and 578cb5c6cd2SMax Reitz # that is enough 579cb5c6cd2SMax Reitz self.migrate() 580cb5c6cd2SMax Reitz self.verify_dest_has_all_bitmaps() 581cb5c6cd2SMax Reitz self.verify_dest_error(None) 582cb5c6cd2SMax Reitz 583cb5c6cd2SMax Reitz def test_alias_on_dst(self) -> None: 584cb5c6cd2SMax Reitz self.set_mapping(self.vm_b, self.cross_mapping()) 585cb5c6cd2SMax Reitz 586cb5c6cd2SMax Reitz # Checks that node-a.bmap-a was migrated to node-b.bmap-b, and 587cb5c6cd2SMax Reitz # that is enough 588cb5c6cd2SMax Reitz self.migrate() 589cb5c6cd2SMax Reitz self.verify_dest_has_all_bitmaps() 590cb5c6cd2SMax Reitz self.verify_dest_error(None) 591cb5c6cd2SMax Reitz 592ca4bfec4SPeter Krempaclass TestAliasTransformMigration(TestDirtyBitmapMigration): 593ca4bfec4SPeter Krempa """ 5944aa6fc69SEric Blake Tests the 'transform' option which modifies bitmap persistence on 5954aa6fc69SEric Blake migration. 596ca4bfec4SPeter Krempa """ 597ca4bfec4SPeter Krempa 598ca4bfec4SPeter Krempa src_node_name = 'node-a' 599ca4bfec4SPeter Krempa dst_node_name = 'node-b' 600ca4bfec4SPeter Krempa src_bmap_name = 'bmap-a' 601ca4bfec4SPeter Krempa dst_bmap_name = 'bmap-b' 602ca4bfec4SPeter Krempa 603ca4bfec4SPeter Krempa def setUp(self) -> None: 604ca4bfec4SPeter Krempa TestDirtyBitmapMigration.setUp(self) 605ca4bfec4SPeter Krempa 606ca4bfec4SPeter Krempa # Now create another block device and let both have two bitmaps each 607*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('blockdev-add', 608ca4bfec4SPeter Krempa node_name='node-b', driver='null-co', 609ca4bfec4SPeter Krempa read_zeroes=False) 610ca4bfec4SPeter Krempa 611*b6aed193SVladimir Sementsov-Ogievskiy self.vm_b.cmd('blockdev-add', 612ca4bfec4SPeter Krempa node_name='node-a', driver='null-co', 613ca4bfec4SPeter Krempa read_zeroes=False) 614ca4bfec4SPeter Krempa 615ca4bfec4SPeter Krempa bmaps_to_add = (('node-a', 'bmap-b'), 616ca4bfec4SPeter Krempa ('node-b', 'bmap-a'), 617ca4bfec4SPeter Krempa ('node-b', 'bmap-b')) 618ca4bfec4SPeter Krempa 619ca4bfec4SPeter Krempa for (node, bmap) in bmaps_to_add: 620*b6aed193SVladimir Sementsov-Ogievskiy self.vm_a.cmd('block-dirty-bitmap-add', 621ca4bfec4SPeter Krempa node=node, name=bmap) 622ca4bfec4SPeter Krempa 623ca4bfec4SPeter Krempa @staticmethod 624ca4bfec4SPeter Krempa def transform_mapping() -> BlockBitmapMapping: 625ca4bfec4SPeter Krempa return [ 626ca4bfec4SPeter Krempa { 627ca4bfec4SPeter Krempa 'node-name': 'node-a', 628ca4bfec4SPeter Krempa 'alias': 'node-a', 629ca4bfec4SPeter Krempa 'bitmaps': [ 630ca4bfec4SPeter Krempa { 631ca4bfec4SPeter Krempa 'name': 'bmap-a', 632ca4bfec4SPeter Krempa 'alias': 'bmap-a', 633ca4bfec4SPeter Krempa 'transform': 634ca4bfec4SPeter Krempa { 635ca4bfec4SPeter Krempa 'persistent': True 636ca4bfec4SPeter Krempa } 637ca4bfec4SPeter Krempa }, 638ca4bfec4SPeter Krempa { 639ca4bfec4SPeter Krempa 'name': 'bmap-b', 640ca4bfec4SPeter Krempa 'alias': 'bmap-b' 641ca4bfec4SPeter Krempa } 642ca4bfec4SPeter Krempa ] 643ca4bfec4SPeter Krempa }, 644ca4bfec4SPeter Krempa { 645ca4bfec4SPeter Krempa 'node-name': 'node-b', 646ca4bfec4SPeter Krempa 'alias': 'node-b', 647ca4bfec4SPeter Krempa 'bitmaps': [ 648ca4bfec4SPeter Krempa { 649ca4bfec4SPeter Krempa 'name': 'bmap-a', 650ca4bfec4SPeter Krempa 'alias': 'bmap-a' 651ca4bfec4SPeter Krempa }, 652ca4bfec4SPeter Krempa { 653ca4bfec4SPeter Krempa 'name': 'bmap-b', 654ca4bfec4SPeter Krempa 'alias': 'bmap-b' 655ca4bfec4SPeter Krempa } 656ca4bfec4SPeter Krempa ] 657ca4bfec4SPeter Krempa } 658ca4bfec4SPeter Krempa ] 659ca4bfec4SPeter Krempa 660ca4bfec4SPeter Krempa def verify_dest_bitmap_state(self) -> None: 661ca4bfec4SPeter Krempa bitmaps = self.vm_b.query_bitmaps() 662ca4bfec4SPeter Krempa 663ca4bfec4SPeter Krempa for node in bitmaps: 6644aa6fc69SEric Blake bitmaps[node] = sorted(((bmap['name'], bmap['persistent']) 6654aa6fc69SEric Blake for bmap in bitmaps[node])) 666ca4bfec4SPeter Krempa 667ca4bfec4SPeter Krempa self.assertEqual(bitmaps, 668ca4bfec4SPeter Krempa {'node-a': [('bmap-a', True), ('bmap-b', False)], 669ca4bfec4SPeter Krempa 'node-b': [('bmap-a', False), ('bmap-b', False)]}) 670ca4bfec4SPeter Krempa 671ca4bfec4SPeter Krempa def test_transform_on_src(self) -> None: 672ca4bfec4SPeter Krempa self.set_mapping(self.vm_a, self.transform_mapping()) 673ca4bfec4SPeter Krempa 674ca4bfec4SPeter Krempa self.migrate() 675ca4bfec4SPeter Krempa self.verify_dest_bitmap_state() 676ca4bfec4SPeter Krempa self.verify_dest_error(None) 677ca4bfec4SPeter Krempa 678ca4bfec4SPeter Krempa def test_transform_on_dst(self) -> None: 679ca4bfec4SPeter Krempa self.set_mapping(self.vm_b, self.transform_mapping()) 680ca4bfec4SPeter Krempa 681ca4bfec4SPeter Krempa self.migrate() 682ca4bfec4SPeter Krempa self.verify_dest_bitmap_state() 683ca4bfec4SPeter Krempa self.verify_dest_error(None) 684cb5c6cd2SMax Reitz 685cb5c6cd2SMax Reitzif __name__ == '__main__': 686cb5c6cd2SMax Reitz iotests.main(supported_protocols=['file']) 687