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