@@ -1850,6 +1850,130 @@ def ip_addr_check(session, mac_addr, ret_list, if_index, if_name):
1850
1850
if session_serial :
1851
1851
session_serial .close ()
1852
1852
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
+
1853
1977
@error_context .context_aware
1854
1978
def gagent_check_reboot_shutdown (self , test , params , env ):
1855
1979
"""
0 commit comments