1*** Settings ***
2Documentation  Utilities for fan tests.
3
4Library        ../lib/bmc_ssh_utils.py
5Resource       ../lib/state_manager.robot
6Resource       ../lib/openbmc_ffdc_utils.robot
7Variables      ../data/variables.py
8
9*** Variables ***
10
11# Fan state values.
12${fan_functional}      ${1}
13${fan_nonfunctional}   ${0}
14
15# Criteria for a fan at maximum speed.
16${max_speed}=  ${10400}
17
18
19*** Keywords ***
20
21Is Water Cooled
22    [Documentation]  Return 1 if system is water cooled, 0 othersise.
23
24    ${water_cooled}=  Read Attribute
25    ...  ${HOST_INVENTORY_URI}system/chassis  WaterCooled
26    [Return]  ${water_cooled}
27
28
29Get Fan Names
30    [Documentation]  Get the names of the fans marked present in inventory.
31    [Arguments]  ${fan_names}
32    # This keyword populates the fan_names list with the names of
33    # fans present in inventory e.g. fan0, fan2, fan3.
34
35    # Description of Argument(s):
36    # fan_names   The list of fan names to which new fan names are to be
37    #             added to.  This list is returned to the caller.
38
39    ${fan_uris}=  Get Endpoint Paths  ${HOST_INVENTORY_URI}system  fan
40    FOR  ${fan_uri}  IN  @{fan_uris}
41        ${fan_properties}=  Read Properties  ${fan_uri}
42        ${fan_present}=  Get Variable Value  ${fan_properties['Present']}  0
43        ${fan_functional}=  Get Variable Value
44        ...  ${fan_properties['Functional']}  0
45        Continue For Loop If  ${fan_present} == 0 or ${fan_functional} == 0
46        ${remaining_uri}  ${fan_name}=  Split Path  ${fan_uri}
47        Append To List  ${fan_names}  ${fan_name}
48    END
49
50    [Return]  ${fan_names}
51
52
53Verify System Error Indication Due To Fans
54    [Documentation]  Verify enclosure LEDs are on and there's an error log.
55
56    # Both enclosure LEDs should now be On.
57    Verify Front And Rear LED State  On
58
59    # An error log should now exist.
60    Error Logs Should Exist
61
62
63Verify Front And Rear LED State
64    [Documentation]  Check state of the front and rear enclsure fault LEDs.
65    [Arguments]  ${state}
66    # Both LEDs should be in the specified state.  If not fail the test case.
67
68    # Description of Argument(s):
69    # state    The state to check for, either 'Off' or 'On'.
70
71    ${front_fault}=  Get System LED State  front_fault
72    ${rear_fault}=  Get System LED State  rear_fault
73
74    Run Keyword If
75    ...  '${front_fault}' != '${state}' or '${rear_fault}' != '${state}'
76    ...  Fail  msg=Expecting both enclosure LEDs to be ${state}.
77
78
79Set Fan State
80    [Documentation]  Set the fan state, either functional or non-functional.
81    [Arguments]  ${fan_name}  ${fan_state}
82
83    # Description of Argument(s):
84    # fan_name     The name of the fan, e.g. "fan2".
85    # fan_state    The state to set, 1 for functional, 2 for non-functional.
86
87    ${valueDict}=  Create Dictionary  data=${fan_state}
88    Write Attribute
89    ...  ${HOST_INVENTORY_URI}system/chassis/motherboard/${fan_name}
90    ...  Functional  data=${valueDict}
91
92
93Set Fan Target Speed
94    [Documentation]  Set the target speed of a fan.
95    [Arguments]  ${fan_name}  ${fan_speed}
96
97    # Description of argument(s):
98    # fan_name    The name of the fan (e.g. "fan0").
99    # fan_speed   The target speed to set (e.g. "9000").
100
101    ${valueDict}=  Create Dictionary  data=${fan_speed}
102    Write Attribute  ${SENSORS_URI}fan_tach/${fan_name}_0
103    ...  Target  data=${valueDict}
104
105
106Get Target Speed Of Fans
107    [Documentation]  Return the maximum target speed of the system fans.
108
109    ${max_target}=  Set Variable  0
110    ${paths}=  Get Endpoint Paths  ${SENSORS_URI}fan_tach/  0
111    FOR  ${path}  IN  @{paths}
112        ${response}=  OpenBMC Get Request  ${path}
113        ${target_speed}=  Set Variable  ${response.json()["data"]["Target"]}
114        ${max_target}=  Run Keyword If  ${target_speed} > ${max_target}
115        ...  Set Variable  ${target_speed}  ELSE  Set Variable  ${max_target}
116    END
117    [Return]  ${max_target}
118
119
120Get Target And Blade Speeds
121    [Documentation]  Return the fan target speed setting, the speed of the
122    ...  fan's clockwise blade, and the speed of the counter-clockwise blade.
123    # Each fan unit has two counter-rotating fan blades
124    # One blade is expected to be moving but the other blade may not be
125    # moving whenever the fan unit is transitioning to a new target speed.
126    [Arguments]  ${fan_name}
127
128    # Description of argument(s):
129    # fan_name       The name of a fan (e.g. "fan0")
130
131    # Get the fan target speed and the clockwise blade speed.
132    ${path}=  Catenate  ${SENSORS_URI}fan_tach/${fan_name}_0
133    ${response}=  OpenBMC Get Request  ${path}
134    ${fan_clockwise_speed}=  Set Variable  ${response.json()["data"]["Value"]}
135    ${target_speed}=  Set Variable  ${response.json["data"]["Target"]}
136
137    # Get the counter-clockwise blade speed.
138    ${path}=  Catenate  ${SENSORS_URI}fan_tach/${fan_name}_1
139    ${response}=  OpenBMC Get Request  ${path}
140    ${fan_counterclockwise_speed}=  Set Variable  ${response.json()["data"]["Value"]}
141
142    [Return]  ${target_speed}  ${fan_clockwise_speed}
143    ...  ${fan_counterclockwise_speed}
144
145
146Get Fan Target And Speed
147    [Documentation]  Return the fan target speed setting and the
148    ...  speed of the fastest blade.
149    [Arguments]  ${fan_name}
150
151    # Description of argument(s):
152    # fan_name       The name of a fan (e.g. "fan0")
153
154    ${target_speed}  ${clockwise_speed}  ${counterclockwise_speed}=
155    ...  Get Target And Blade Speeds  ${fan_name}
156    ${blade_speed}=  Run Keyword If
157    ...  ${clockwise_speed} > ${counterclockwise_speed}
158    ...  Set Variable  ${clockwise_speed}  ELSE
159    ...  Set Variable  ${counterclockwise_speed}
160    [Return]  ${target_speed}  ${blade_speed}
161
162
163Set Fan Daemon State
164    [Documentation]  Set the state of the fan control service.
165    [Arguments]  ${state}
166
167    # Description of argument(s):
168    # state     The desired state of the service, usually
169    #           "start", "stop", or "restart".
170
171    ${cmd}=  Catenate  systemctl  ${state}  phosphor-fan-control@0.service
172    ${stdout}  ${stderr}  ${rc}=  BMC Execute Command  ${cmd}
173
174
175Verify Minimum Number Of Fans With Cooling Type
176    [Documentation]  Verify minimum number of fans.
177    [Arguments]  ${num_fans}  ${water_cooled}
178
179    # Description of argument(s):
180    # num_fans       The number of fans present in the system.
181    # water_cooled   The value 1 if the system is water cooled,
182    #                0 if air cooled.
183
184    # For a water cooled system.
185    ${min_fans_water}=  Set Variable  2
186
187    # For an air cooled system.
188    ${min_fans_air}=  Set Variable  3
189
190    Printn
191    Rpvars  num_fans  water_cooled
192
193    # If water cooled must have at least min_fans_water fans, otherwise
194    # issue Fatal Error and terminate testing.
195    Run Keyword If  ${water_cooled} == 1 and ${num_fans} < ${min_fans_water}
196    ...  Fatal Error
197    ...  msg=Water cooled but less than ${min_fans_water} fans present.
198
199    # If air cooled must have at least min_fans_air fans.
200    Run Keyword If  ${water_cooled} == 0 and ${num_fans} < ${min_fans_air}
201    ...  Fatal Error
202    ...  msg=Air cooled but less than ${min_fans_air} fans present.
203
204
205Verify Fan Monitors With State
206    [Documentation]  Verify fan monitor daemons in the system state.
207    [Arguments]  ${power_state}
208    # The number of monitoring daemons is dependent upon the system
209    # power state.  If power is off there should be 0, if power
210    # is on there should be several.
211
212    # Description of argument(s):
213    # power_state   Power staet of the system, either "On" or "Off"
214
215    ${cmd}=  Catenate  systemctl list-units | grep phosphor-fan | wc -l
216    ${num_fan_daemons}  ${stderr}  ${rc}=  BMC Execute Command  ${cmd}
217
218    Rpvars  power_state  num_fan_daemons
219
220    # Fail if system is On and there are no fan monitors.
221    Run Keyword If  '${power_state}' == 'On' and ${num_fan_daemons} == 0
222    ...  Fail  msg=No phosphor-fan monitors found at power on.
223
224    # Fail if system is Off and the fan monitors are present.
225    Run Keyword If  '${power_state}' == 'Off' and ${num_fan_daemons} != 0
226    ...  Fail  msg=Phosphor-fan monitors found at power off.
227
228
229Get Fan Count And Names
230    [Documentation]  Return the number of fans and the fan names.
231
232    # The @{fan_names} list holds the names of the fans in the system.
233    @{fan_names}  Create List
234    ${fan_names}=  Get Fan Names  ${fan_names}
235
236    ${number_of_fans}=  Get Length  ${fan_names}
237
238    [Return]  ${number_of_fans}  ${fan_names}
239
240
241
242Reset Fans
243    [Documentation]  Set the fans to functional state.
244    # Set state of fans to functional by writing 1 to the Functional
245    # attribute of each fan in the @{fan_names} list.  If @{fan_names}
246    # is empty nothing is done.
247    [Arguments]  ${fan_names}
248
249    # Description of Argument(s):
250    # fan_names    A list containing the names of the fans (e.g. fan0
251    #              fan2 fan3).
252
253    FOR  ${fan_name}  IN  @{fan_names}
254        Set Fan State  ${fan_name}  ${fan_functional}
255    END
256
257Verify Fan Speed
258    [Documentation]  Verify fans are running at or near target speed.
259    [Arguments]  ${tolerance}  ${fan_names}
260
261    # Description of argument(s):
262    # tolerance   The speed tolerance criteria.
263    #             A tolerance value of .15 means that the fan's speed
264    #             should be within 15% of its set target speed.
265    #             Fans may be accelerating to meet a new target, so
266    #             allow .10 extra.
267    # fan_names   A list containing the names of the fans (e.g. fan0 fan1).
268
269    # Compare the fan's speed with its target speed.
270    FOR  ${fan_name}  IN  @{fan_names}
271        ${target_speed}  ${fan_speed}=  Get Fan Target And Speed  ${fan_name}
272        Rpvars  fan_name  target_speed  fan_speed
273        # Calculate tolerance, which is a % of the target speed.
274        ${tolerance_value}=  Evaluate  ${tolerance}*${target_speed}
275        # Calculate upper and lower speed limits.
276        ${max_limit}=  Evaluate   ${target_speed}+${tolerance_value}
277        ${min_limit}=  Evaluate   ${target_speed}-${tolerance_value}
278        Run Keyword If
279        ...  ${fan_speed} < ${min_limit} or ${fan_speed} > ${max_limit}
280        ...  Fail  msg=${fan_name} speed of ${fan_speed} is out of range.
281    END
282
283Verify Direct Fan Control
284    [Documentation]  Verify direct control of fans.
285    [Arguments]  ${max_speed}  ${min_speed}
286    ...  ${minutes_to_stabilize}  ${number_of_fans}  ${fan_names}
287
288    # Overview:
289    # Turn off BMC's fan control daemon, then test to confirm
290    # that fans can be controlled manually.
291    # The app that takes data from sysfs and updates dbus is named hwmon.
292    # Verify hwmon functionality by comparing with what's on dbus
293    # (/xyz/openbmc_project/sensors/fan_tach/fan0_0, fan0_1, etc.)
294    # with what's in the BMC's file system at
295    # /sys/class/hwmon/hwmon9/fan*_input.
296
297    # Description of argument(s):
298    # max_speed               Integer value of maximum fan speed.
299    # min_speed               Integer value of minimum speed.
300    # minutes_to_stabilize    Time to wait for fan daemons to
301    #                         stabilize fan operation after
302    #                         tests (e.g. "4").
303    # number_of_fans          The number of fans in the system.
304    # fan_names               A list containing the names of the
305    #                         fans (e.g. fan0 fan1).
306
307    # Login to BMC and disable the fan daemon. Disabling the daemon sets
308    # manual mode.
309    Open Connection And Log In
310    Set Fan Daemon State  stop
311
312    # For each fan, set a new target speed and wait for the fan to
313    # accelerate.  Then check that the fan is running near that speed.
314    FOR  ${fan_name}  IN  @{fan_names}
315        Set Fan Target Speed  ${fan_name}  ${max_speed}
316        Run Key U  Sleep \ 60s
317        ${target_speed}  ${cw_speed}  ${ccw_speed}=
318        ...  Get Target And Blade Speeds  ${fan_name}
319        Rpvars  fan_name  target_speed  cw_speed  ccw_speed
320        Run Keyword If
321        ...  ${cw_speed} < ${min_speed} or ${ccw_speed} < ${min_speed}
322        ...  Fail  msg=${fan_name} failed manual speed test.
323    END
324
325    # Check the fan speeds in the BMC file system.
326
327    # Get the location of the fan hwmon.
328    ${controller_path}  ${stderr}  ${rc}=  BMC Execute Command
329    ...  grep -ir max31785a /sys/class/hwmon/hwmon* | grep name
330    # E.g., controller_path=/sys/class/hwmon/hwmon10/name:max31785a.
331
332    ${hwmon_path}  ${file_name}=  Split Path  ${controller_path}
333    # E.g.,  /sys/class/hwmon/hwmon10  or  /sys/class/hwmon/hwmon9.
334
335    Rpvars  controller_path  hwmon_path
336
337    # Run the BMC command which gets fan speeds from the file system.
338    ${cmd}=  Catenate  cat ${hwmon_path}/fan*_input
339    ${stdout}  ${stderr}  ${rc}=
340    ...  BMC Execute Command  ${cmd}
341
342    # Convert output to integer values.
343    ${speeds}=  Evaluate  map(int, $stdout.split(${\n}))
344    Rpvars  speeds
345    # Count the number of speeds > ${min_speed}.
346    ${count}=  Set Variable  ${0}
347    FOR  ${speed}  IN  @{speeds}
348        ${count}=  Run Keyword If  ${speed} > ${min_speed}
349        ...  Evaluate  ${count}+1  ELSE  Set Variable  ${count}
350        # Because each fan has two rotating fan blades, the count should be
351        # equual to 2*${number_of_fans}.  On water-cooled systems some
352        # speeds may be reported by hwmon as 0.  That is expected,
353        # and the number_of_fans reported in the system will be less.
354    END
355    ${fail_test}=  Evaluate  (2*${number_of_fans})-${count}
356
357    # Re-enable the fan daemon
358    Set Fan Daemon State  restart
359
360    Run Keyword If  ${fail_test} > ${0}  Fail
361    ...  msg=hwmon did not properly report fan speeds.
362
363    # Wait for the daemon to take control and gracefully set fan speeds
364    # back to normal.
365    ${msg}=  Catenate  Waiting ${minutes_to_stabilize} minutes
366    ...  for fan daemon to stabilize fans.
367    Print Timen  ${msg}
368    Run Key U  Sleep \ ${minutes_to_stabilize}m
369
370
371Verify Fan Speed Increase
372    [Documentation]  Verify that the speed of working fans increase when
373    ...  one fan is marked as disabled.
374    #  A non-functional fan should cause an error log and
375    #  an enclosure LED will light.  The other fans should speed up.
376    [Arguments]  ${fan_names}
377
378    # Description of argument(s):
379    # fan_names   A list containing the names of the fans (e.g. fan0 fan1).
380
381    # Allow system_response_time before checking if there was a
382    # response by the system to an applied fault.
383    ${system_response_time}=  Set Variable  60s
384
385    # Choose a fan to test with, e.g., fan0.
386    ${test_fan_name}=  Get From List  ${fan_names}  0
387
388    ${initial_speed}=  Get Target Speed Of Fans
389    Rpvars  test_fan_name  initial_speed
390
391    # If initial speed is not already at maximum, set expect_increase.
392    # This flag is used later to determine if speed checking is
393    # to be done or not.
394    ${expect_increase}=  Run Keyword If
395    ...  ${initial_speed} < ${max_speed}
396    ...  Set Variable  1  ELSE  Set Variable  0
397
398    Set Fan State  ${test_fan_name}  ${fan_nonfunctional}
399
400    Run Key U  Sleep \ ${system_response_time}
401
402    # A heavily loaded system may have powered-off.
403    ${host_state}=  Get Host State
404    Rpvars  host_state
405    Run Keyword If  'Running' != '${host_state}'  Pass Execution
406    ...  msg=System shutdown so skipping remainder of test.
407
408    ${new_fan_speed}=  Get Target Speed Of Fans
409    Rpvars  expect_increase  initial_speed  new_fan_speed
410
411    # Fail if current fan speed did not increase past the initial
412    # speed, but do this check only if not at maximum speed to begin with.
413    Run Keyword If
414    ...  ${expect_increase} == 1 and ${new_fan_speed} < ${initial_speed}
415    ...  Fail  msg=Remaining fans did not increase speed with loss of one fan.
416
417
418
419Verify System Shutdown Due To Fans
420    [Documentation]  Shut down when not enough fans.
421    [Arguments]  ${fan_names}
422
423    # Description of argument(s):
424    # fan_names   A list containing the names of the fans (e.g. fan0 fan1).
425
426    ${wait_after_poweroff}=  Set Variable  15s
427
428    # Set fans to be non-functional.
429    FOR  ${fan_name}  IN  @{fan_names}
430        Set Fan State  ${fan_name}  ${fan_nonfunctional}
431    END
432
433    # System should notice the non-functional fans and power-off.
434    # The Wait For PowerOff keyword will time-out and report
435    # an error if power off does not happen within a reasonable time.
436    Wait For PowerOff
437