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