@@ -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:\n Guest OS: %s\n QGA: %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