From 283153f10ab474cb2f14cc2b3f272edcf13aedaf Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Wed, 3 Aug 2022 16:44:46 +0200 Subject: [PATCH] iotests/backing-file-invalidation: Add new test Add a new test to see what happens when you migrate a VM with a backing chain that has json:{} backing file strings, which, when opened, will be resolved to plain filenames. Signed-off-by: Hanna Reitz Message-Id: <20220803144446.20723-4-hreitz@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- .../tests/backing-file-invalidation | 152 ++++++++++++++++++ .../tests/backing-file-invalidation.out | 5 + 2 files changed, 157 insertions(+) create mode 100755 tests/qemu-iotests/tests/backing-file-invalidation create mode 100644 tests/qemu-iotests/tests/backing-file-invalidation.out diff --git a/tests/qemu-iotests/tests/backing-file-invalidation b/tests/qemu-iotests/tests/backing-file-invalidation new file mode 100755 index 0000000000..4eccc80153 --- /dev/null +++ b/tests/qemu-iotests/tests/backing-file-invalidation @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# group: rw migration +# +# Migrate a VM with a BDS with backing nodes, which runs +# bdrv_invalidate_cache(), which for qcow2 and qed triggers reading the +# backing file string from the image header. Check whether this +# interferes with bdrv_backing_overridden(). +# +# Copyright (C) 2022 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import json +import os +from typing import Optional + +import iotests +from iotests import qemu_img_create, qemu_img_info + + +image_size = 1 * 1024 * 1024 +imgs = [os.path.join(iotests.test_dir, f'{i}.img') for i in range(0, 4)] + +mig_sock = os.path.join(iotests.sock_dir, 'mig.sock') + + +class TestPostMigrateFilename(iotests.QMPTestCase): + vm_s: Optional[iotests.VM] = None + vm_d: Optional[iotests.VM] = None + + def setUp(self) -> None: + # Create backing chain of three images, where the backing file strings + # are json:{} filenames + qemu_img_create('-f', iotests.imgfmt, imgs[0], str(image_size)) + for i in range(1, 3): + backing = { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': imgs[i - 1] + } + } + qemu_img_create('-f', iotests.imgfmt, '-F', iotests.imgfmt, + '-b', 'json:' + json.dumps(backing), + imgs[i], str(image_size)) + + def tearDown(self) -> None: + if self.vm_s is not None: + self.vm_s.shutdown() + if self.vm_d is not None: + self.vm_d.shutdown() + + for img in imgs: + try: + os.remove(img) + except OSError: + pass + try: + os.remove(mig_sock) + except OSError: + pass + + def test_migration(self) -> None: + """ + Migrate a VM with the backing chain created in setUp() attached. At + the end of the migration process, the destination will run + bdrv_invalidate_cache(), which for some image formats (qcow2 and qed) + means the backing file string is re-read from the image header. If + this overwrites bs->auto_backing_file, doing so may cause + bdrv_backing_overridden() to become true: The image header reports a + json:{} filename, but when opening it, bdrv_refresh_filename() will + simplify it to a plain simple filename; and when bs->auto_backing_file + and bs->backing->bs->filename differ, bdrv_backing_overridden() becomes + true. + If bdrv_backing_overridden() is true, the BDS will be forced to get a + json:{} filename, which in general is not the end of the world, but not + great. Check whether that happens, i.e. whether migration changes the + node's filename. + """ + + blockdev = { + 'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': imgs[2] + } + } + + self.vm_s = iotests.VM(path_suffix='a') \ + .add_blockdev(json.dumps(blockdev)) + self.vm_d = iotests.VM(path_suffix='b') \ + .add_blockdev(json.dumps(blockdev)) \ + .add_incoming(f'unix:{mig_sock}') + + assert self.vm_s is not None + assert self.vm_d is not None + + self.vm_s.launch() + self.vm_d.launch() + + pre_mig_filename = self.vm_s.node_info('node0')['file'] + + self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}') + + # Wait for migration to be done + self.vm_s.event_wait('STOP') + self.vm_d.event_wait('RESUME') + + post_mig_filename = self.vm_d.node_info('node0')['file'] + + # Verify that the filename hasn't changed from before the migration + self.assertEqual(pre_mig_filename, post_mig_filename) + + self.vm_s.shutdown() + self.vm_s = None + + # For good measure, try creating an overlay and check its backing + # chain below. This is how the issue was originally found. + result = self.vm_d.qmp('blockdev-snapshot-sync', + format=iotests.imgfmt, + snapshot_file=imgs[3], + node_name='node0', + snapshot_node_name='node0-overlay') + self.assert_qmp(result, 'return', {}) + + self.vm_d.shutdown() + self.vm_d = None + + # Check the newly created overlay's backing chain + chain = qemu_img_info('--backing-chain', imgs[3]) + for index, image in enumerate(chain): + self.assertEqual(image['filename'], imgs[3 - index]) + + +if __name__ == '__main__': + # These are the image formats that run their open() function from their + # .bdrv_co_invaliate_cache() implementations, so test them + iotests.main(supported_fmts=['qcow2', 'qed'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/backing-file-invalidation.out b/tests/qemu-iotests/tests/backing-file-invalidation.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/backing-file-invalidation.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK