xref: /qemu/tests/functional/test_virtio_balloon.py (revision e88a579392f74aa7658299f29dc43aca199e4533)
11456e906SDaniel P. Berrangé#!/usr/bin/env python3
21456e906SDaniel P. Berrangé#
31456e906SDaniel P. Berrangé# virtio-balloon tests
41456e906SDaniel P. Berrangé#
51456e906SDaniel P. Berrangé# This work is licensed under the terms of the GNU GPL, version 2 or
61456e906SDaniel P. Berrangé# later.  See the COPYING file in the top-level directory.
71456e906SDaniel P. Berrangé
81456e906SDaniel P. Berrangéimport time
91456e906SDaniel P. Berrangé
101456e906SDaniel P. Berrangéfrom qemu_test import QemuSystemTest, Asset
111456e906SDaniel P. Berrangéfrom qemu_test import wait_for_console_pattern
121456e906SDaniel P. Berrangéfrom qemu_test import exec_command_and_wait_for_pattern
131456e906SDaniel P. Berrangé
141456e906SDaniel P. BerrangéUNSET_STATS_VALUE = 18446744073709551615
151456e906SDaniel P. Berrangé
161456e906SDaniel P. Berrangé
171456e906SDaniel P. Berrangéclass VirtioBalloonx86(QemuSystemTest):
181456e906SDaniel P. Berrangé
191456e906SDaniel P. Berrangé    ASSET_KERNEL = Asset(
201456e906SDaniel P. Berrangé        ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
211456e906SDaniel P. Berrangé         '/31/Server/x86_64/os/images/pxeboot/vmlinuz'),
221456e906SDaniel P. Berrangé        'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129')
231456e906SDaniel P. Berrangé
241456e906SDaniel P. Berrangé    ASSET_INITRD = Asset(
251456e906SDaniel P. Berrangé        ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
261456e906SDaniel P. Berrangé         '/31/Server/x86_64/os/images/pxeboot/initrd.img'),
271456e906SDaniel P. Berrangé        '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b')
281456e906SDaniel P. Berrangé
291456e906SDaniel P. Berrangé    ASSET_DISKIMAGE = Asset(
301456e906SDaniel P. Berrangé        ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
311456e906SDaniel P. Berrangé         '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'),
321456e906SDaniel P. Berrangé        'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0')
331456e906SDaniel P. Berrangé
341456e906SDaniel P. Berrangé    DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 '
3584272158SDaniel P. Berrangé                             'rd.rescue quiet')
361456e906SDaniel P. Berrangé
371456e906SDaniel P. Berrangé    def wait_for_console_pattern(self, success_message, vm=None):
381456e906SDaniel P. Berrangé        wait_for_console_pattern(
391456e906SDaniel P. Berrangé            self,
401456e906SDaniel P. Berrangé            success_message,
411456e906SDaniel P. Berrangé            failure_message="Kernel panic - not syncing",
421456e906SDaniel P. Berrangé            vm=vm,
431456e906SDaniel P. Berrangé        )
441456e906SDaniel P. Berrangé
451456e906SDaniel P. Berrangé    def mount_root(self):
461456e906SDaniel P. Berrangé        self.wait_for_console_pattern('Entering emergency mode.')
471456e906SDaniel P. Berrangé        prompt = '# '
481456e906SDaniel P. Berrangé        self.wait_for_console_pattern(prompt)
491456e906SDaniel P. Berrangé
5084272158SDaniel P. Berrangé        # Synchronize on virtio-block driver creating the root device
5184272158SDaniel P. Berrangé        exec_command_and_wait_for_pattern(self,
5284272158SDaniel P. Berrangé                        "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done",
5384272158SDaniel P. Berrangé                        "vda1")
5484272158SDaniel P. Berrangé
551456e906SDaniel P. Berrangé        exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot',
561456e906SDaniel P. Berrangé                                          prompt)
571456e906SDaniel P. Berrangé        exec_command_and_wait_for_pattern(self, 'chroot /sysroot',
581456e906SDaniel P. Berrangé                                          prompt)
591456e906SDaniel P. Berrangé        exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon",
601456e906SDaniel P. Berrangé                                          prompt)
611456e906SDaniel P. Berrangé
621456e906SDaniel P. Berrangé    def assert_initial_stats(self):
631456e906SDaniel P. Berrangé        ret = self.vm.qmp('qom-get',
641456e906SDaniel P. Berrangé                          {'path': '/machine/peripheral/balloon',
651456e906SDaniel P. Berrangé                           'property': 'guest-stats'})['return']
661456e906SDaniel P. Berrangé        when = ret.get('last-update')
671456e906SDaniel P. Berrangé        assert when == 0
681456e906SDaniel P. Berrangé        stats = ret.get('stats')
691456e906SDaniel P. Berrangé        for name, val in stats.items():
701456e906SDaniel P. Berrangé            assert val == UNSET_STATS_VALUE
711456e906SDaniel P. Berrangé
721456e906SDaniel P. Berrangé    def assert_running_stats(self, then):
7384272158SDaniel P. Berrangé        # We told the QEMU to refresh stats every 100ms, but
7484272158SDaniel P. Berrangé        # there can be a delay between virtio-ballon driver
7584272158SDaniel P. Berrangé        # being modprobed and seeing the first stats refresh
7684272158SDaniel P. Berrangé        # Retry a few times for robustness under heavy load
7784272158SDaniel P. Berrangé        retries = 10
7884272158SDaniel P. Berrangé        when = 0
7984272158SDaniel P. Berrangé        while when == 0 and retries:
801456e906SDaniel P. Berrangé            ret = self.vm.qmp('qom-get',
811456e906SDaniel P. Berrangé                              {'path': '/machine/peripheral/balloon',
821456e906SDaniel P. Berrangé                               'property': 'guest-stats'})['return']
831456e906SDaniel P. Berrangé            when = ret.get('last-update')
8484272158SDaniel P. Berrangé            if when == 0:
8584272158SDaniel P. Berrangé                retries = retries - 1
8684272158SDaniel P. Berrangé                time.sleep(0.5)
8784272158SDaniel P. Berrangé
881456e906SDaniel P. Berrangé        now = time.time()
891456e906SDaniel P. Berrangé
901456e906SDaniel P. Berrangé        assert when > then and when < now
911456e906SDaniel P. Berrangé        stats = ret.get('stats')
921456e906SDaniel P. Berrangé        # Stat we expect this particular Kernel to have set
931456e906SDaniel P. Berrangé        expectData = [
941456e906SDaniel P. Berrangé            "stat-available-memory",
951456e906SDaniel P. Berrangé            "stat-disk-caches",
961456e906SDaniel P. Berrangé            "stat-free-memory",
971456e906SDaniel P. Berrangé            "stat-htlb-pgalloc",
981456e906SDaniel P. Berrangé            "stat-htlb-pgfail",
991456e906SDaniel P. Berrangé            "stat-major-faults",
1001456e906SDaniel P. Berrangé            "stat-minor-faults",
1011456e906SDaniel P. Berrangé            "stat-swap-in",
1021456e906SDaniel P. Berrangé            "stat-swap-out",
1031456e906SDaniel P. Berrangé            "stat-total-memory",
1041456e906SDaniel P. Berrangé        ]
1051456e906SDaniel P. Berrangé        for name, val in stats.items():
1061456e906SDaniel P. Berrangé            if name in expectData:
1071456e906SDaniel P. Berrangé                assert val != UNSET_STATS_VALUE
1081456e906SDaniel P. Berrangé            else:
1091456e906SDaniel P. Berrangé                assert val == UNSET_STATS_VALUE
1101456e906SDaniel P. Berrangé
1111456e906SDaniel P. Berrangé    def test_virtio_balloon_stats(self):
1121456e906SDaniel P. Berrangé        self.set_machine('q35')
113*4dc11ee4SThomas Huth        self.require_accelerator("kvm")
1141456e906SDaniel P. Berrangé        kernel_path = self.ASSET_KERNEL.fetch()
1151456e906SDaniel P. Berrangé        initrd_path = self.ASSET_INITRD.fetch()
1161456e906SDaniel P. Berrangé        diskimage_path = self.ASSET_DISKIMAGE.fetch()
1171456e906SDaniel P. Berrangé
1181456e906SDaniel P. Berrangé        self.vm.set_console()
1191456e906SDaniel P. Berrangé        self.vm.add_args("-S")
1201456e906SDaniel P. Berrangé        self.vm.add_args("-cpu", "max")
1211456e906SDaniel P. Berrangé        self.vm.add_args("-m", "2G")
1221456e906SDaniel P. Berrangé        # Slow down BIOS phase with boot menu, so that after a system
1231456e906SDaniel P. Berrangé        # reset, we can reliably catch the clean stats again in BIOS
1241456e906SDaniel P. Berrangé        # phase before the guest OS launches
1251456e906SDaniel P. Berrangé        self.vm.add_args("-boot", "menu=on")
126*4dc11ee4SThomas Huth        self.vm.add_args("-accel", "kvm")
1271456e906SDaniel P. Berrangé        self.vm.add_args("-device", "virtio-balloon,id=balloon")
1281456e906SDaniel P. Berrangé        self.vm.add_args('-drive',
1291456e906SDaniel P. Berrangé                         f'file={diskimage_path},if=none,id=drv0,snapshot=on')
1301456e906SDaniel P. Berrangé        self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' +
1311456e906SDaniel P. Berrangé                         'drive=drv0,id=virtio-disk0,bootindex=1')
1321456e906SDaniel P. Berrangé
1331456e906SDaniel P. Berrangé        self.vm.add_args(
1341456e906SDaniel P. Berrangé            "-kernel",
1351456e906SDaniel P. Berrangé            kernel_path,
1361456e906SDaniel P. Berrangé            "-initrd",
1371456e906SDaniel P. Berrangé            initrd_path,
1381456e906SDaniel P. Berrangé            "-append",
1391456e906SDaniel P. Berrangé            self.DEFAULT_KERNEL_PARAMS
1401456e906SDaniel P. Berrangé        )
1411456e906SDaniel P. Berrangé        self.vm.launch()
1421456e906SDaniel P. Berrangé
1431456e906SDaniel P. Berrangé        # Poll stats at 100ms
1441456e906SDaniel P. Berrangé        self.vm.qmp('qom-set',
1451456e906SDaniel P. Berrangé                    {'path': '/machine/peripheral/balloon',
1461456e906SDaniel P. Berrangé                     'property': 'guest-stats-polling-interval',
1471456e906SDaniel P. Berrangé                     'value': 100 })
1481456e906SDaniel P. Berrangé
1491456e906SDaniel P. Berrangé        # We've not run any guest code yet, neither BIOS or guest,
1501456e906SDaniel P. Berrangé        # so stats should be all default values
1511456e906SDaniel P. Berrangé        self.assert_initial_stats()
1521456e906SDaniel P. Berrangé
1531456e906SDaniel P. Berrangé        self.vm.qmp('cont')
1541456e906SDaniel P. Berrangé
1551456e906SDaniel P. Berrangé        then = time.time()
1561456e906SDaniel P. Berrangé        self.mount_root()
1571456e906SDaniel P. Berrangé        self.assert_running_stats(then)
1581456e906SDaniel P. Berrangé
1591456e906SDaniel P. Berrangé        # Race window between these two commands, where we
1601456e906SDaniel P. Berrangé        # rely on '-boot menu=on' to (hopefully) ensure we're
1611456e906SDaniel P. Berrangé        # still executing the BIOS when QEMU processes the
1621456e906SDaniel P. Berrangé        # 'stop', and thus have not loaded the virtio-balloon
1631456e906SDaniel P. Berrangé        # driver in the guest
1641456e906SDaniel P. Berrangé        self.vm.qmp('system_reset')
1651456e906SDaniel P. Berrangé        self.vm.qmp('stop')
1661456e906SDaniel P. Berrangé
1671456e906SDaniel P. Berrangé        # If the above assumption held, we're in BIOS now and
1681456e906SDaniel P. Berrangé        # stats should be all back at their default values
1691456e906SDaniel P. Berrangé        self.assert_initial_stats()
1701456e906SDaniel P. Berrangé        self.vm.qmp('cont')
1711456e906SDaniel P. Berrangé
1721456e906SDaniel P. Berrangé        then = time.time()
1731456e906SDaniel P. Berrangé        self.mount_root()
1741456e906SDaniel P. Berrangé        self.assert_running_stats(then)
1751456e906SDaniel P. Berrangé
1761456e906SDaniel P. Berrangé
1771456e906SDaniel P. Berrangéif __name__ == '__main__':
1781456e906SDaniel P. Berrangé    QemuSystemTest.main()
179