master
py 178 lines 6.28 KB
Raw
1 #!/usr/bin/env python3
2 #
3 # virtio-balloon tests
4 #
5 # This work is licensed under the terms of the GNU GPL, version 2 or
6 # later. See the COPYING file in the top-level directory.
7
8 import time
9
10 from qemu_test import QemuSystemTest, Asset
11 from qemu_test import wait_for_console_pattern
12 from qemu_test import exec_command_and_wait_for_pattern
13
14 UNSET_STATS_VALUE = 18446744073709551615
15
16
17 class VirtioBalloonx86(QemuSystemTest):
18
19 ASSET_KERNEL = Asset(
20 ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
21 '/31/Server/x86_64/os/images/pxeboot/vmlinuz'),
22 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129')
23
24 ASSET_INITRD = Asset(
25 ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
26 '/31/Server/x86_64/os/images/pxeboot/initrd.img'),
27 '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b')
28
29 ASSET_DISKIMAGE = Asset(
30 ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
31 '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'),
32 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0')
33
34 DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 '
35 'rd.rescue quiet')
36
37 def wait_for_console_pattern(self, success_message, vm=None):
38 wait_for_console_pattern(
39 self,
40 success_message,
41 failure_message="Kernel panic - not syncing",
42 vm=vm,
43 )
44
45 def mount_root(self):
46 self.wait_for_console_pattern('Entering emergency mode.')
47 prompt = '# '
48 self.wait_for_console_pattern(prompt)
49
50 # Synchronize on virtio-block driver creating the root device
51 exec_command_and_wait_for_pattern(self,
52 "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done",
53 "vda1")
54
55 exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot',
56 prompt)
57 exec_command_and_wait_for_pattern(self, 'chroot /sysroot',
58 prompt)
59 exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon",
60 prompt)
61
62 def assert_initial_stats(self):
63 ret = self.vm.qmp('qom-get',
64 {'path': '/machine/peripheral/balloon',
65 'property': 'guest-stats'})['return']
66 when = ret.get('last-update')
67 assert when == 0
68 stats = ret.get('stats')
69 for _name, val in stats.items():
70 assert val == UNSET_STATS_VALUE
71
72 def assert_running_stats(self, then):
73 # We told the QEMU to refresh stats every 100ms, but
74 # there can be a delay between virtio-ballon driver
75 # being modprobed and seeing the first stats refresh
76 # Retry a few times for robustness under heavy load
77 retries = 10
78 when = 0
79 while when == 0 and retries:
80 ret = self.vm.qmp('qom-get',
81 {'path': '/machine/peripheral/balloon',
82 'property': 'guest-stats'})['return']
83 when = ret.get('last-update')
84 if when == 0:
85 retries = retries - 1
86 time.sleep(0.5)
87
88 now = time.time()
89
90 assert now > when > then
91 stats = ret.get('stats')
92 # Stat we expect this particular Kernel to have set
93 expect_data = [
94 "stat-available-memory",
95 "stat-disk-caches",
96 "stat-free-memory",
97 "stat-htlb-pgalloc",
98 "stat-htlb-pgfail",
99 "stat-major-faults",
100 "stat-minor-faults",
101 "stat-swap-in",
102 "stat-swap-out",
103 "stat-total-memory",
104 ]
105 for name, val in stats.items():
106 if name in expect_data:
107 assert val != UNSET_STATS_VALUE
108 else:
109 assert val == UNSET_STATS_VALUE
110
111 def test_virtio_balloon_stats(self):
112 self.set_machine('q35')
113 self.require_accelerator("kvm")
114 kernel_path = self.ASSET_KERNEL.fetch()
115 initrd_path = self.ASSET_INITRD.fetch()
116 diskimage_path = self.ASSET_DISKIMAGE.fetch()
117
118 self.vm.set_console()
119 self.vm.add_args("-S")
120 self.vm.add_args("-cpu", "max")
121 self.vm.add_args("-m", "2G")
122 # Slow down BIOS phase with boot menu, so that after a system
123 # reset, we can reliably catch the clean stats again in BIOS
124 # phase before the guest OS launches
125 self.vm.add_args("-boot", "menu=on")
126 self.vm.add_args("-accel", "kvm")
127 self.vm.add_args("-device", "virtio-balloon,id=balloon")
128 self.vm.add_args('-drive',
129 f'file={diskimage_path},if=none,id=drv0,snapshot=on')
130 self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' +
131 'drive=drv0,id=virtio-disk0,bootindex=1')
132
133 self.vm.add_args(
134 "-kernel",
135 kernel_path,
136 "-initrd",
137 initrd_path,
138 "-append",
139 self.DEFAULT_KERNEL_PARAMS
140 )
141 self.vm.launch()
142
143 # Poll stats at 100ms
144 self.vm.qmp('qom-set',
145 {'path': '/machine/peripheral/balloon',
146 'property': 'guest-stats-polling-interval',
147 'value': 100 })
148
149 # We've not run any guest code yet, neither BIOS or guest,
150 # so stats should be all default values
151 self.assert_initial_stats()
152
153 self.vm.qmp('cont')
154
155 then = time.time()
156 self.mount_root()
157 self.assert_running_stats(then)
158
159 # Race window between these two commands, where we
160 # rely on '-boot menu=on' to (hopefully) ensure we're
161 # still executing the BIOS when QEMU processes the
162 # 'stop', and thus have not loaded the virtio-balloon
163 # driver in the guest
164 self.vm.qmp('system_reset')
165 self.vm.qmp('stop')
166
167 # If the above assumption held, we're in BIOS now and
168 # stats should be all back at their default values
169 self.assert_initial_stats()
170 self.vm.qmp('cont')
171
172 then = time.time()
173 self.mount_root()
174 self.assert_running_stats(then)
175
176
177 if __name__ == '__main__':
178 QemuSystemTest.main()