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