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