qemu/tests/functional/test_virtio_balloon.py
Thomas Huth 4dc11ee468 tests/functional/test_virtio_balloon: Only use KVM for running this test
The virtio_balloon test is currently hanging for unknown reasons
when being run on the shared gitlab CI runners (which don't provide
KVM, thus it's running in TCG mode there). All other functional tests
that use the same asset (the Fedora 31 kernel) have already been
marked to work only with KVM in the past, so those other tests are
skipped on the shared gitlab CI runners. As long as the problem isn't
fully understood and fixed, let's do the same with the virtio_balloon
test to avoid that the CI is failing here.

Message-ID: <20250307063904.1081961-1-thuth@redhat.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
2025-03-07 09:24:00 +01:00

178 lines
6.3 KiB
Python
Executable file

#!/usr/bin/env python3
#
# virtio-balloon tests
#
# This work is licensed under the terms of the GNU GPL, version 2 or
# later. See the COPYING file in the top-level directory.
import time
from qemu_test import QemuSystemTest, Asset
from qemu_test import wait_for_console_pattern
from qemu_test import exec_command_and_wait_for_pattern
UNSET_STATS_VALUE = 18446744073709551615
class VirtioBalloonx86(QemuSystemTest):
ASSET_KERNEL = Asset(
('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
'/31/Server/x86_64/os/images/pxeboot/vmlinuz'),
'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129')
ASSET_INITRD = Asset(
('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
'/31/Server/x86_64/os/images/pxeboot/initrd.img'),
'277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b')
ASSET_DISKIMAGE = Asset(
('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
'/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'),
'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0')
DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 '
'rd.rescue quiet')
def wait_for_console_pattern(self, success_message, vm=None):
wait_for_console_pattern(
self,
success_message,
failure_message="Kernel panic - not syncing",
vm=vm,
)
def mount_root(self):
self.wait_for_console_pattern('Entering emergency mode.')
prompt = '# '
self.wait_for_console_pattern(prompt)
# Synchronize on virtio-block driver creating the root device
exec_command_and_wait_for_pattern(self,
"while ! (dmesg -c | grep vda:) ; do sleep 1 ; done",
"vda1")
exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot',
prompt)
exec_command_and_wait_for_pattern(self, 'chroot /sysroot',
prompt)
exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon",
prompt)
def assert_initial_stats(self):
ret = self.vm.qmp('qom-get',
{'path': '/machine/peripheral/balloon',
'property': 'guest-stats'})['return']
when = ret.get('last-update')
assert when == 0
stats = ret.get('stats')
for name, val in stats.items():
assert val == UNSET_STATS_VALUE
def assert_running_stats(self, then):
# We told the QEMU to refresh stats every 100ms, but
# there can be a delay between virtio-ballon driver
# being modprobed and seeing the first stats refresh
# Retry a few times for robustness under heavy load
retries = 10
when = 0
while when == 0 and retries:
ret = self.vm.qmp('qom-get',
{'path': '/machine/peripheral/balloon',
'property': 'guest-stats'})['return']
when = ret.get('last-update')
if when == 0:
retries = retries - 1
time.sleep(0.5)
now = time.time()
assert when > then and when < now
stats = ret.get('stats')
# Stat we expect this particular Kernel to have set
expectData = [
"stat-available-memory",
"stat-disk-caches",
"stat-free-memory",
"stat-htlb-pgalloc",
"stat-htlb-pgfail",
"stat-major-faults",
"stat-minor-faults",
"stat-swap-in",
"stat-swap-out",
"stat-total-memory",
]
for name, val in stats.items():
if name in expectData:
assert val != UNSET_STATS_VALUE
else:
assert val == UNSET_STATS_VALUE
def test_virtio_balloon_stats(self):
self.set_machine('q35')
self.require_accelerator("kvm")
kernel_path = self.ASSET_KERNEL.fetch()
initrd_path = self.ASSET_INITRD.fetch()
diskimage_path = self.ASSET_DISKIMAGE.fetch()
self.vm.set_console()
self.vm.add_args("-S")
self.vm.add_args("-cpu", "max")
self.vm.add_args("-m", "2G")
# Slow down BIOS phase with boot menu, so that after a system
# reset, we can reliably catch the clean stats again in BIOS
# phase before the guest OS launches
self.vm.add_args("-boot", "menu=on")
self.vm.add_args("-accel", "kvm")
self.vm.add_args("-device", "virtio-balloon,id=balloon")
self.vm.add_args('-drive',
f'file={diskimage_path},if=none,id=drv0,snapshot=on')
self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' +
'drive=drv0,id=virtio-disk0,bootindex=1')
self.vm.add_args(
"-kernel",
kernel_path,
"-initrd",
initrd_path,
"-append",
self.DEFAULT_KERNEL_PARAMS
)
self.vm.launch()
# Poll stats at 100ms
self.vm.qmp('qom-set',
{'path': '/machine/peripheral/balloon',
'property': 'guest-stats-polling-interval',
'value': 100 })
# We've not run any guest code yet, neither BIOS or guest,
# so stats should be all default values
self.assert_initial_stats()
self.vm.qmp('cont')
then = time.time()
self.mount_root()
self.assert_running_stats(then)
# Race window between these two commands, where we
# rely on '-boot menu=on' to (hopefully) ensure we're
# still executing the BIOS when QEMU processes the
# 'stop', and thus have not loaded the virtio-balloon
# driver in the guest
self.vm.qmp('system_reset')
self.vm.qmp('stop')
# If the above assumption held, we're in BIOS now and
# stats should be all back at their default values
self.assert_initial_stats()
self.vm.qmp('cont')
then = time.time()
self.mount_root()
self.assert_running_stats(then)
if __name__ == '__main__':
QemuSystemTest.main()