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