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