blob: 4eccc801532ecec0d73d50e1f5e649bf45133b35 [file] [log] [blame]
#!/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 <http://www.gnu.org/licenses/>.
#
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'])