master
text 151 lines 5.33 KB
Raw
1 #!/usr/bin/env python3
2 # group: rw migration
3 #
4 # Migrate a VM with a BDS with backing nodes, which runs
5 # bdrv_invalidate_cache(), which for qcow2 and qed triggers reading the
6 # backing file string from the image header. Check whether this
7 # interferes with bdrv_backing_overridden().
8 #
9 # Copyright (C) 2022 Red Hat, Inc.
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 import json
26 import os
27 from typing import Optional
28
29 import iotests
30 from iotests import qemu_img_create, qemu_img_info
31
32
33 image_size = 1 * 1024 * 1024
34 imgs = [os.path.join(iotests.test_dir, f'{i}.img') for i in range(0, 4)]
35
36 mig_sock = os.path.join(iotests.sock_dir, 'mig.sock')
37
38
39 class TestPostMigrateFilename(iotests.QMPTestCase):
40 vm_s: Optional[iotests.VM] = None
41 vm_d: Optional[iotests.VM] = None
42
43 def setUp(self) -> None:
44 # Create backing chain of three images, where the backing file strings
45 # are json:{} filenames
46 qemu_img_create('-f', iotests.imgfmt, imgs[0], str(image_size))
47 for i in range(1, 3):
48 backing = {
49 'driver': iotests.imgfmt,
50 'file': {
51 'driver': 'file',
52 'filename': imgs[i - 1]
53 }
54 }
55 qemu_img_create('-f', iotests.imgfmt, '-F', iotests.imgfmt,
56 '-b', 'json:' + json.dumps(backing),
57 imgs[i], str(image_size))
58
59 def tearDown(self) -> None:
60 if self.vm_s is not None:
61 self.vm_s.shutdown()
62 if self.vm_d is not None:
63 self.vm_d.shutdown()
64
65 for img in imgs:
66 try:
67 os.remove(img)
68 except OSError:
69 pass
70 try:
71 os.remove(mig_sock)
72 except OSError:
73 pass
74
75 def test_migration(self) -> None:
76 """
77 Migrate a VM with the backing chain created in setUp() attached. At
78 the end of the migration process, the destination will run
79 bdrv_invalidate_cache(), which for some image formats (qcow2 and qed)
80 means the backing file string is re-read from the image header. If
81 this overwrites bs->auto_backing_file, doing so may cause
82 bdrv_backing_overridden() to become true: The image header reports a
83 json:{} filename, but when opening it, bdrv_refresh_filename() will
84 simplify it to a plain simple filename; and when bs->auto_backing_file
85 and bs->backing->bs->filename differ, bdrv_backing_overridden() becomes
86 true.
87 If bdrv_backing_overridden() is true, the BDS will be forced to get a
88 json:{} filename, which in general is not the end of the world, but not
89 great. Check whether that happens, i.e. whether migration changes the
90 node's filename.
91 """
92
93 blockdev = {
94 'node-name': 'node0',
95 'driver': iotests.imgfmt,
96 'file': {
97 'driver': 'file',
98 'filename': imgs[2]
99 }
100 }
101
102 self.vm_s = iotests.VM(path_suffix='a') \
103 .add_blockdev(json.dumps(blockdev))
104 self.vm_d = iotests.VM(path_suffix='b') \
105 .add_blockdev(json.dumps(blockdev)) \
106 .add_incoming(f'unix:{mig_sock}')
107
108 assert self.vm_s is not None
109 assert self.vm_d is not None
110
111 self.vm_s.launch()
112 self.vm_d.launch()
113
114 pre_mig_filename = self.vm_s.node_info('node0')['file']
115
116 self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}')
117
118 # Wait for migration to be done
119 self.vm_s.event_wait('STOP')
120 self.vm_d.event_wait('RESUME')
121
122 post_mig_filename = self.vm_d.node_info('node0')['file']
123
124 # Verify that the filename hasn't changed from before the migration
125 self.assertEqual(pre_mig_filename, post_mig_filename)
126
127 self.vm_s.shutdown()
128 self.vm_s = None
129
130 # For good measure, try creating an overlay and check its backing
131 # chain below. This is how the issue was originally found.
132 self.vm_d.cmd('blockdev-snapshot-sync',
133 format=iotests.imgfmt,
134 snapshot_file=imgs[3],
135 node_name='node0',
136 snapshot_node_name='node0-overlay')
137
138 self.vm_d.shutdown()
139 self.vm_d = None
140
141 # Check the newly created overlay's backing chain
142 chain = qemu_img_info('--backing-chain', imgs[3])
143 for index, image in enumerate(chain):
144 self.assertEqual(image['filename'], imgs[3 - index])
145
146
147 if __name__ == '__main__':
148 # These are the image formats that run their open() function from their
149 # .bdrv_co_invaliate_cache() implementations, so test them
150 iotests.main(supported_fmts=['qcow2', 'qed'],
151 supported_protocols=['file'])