1283153f1SHanna Reitz#!/usr/bin/env python3 2283153f1SHanna Reitz# group: rw migration 3283153f1SHanna Reitz# 4283153f1SHanna Reitz# Migrate a VM with a BDS with backing nodes, which runs 5283153f1SHanna Reitz# bdrv_invalidate_cache(), which for qcow2 and qed triggers reading the 6283153f1SHanna Reitz# backing file string from the image header. Check whether this 7283153f1SHanna Reitz# interferes with bdrv_backing_overridden(). 8283153f1SHanna Reitz# 9283153f1SHanna Reitz# Copyright (C) 2022 Red Hat, Inc. 10283153f1SHanna Reitz# 11283153f1SHanna Reitz# This program is free software; you can redistribute it and/or modify 12283153f1SHanna Reitz# it under the terms of the GNU General Public License as published by 13283153f1SHanna Reitz# the Free Software Foundation; either version 2 of the License, or 14283153f1SHanna Reitz# (at your option) any later version. 15283153f1SHanna Reitz# 16283153f1SHanna Reitz# This program is distributed in the hope that it will be useful, 17283153f1SHanna Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 18283153f1SHanna Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19283153f1SHanna Reitz# GNU General Public License for more details. 20283153f1SHanna Reitz# 21283153f1SHanna Reitz# You should have received a copy of the GNU General Public License 22283153f1SHanna Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 23283153f1SHanna Reitz# 24283153f1SHanna Reitz 25283153f1SHanna Reitzimport json 26283153f1SHanna Reitzimport os 27283153f1SHanna Reitzfrom typing import Optional 28283153f1SHanna Reitz 29283153f1SHanna Reitzimport iotests 30283153f1SHanna Reitzfrom iotests import qemu_img_create, qemu_img_info 31283153f1SHanna Reitz 32283153f1SHanna Reitz 33283153f1SHanna Reitzimage_size = 1 * 1024 * 1024 34283153f1SHanna Reitzimgs = [os.path.join(iotests.test_dir, f'{i}.img') for i in range(0, 4)] 35283153f1SHanna Reitz 36283153f1SHanna Reitzmig_sock = os.path.join(iotests.sock_dir, 'mig.sock') 37283153f1SHanna Reitz 38283153f1SHanna Reitz 39283153f1SHanna Reitzclass TestPostMigrateFilename(iotests.QMPTestCase): 40283153f1SHanna Reitz vm_s: Optional[iotests.VM] = None 41283153f1SHanna Reitz vm_d: Optional[iotests.VM] = None 42283153f1SHanna Reitz 43283153f1SHanna Reitz def setUp(self) -> None: 44283153f1SHanna Reitz # Create backing chain of three images, where the backing file strings 45283153f1SHanna Reitz # are json:{} filenames 46283153f1SHanna Reitz qemu_img_create('-f', iotests.imgfmt, imgs[0], str(image_size)) 47283153f1SHanna Reitz for i in range(1, 3): 48283153f1SHanna Reitz backing = { 49283153f1SHanna Reitz 'driver': iotests.imgfmt, 50283153f1SHanna Reitz 'file': { 51283153f1SHanna Reitz 'driver': 'file', 52283153f1SHanna Reitz 'filename': imgs[i - 1] 53283153f1SHanna Reitz } 54283153f1SHanna Reitz } 55283153f1SHanna Reitz qemu_img_create('-f', iotests.imgfmt, '-F', iotests.imgfmt, 56283153f1SHanna Reitz '-b', 'json:' + json.dumps(backing), 57283153f1SHanna Reitz imgs[i], str(image_size)) 58283153f1SHanna Reitz 59283153f1SHanna Reitz def tearDown(self) -> None: 60283153f1SHanna Reitz if self.vm_s is not None: 61283153f1SHanna Reitz self.vm_s.shutdown() 62283153f1SHanna Reitz if self.vm_d is not None: 63283153f1SHanna Reitz self.vm_d.shutdown() 64283153f1SHanna Reitz 65283153f1SHanna Reitz for img in imgs: 66283153f1SHanna Reitz try: 67283153f1SHanna Reitz os.remove(img) 68283153f1SHanna Reitz except OSError: 69283153f1SHanna Reitz pass 70283153f1SHanna Reitz try: 71283153f1SHanna Reitz os.remove(mig_sock) 72283153f1SHanna Reitz except OSError: 73283153f1SHanna Reitz pass 74283153f1SHanna Reitz 75283153f1SHanna Reitz def test_migration(self) -> None: 76283153f1SHanna Reitz """ 77283153f1SHanna Reitz Migrate a VM with the backing chain created in setUp() attached. At 78283153f1SHanna Reitz the end of the migration process, the destination will run 79283153f1SHanna Reitz bdrv_invalidate_cache(), which for some image formats (qcow2 and qed) 80283153f1SHanna Reitz means the backing file string is re-read from the image header. If 81283153f1SHanna Reitz this overwrites bs->auto_backing_file, doing so may cause 82283153f1SHanna Reitz bdrv_backing_overridden() to become true: The image header reports a 83283153f1SHanna Reitz json:{} filename, but when opening it, bdrv_refresh_filename() will 84283153f1SHanna Reitz simplify it to a plain simple filename; and when bs->auto_backing_file 85283153f1SHanna Reitz and bs->backing->bs->filename differ, bdrv_backing_overridden() becomes 86283153f1SHanna Reitz true. 87283153f1SHanna Reitz If bdrv_backing_overridden() is true, the BDS will be forced to get a 88283153f1SHanna Reitz json:{} filename, which in general is not the end of the world, but not 89283153f1SHanna Reitz great. Check whether that happens, i.e. whether migration changes the 90283153f1SHanna Reitz node's filename. 91283153f1SHanna Reitz """ 92283153f1SHanna Reitz 93283153f1SHanna Reitz blockdev = { 94283153f1SHanna Reitz 'node-name': 'node0', 95283153f1SHanna Reitz 'driver': iotests.imgfmt, 96283153f1SHanna Reitz 'file': { 97283153f1SHanna Reitz 'driver': 'file', 98283153f1SHanna Reitz 'filename': imgs[2] 99283153f1SHanna Reitz } 100283153f1SHanna Reitz } 101283153f1SHanna Reitz 102283153f1SHanna Reitz self.vm_s = iotests.VM(path_suffix='a') \ 103283153f1SHanna Reitz .add_blockdev(json.dumps(blockdev)) 104283153f1SHanna Reitz self.vm_d = iotests.VM(path_suffix='b') \ 105283153f1SHanna Reitz .add_blockdev(json.dumps(blockdev)) \ 106283153f1SHanna Reitz .add_incoming(f'unix:{mig_sock}') 107283153f1SHanna Reitz 108283153f1SHanna Reitz assert self.vm_s is not None 109283153f1SHanna Reitz assert self.vm_d is not None 110283153f1SHanna Reitz 111283153f1SHanna Reitz self.vm_s.launch() 112283153f1SHanna Reitz self.vm_d.launch() 113283153f1SHanna Reitz 114283153f1SHanna Reitz pre_mig_filename = self.vm_s.node_info('node0')['file'] 115283153f1SHanna Reitz 116283153f1SHanna Reitz self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}') 117283153f1SHanna Reitz 118283153f1SHanna Reitz # Wait for migration to be done 119283153f1SHanna Reitz self.vm_s.event_wait('STOP') 120283153f1SHanna Reitz self.vm_d.event_wait('RESUME') 121283153f1SHanna Reitz 122283153f1SHanna Reitz post_mig_filename = self.vm_d.node_info('node0')['file'] 123283153f1SHanna Reitz 124283153f1SHanna Reitz # Verify that the filename hasn't changed from before the migration 125283153f1SHanna Reitz self.assertEqual(pre_mig_filename, post_mig_filename) 126283153f1SHanna Reitz 127283153f1SHanna Reitz self.vm_s.shutdown() 128283153f1SHanna Reitz self.vm_s = None 129283153f1SHanna Reitz 130283153f1SHanna Reitz # For good measure, try creating an overlay and check its backing 131283153f1SHanna Reitz # chain below. This is how the issue was originally found. 132*b6aed193SVladimir Sementsov-Ogievskiy self.vm_d.cmd('blockdev-snapshot-sync', 133283153f1SHanna Reitz format=iotests.imgfmt, 134283153f1SHanna Reitz snapshot_file=imgs[3], 135283153f1SHanna Reitz node_name='node0', 136283153f1SHanna Reitz snapshot_node_name='node0-overlay') 137283153f1SHanna Reitz 138283153f1SHanna Reitz self.vm_d.shutdown() 139283153f1SHanna Reitz self.vm_d = None 140283153f1SHanna Reitz 141283153f1SHanna Reitz # Check the newly created overlay's backing chain 142283153f1SHanna Reitz chain = qemu_img_info('--backing-chain', imgs[3]) 143283153f1SHanna Reitz for index, image in enumerate(chain): 144283153f1SHanna Reitz self.assertEqual(image['filename'], imgs[3 - index]) 145283153f1SHanna Reitz 146283153f1SHanna Reitz 147283153f1SHanna Reitzif __name__ == '__main__': 148283153f1SHanna Reitz # These are the image formats that run their open() function from their 149283153f1SHanna Reitz # .bdrv_co_invaliate_cache() implementations, so test them 150283153f1SHanna Reitz iotests.main(supported_fmts=['qcow2', 'qed'], 151283153f1SHanna Reitz supported_protocols=['file']) 152