Skip to content

Commit c9269f4

Browse files
committed
qemu_guest_agent: Add new api support 'guest-get-load'
Add a new guest agent command 'guest-get-load' to get cpu load average info of Guest. Signed-off-by: Dehan Meng <[email protected]>
1 parent 0874a20 commit c9269f4

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

qemu/tests/cfg/qemu_guest_agent.cfg

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,14 @@
655655
remove_image_image1 = yes
656656
cmd_run_debugview = 'start WIN_UTILS:\Debugviewconsole.exe -d C:\debug.dbglog'
657657
cmd_check_string_VSS = 'type C:\debug.dbglog | findstr /i "QEMU Guest Agent VSS Provider\[[0-9]*\]"'
658+
- check_get_load:
659+
gagent_check_type = get_load
660+
cmd_get_load = "cat /proc/loadavg |awk '{print $1,$2,$3}'"
661+
cmd_install_stressng = "dnf -y install stress-ng"
662+
cmd_run_stressng = "stress-ng --cpu 8 --cpu-load 80 --timeout 30 --quiet &"
663+
Windows:
664+
cmd_get_load = "wmic cpu get loadpercentage | findstr /v LoadPercentage"
665+
cmd_get_load_2 = "wmic cpu get loadpercentage | findstr /i LoadPercentage"
658666
variants:
659667
- virtio_serial:
660668
gagent_serial_type = virtio

qemu/tests/qemu_guest_agent.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,130 @@ def ip_addr_check(session, mac_addr, ret_list, if_index, if_name):
18501850
if session_serial:
18511851
session_serial.close()
18521852

1853+
@error_context.context_aware
1854+
def gagent_check_get_load(self, test, params, env):
1855+
"""
1856+
Test guest-get-load command functionality.
1857+
1858+
Steps:
1859+
1) Get initial load values and verify qga/guest match
1860+
2) Start stress test and verify load increases
1861+
3) Stop stress test and verify load decreases
1862+
1863+
:param test: kvm test object
1864+
:param params: Dictionary with test parameters
1865+
:param env: Dictionary with test environment
1866+
"""
1867+
1868+
def _get_load_stats(session, get_guest=True):
1869+
"""
1870+
Get load statistics from either guest OS or QGA.
1871+
Returns tuple of (1min, 5min, 15min) load values.
1872+
"""
1873+
if get_guest:
1874+
try:
1875+
loads = session.cmd_output(params["cmd_get_load"]).strip().split()
1876+
return tuple(round(float(x), 2) for x in loads[:3])
1877+
except (IndexError, ValueError) as e:
1878+
test.error(f"Failed to get guest load stats: {e}")
1879+
else:
1880+
try:
1881+
loads = self.gagent.get_load()
1882+
load_keys = ("load1m", "load5m", "load15m")
1883+
return tuple(round(float(loads[k]), 2) for k in load_keys)
1884+
except (KeyError, ValueError) as e:
1885+
test.error(f"Failed to get QGA load stats: {e}")
1886+
1887+
def _verify_load_values(qga_vals, guest_vals, check_type="match"):
1888+
"""
1889+
Compare load values between QGA and guest OS.
1890+
Also verifies if values changed as expected.
1891+
"""
1892+
errors = []
1893+
periods = ["1-minute", "5-minute", "15-minute"]
1894+
1895+
for period, qga, guest in zip(periods, qga_vals, guest_vals):
1896+
if abs(qga - guest) > 0.5:
1897+
errors.append(
1898+
f"{period} load mismatch: guest={guest:.2f}, qga={qga:.2f}"
1899+
)
1900+
1901+
# Only check load1m for increase/decrease
1902+
if check_type != "match" and prev_values:
1903+
qga_1m = qga_vals[0]
1904+
guest_1m = guest_vals[0]
1905+
prev_qga_1m = prev_values["qga"][0]
1906+
prev_guest_1m = prev_values["guest"][0]
1907+
1908+
if check_type == "increase":
1909+
if qga_1m <= prev_qga_1m or guest_1m <= prev_guest_1m:
1910+
errors.append(
1911+
"1-minute load did not increase as expected:\n"
1912+
f"QGA: {prev_qga_1m:.2f} -> {qga_1m:.2f}\n"
1913+
f"Guest: {prev_guest_1m:.2f} -> {guest_1m:.2f}"
1914+
)
1915+
elif check_type == "decrease":
1916+
if qga_1m >= prev_qga_1m or guest_1m >= prev_guest_1m:
1917+
errors.append(
1918+
"1-minute load did not decrease as expected:\n"
1919+
f"QGA: {prev_qga_1m:.2f} -> {qga_1m:.2f}\n"
1920+
f"Guest: {prev_guest_1m:.2f} -> {guest_1m:.2f}"
1921+
)
1922+
1923+
return errors
1924+
1925+
def _log_load_values(guest_vals, qga_vals, phase):
1926+
"""Log load values in a consistent format"""
1927+
LOG_JOB.info(
1928+
"%s load averages:\nGuest OS: %s\nQGA: %s",
1929+
phase,
1930+
[f"{x:.2f}" for x in guest_vals],
1931+
[f"{x:.2f}" for x in qga_vals],
1932+
)
1933+
1934+
session = self._get_session(params, self.vm)
1935+
self._open_session_list.append(session)
1936+
prev_values = None
1937+
1938+
# Initial load check
1939+
error_context.context("Check initial load average info", LOG_JOB.info)
1940+
guest_vals = _get_load_stats(session)
1941+
qga_vals = _get_load_stats(session, False)
1942+
prev_values = {"guest": guest_vals, "qga": qga_vals}
1943+
1944+
_log_load_values(guest_vals, qga_vals, "Initial")
1945+
1946+
if errors := _verify_load_values(qga_vals, guest_vals):
1947+
test.fail("Initial load check failed:\n" + "\n".join(errors))
1948+
1949+
# Stress test
1950+
error_context.context("Starting CPU stress test", LOG_JOB.info)
1951+
s, o = session.cmd_status_output(params["cmd_install_stressng"])
1952+
if s != 0:
1953+
test.error(f"Failed to install stress-ng: {o}")
1954+
session.cmd(params["cmd_run_stressng"])
1955+
time.sleep(25)
1956+
1957+
guest_vals = _get_load_stats(session)
1958+
qga_vals = _get_load_stats(session, False)
1959+
1960+
_log_load_values(guest_vals, qga_vals, "Under stress")
1961+
1962+
if errors := _verify_load_values(qga_vals, guest_vals, "increase"):
1963+
test.fail("Stress test load check failed:\n" + "\n".join(errors))
1964+
1965+
prev_values = {"guest": guest_vals, "qga": qga_vals}
1966+
1967+
# sleep (60) wait for the stress-ng terminated.
1968+
time.sleep(60)
1969+
guest_vals = _get_load_stats(session)
1970+
qga_vals = _get_load_stats(session, False)
1971+
1972+
_log_load_values(guest_vals, qga_vals, "After stress")
1973+
1974+
if errors := _verify_load_values(qga_vals, guest_vals, "decrease"):
1975+
test.fail("Post-stress load check failed:\n" + "\n".join(errors))
1976+
18531977
@error_context.context_aware
18541978
def gagent_check_reboot_shutdown(self, test, params, env):
18551979
"""

0 commit comments

Comments
 (0)