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