xref: /openbmc/qemu/tests/qemu-iotests/300 (revision cb5c6cd2)
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