@@ -1214,7 +1214,7 @@ def gagent_check_fstrim(self, test, params, env):
1214
1214
Execute "guest-fstrim" command to guest agent
1215
1215
:param test: kvm test object
1216
1216
:param params: Dictionary with the test parameters
1217
- :param env: Dictionary with test environment.
1217
+ :param env: Dictionary with the test environment.
1218
1218
1219
1219
"""
1220
1220
@@ -2072,6 +2072,166 @@ def parse_ipv6_route(route_output):
2072
2072
if session :
2073
2073
session .close ()
2074
2074
2075
+ @error_context .context_aware
2076
+ def gagent_check_get_load (self , test , params , env ):
2077
+ """
2078
+ Test guest-get-load command functionality.
2079
+
2080
+ Steps:
2081
+ 1) Get initial load values and verify qga/guest match
2082
+ 2) Start stress test and verify load increases
2083
+ 3) Stop stress test and verify load decreases
2084
+
2085
+ :param test: kvm test object
2086
+ :param params: Dictionary with test parameters
2087
+ :param env: Dictionary with test environment
2088
+ """
2089
+
2090
+ def _get_load_stats (session , get_guest = True ):
2091
+ """
2092
+ Get load statistics from either guest OS or QGA.
2093
+ Returns tuple of (1min, 5min, 15min) load values.
2094
+ """
2095
+ if get_guest :
2096
+ try :
2097
+ loads = session .cmd_output (params ["cmd_get_load" ]).strip ().split ()
2098
+ return tuple (round (float (x ), 2 ) for x in loads [:3 ])
2099
+ except (IndexError , ValueError ) as e :
2100
+ test .error (f"Failed to get guest load stats: { e } " )
2101
+ else :
2102
+ try :
2103
+ loads = self .gagent .get_load ()
2104
+ load_keys = ("load1m" , "load5m" , "load15m" )
2105
+ return tuple (round (float (loads [k ]), 2 ) for k in load_keys )
2106
+ except (KeyError , ValueError ) as e :
2107
+ test .error (f"Failed to get QGA load stats: { e } " )
2108
+
2109
+ def _verify_load_values (qga_vals , guest_vals , check_type = "match" ):
2110
+ """
2111
+ Compare load values between QGA and guest OS.
2112
+ Also verifies if values changed as expected.
2113
+ """
2114
+ errors = []
2115
+ periods = ["1-minute" , "5-minute" , "15-minute" ]
2116
+
2117
+ for period , qga , guest in zip (periods , qga_vals , guest_vals ):
2118
+ if abs (qga - guest ) > 0.5 :
2119
+ errors .append (
2120
+ f"{ period } load mismatch: guest={ guest :.2f} , qga={ qga :.2f} "
2121
+ )
2122
+
2123
+ # Only check load1m for increase/decrease
2124
+ if check_type != "match" and prev_values :
2125
+ qga_1m = qga_vals [0 ]
2126
+ guest_1m = guest_vals [0 ]
2127
+ prev_qga_1m = prev_values ["qga" ][0 ]
2128
+ prev_guest_1m = prev_values ["guest" ][0 ]
2129
+
2130
+ if check_type == "increase" :
2131
+ if qga_1m <= prev_qga_1m or guest_1m <= prev_guest_1m :
2132
+ errors .append (
2133
+ "1-minute load did not increase as expected:\n "
2134
+ f"QGA: { prev_qga_1m :.2f} -> { qga_1m :.2f} \n "
2135
+ f"Guest: { prev_guest_1m :.2f} -> { guest_1m :.2f} "
2136
+ )
2137
+ elif check_type == "decrease" :
2138
+ if qga_1m >= prev_qga_1m or guest_1m >= prev_guest_1m :
2139
+ errors .append (
2140
+ "1-minute load did not decrease as expected:\n "
2141
+ f"QGA: { prev_qga_1m :.2f} -> { qga_1m :.2f} \n "
2142
+ f"Guest: { prev_guest_1m :.2f} -> { guest_1m :.2f} "
2143
+ )
2144
+
2145
+ return errors
2146
+
2147
+ def _log_load_values (guest_vals , qga_vals , phase ):
2148
+ """Log load values in a consistent format"""
2149
+ LOG_JOB .info (
2150
+ "%s load averages:\n Guest OS: %s\n QGA: %s" ,
2151
+ phase ,
2152
+ [f"{ x :.2f} " for x in guest_vals ],
2153
+ [f"{ x :.2f} " for x in qga_vals ],
2154
+ )
2155
+
2156
+ session = self ._get_session (params , self .vm )
2157
+ self ._open_session_list .append (session )
2158
+ prev_values = None
2159
+
2160
+ if params .get ("os_type" ) == "windows" :
2161
+ error_context .context ("Get load info for Windows" , LOG_JOB .info )
2162
+ try :
2163
+ # Get initial load values
2164
+ load_info = self .gagent .get_load ()
2165
+ # Check if all required fields exist
2166
+ for key in ["load1m" , "load5m" , "load15m" ]:
2167
+ if key not in load_info :
2168
+ test .fail (f"Missing { key } in guest-get-load return value" )
2169
+ initial_load = load_info ["load1m" ]
2170
+ LOG_JOB .info ("Initial load info from guest-agent: %s" , load_info )
2171
+
2172
+ # Start CPU stress test
2173
+ error_context .context ("Start CPU stress test" , LOG_JOB .info )
2174
+ session .cmd (params ["cmd_run_stress" ])
2175
+ time .sleep (10 )
2176
+
2177
+ # Get load values after stress
2178
+ load_info = self .gagent .get_load ()
2179
+ stress_load = load_info ["load1m" ]
2180
+ LOG_JOB .info ("Load info after stress: %s" , load_info )
2181
+
2182
+ # Verify load value changed
2183
+ if stress_load <= initial_load :
2184
+ test .fail (
2185
+ f"Load value did not increase after CPU stress:"
2186
+ f" before={ initial_load } , after={ stress_load } "
2187
+ )
2188
+ LOG_JOB .info (
2189
+ "Load value increased as expected: before=%s, after=%s" ,
2190
+ initial_load ,
2191
+ stress_load ,
2192
+ )
2193
+ except guest_agent .VAgentCmdError as e :
2194
+ test .fail (f"guest-get-load command failed: { e } " )
2195
+ else :
2196
+ # Initial load check
2197
+ error_context .context ("Check initial load average info" , LOG_JOB .info )
2198
+ guest_vals = _get_load_stats (session )
2199
+ qga_vals = _get_load_stats (session , False )
2200
+ prev_values = {"guest" : guest_vals , "qga" : qga_vals }
2201
+
2202
+ _log_load_values (guest_vals , qga_vals , "Initial" )
2203
+
2204
+ if errors := _verify_load_values (qga_vals , guest_vals ):
2205
+ test .fail ("Initial load check failed:\n " + "\n " .join (errors ))
2206
+
2207
+ # Stress test
2208
+ error_context .context ("Starting CPU stress test" , LOG_JOB .info )
2209
+ s , o = session .cmd_status_output (params ["cmd_install_stressng" ])
2210
+ if s != 0 :
2211
+ test .error (f"Failed to install stress-ng: { o } " )
2212
+ session .cmd (params ["cmd_run_stress" ])
2213
+ time .sleep (25 )
2214
+
2215
+ guest_vals = _get_load_stats (session )
2216
+ qga_vals = _get_load_stats (session , False )
2217
+
2218
+ _log_load_values (guest_vals , qga_vals , "Under stress" )
2219
+
2220
+ if errors := _verify_load_values (qga_vals , guest_vals , "increase" ):
2221
+ test .fail ("Stress test load check failed:\n " + "\n " .join (errors ))
2222
+
2223
+ prev_values = {"guest" : guest_vals , "qga" : qga_vals }
2224
+
2225
+ # sleep (60) wait for the stress-ng terminated.
2226
+ time .sleep (60 )
2227
+ guest_vals = _get_load_stats (session )
2228
+ qga_vals = _get_load_stats (session , False )
2229
+
2230
+ _log_load_values (guest_vals , qga_vals , "After stress" )
2231
+
2232
+ if errors := _verify_load_values (qga_vals , guest_vals , "decrease" ):
2233
+ test .fail ("Post-stress load check failed:\n " + "\n " .join (errors ))
2234
+
2075
2235
@error_context .context_aware
2076
2236
def gagent_check_reboot_shutdown (self , test , params , env ):
2077
2237
"""
@@ -2273,7 +2433,7 @@ def gagent_check_file_write(self, test, params, env):
2273
2433
2274
2434
:param test: kvm test object
2275
2435
:param params: Dictionary with the test parameters
2276
- :param env: Dictionary with test environment.
2436
+ :param env: Dictionary with the test environment.
2277
2437
"""
2278
2438
error_context .context (
2279
2439
"Change guest-file related cmd to white list and get guest file name."
@@ -2894,7 +3054,7 @@ def gagent_check_fsfreeze(self, test, params, env):
2894
3054
2895
3055
:param test: kvm test object
2896
3056
:param params: Dictionary with the test parameters
2897
- :param env: Dictionary with test environmen.
3057
+ :param env: Dictionary with the test environmen.
2898
3058
"""
2899
3059
session = self ._get_session (params , self .vm )
2900
3060
self ._open_session_list .append (session )
@@ -2926,7 +3086,7 @@ def gagent_check_fsfreeze_list(self, test, params, env):
2926
3086
2927
3087
:param test: kvm test object
2928
3088
:param params: Dictionary with the test parameters
2929
- :param env: Dictionary with test environmen.
3089
+ :param env: Dictionary with the test environmen.
2930
3090
"""
2931
3091
session = self ._get_session (params , self .vm )
2932
3092
self ._open_session_list .append (session )
@@ -3019,7 +3179,7 @@ def gagent_check_thaw_unfrozen(self, test, params, env):
3019
3179
3020
3180
:param test: kvm test object
3021
3181
:param params: Dictionary with the test parameters
3022
- :param env: Dictionary with test environment.
3182
+ :param env: Dictionary with the test environment
3023
3183
"""
3024
3184
error_context .context ("Verify if FS is thawed" , LOG_JOB .info )
3025
3185
expect_status = self .gagent .FSFREEZE_STATUS_THAWED
@@ -3041,7 +3201,7 @@ def gagent_check_freeze_frozen(self, test, params, env):
3041
3201
3042
3202
:param test: kvm test object
3043
3203
:param params: Dictionary with the test parameters
3044
- :param env: Dictionary with test environment.
3204
+ :param env: Dictionary with the test environment
3045
3205
"""
3046
3206
# Since Qemu9.1, the report message changes.
3047
3207
qga_ver = self .gagent .guest_info ()["version" ].split ("." )
@@ -3498,7 +3658,7 @@ def gagent_check_vss_status(self, test, params, env):
3498
3658
3499
3659
:param test: kvm test object
3500
3660
:param params: Dictionary with the test parameters
3501
- :param env: Dictionary with test environment
3661
+ :param env: Dictionary with test environment.
3502
3662
"""
3503
3663
3504
3664
def check_vss_info (cmd_type , key , expect_value ):
@@ -4867,7 +5027,7 @@ def gagent_check_resource_leak(self, test, params, env):
4867
5027
4868
5028
:param test: kvm test object
4869
5029
:param params: Dictionary with the test parameters
4870
- :param env: Dictionary with test environment.
5030
+ :param env: Dictionary with the test environment.
4871
5031
"""
4872
5032
4873
5033
def execute_qga_cmds_loop ():
0 commit comments