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