|  | *** Settings *** | 
|  | Documentation  Utilities for fan tests. | 
|  |  | 
|  | Library        ../lib/bmc_ssh_utils.py | 
|  | Resource       ../lib/state_manager.robot | 
|  | Resource       ../lib/openbmc_ffdc_utils.robot | 
|  | Variables      ../data/variables.py | 
|  |  | 
|  | *** Variables *** | 
|  |  | 
|  | # Fan state values. | 
|  | ${fan_functional}      ${1} | 
|  | ${fan_nonfunctional}   ${0} | 
|  |  | 
|  | # Criteria for a fan at maximum speed. | 
|  | ${max_speed}=  ${10400} | 
|  |  | 
|  |  | 
|  | *** Keywords *** | 
|  |  | 
|  | Is Water Cooled | 
|  | [Documentation]  Return 1 if system is water cooled, 0 othersise. | 
|  |  | 
|  | ${water_cooled}=  Read Attribute | 
|  | ...  ${HOST_INVENTORY_URI}system/chassis  WaterCooled | 
|  | RETURN  ${water_cooled} | 
|  |  | 
|  |  | 
|  | Get Fan Names | 
|  | [Documentation]  Get the names of the fans marked present in inventory. | 
|  | [Arguments]  ${fan_names} | 
|  | # This keyword populates the fan_names list with the names of | 
|  | # fans present in inventory e.g. fan0, fan2, fan3. | 
|  |  | 
|  | # Description of Argument(s): | 
|  | # fan_names   The list of fan names to which new fan names are to be | 
|  | #             added to.  This list is returned to the caller. | 
|  |  | 
|  | ${fan_uris}=  Get Endpoint Paths  ${HOST_INVENTORY_URI}system  fan | 
|  | FOR  ${fan_uri}  IN  @{fan_uris} | 
|  | ${fan_properties}=  Read Properties  ${fan_uri} | 
|  | ${fan_present}=  Get Variable Value  ${fan_properties['Present']}  0 | 
|  | ${fan_functional}=  Get Variable Value | 
|  | ...  ${fan_properties['Functional']}  0 | 
|  | Continue For Loop If  ${fan_present} == 0 or ${fan_functional} == 0 | 
|  | ${remaining_uri}  ${fan_name}=  Split Path  ${fan_uri} | 
|  | Append To List  ${fan_names}  ${fan_name} | 
|  | END | 
|  |  | 
|  | RETURN  ${fan_names} | 
|  |  | 
|  |  | 
|  | Verify System Error Indication Due To Fans | 
|  | [Documentation]  Verify enclosure LEDs are on and there's an error log. | 
|  |  | 
|  | # Both enclosure LEDs should now be On. | 
|  | Verify Front And Rear LED State  On | 
|  |  | 
|  | # An error log should now exist. | 
|  | Error Logs Should Exist | 
|  |  | 
|  |  | 
|  | Verify Front And Rear LED State | 
|  | [Documentation]  Check state of the front and rear enclsure fault LEDs. | 
|  | [Arguments]  ${state} | 
|  | # Both LEDs should be in the specified state.  If not fail the test case. | 
|  |  | 
|  | # Description of Argument(s): | 
|  | # state    The state to check for, either 'Off' or 'On'. | 
|  |  | 
|  | ${front_fault}=  Get System LED State  front_fault | 
|  | ${rear_fault}=  Get System LED State  rear_fault | 
|  |  | 
|  | Run Keyword If | 
|  | ...  '${front_fault}' != '${state}' or '${rear_fault}' != '${state}' | 
|  | ...  Fail  msg=Expecting both enclosure LEDs to be ${state}. | 
|  |  | 
|  |  | 
|  | Set Fan State | 
|  | [Documentation]  Set the fan state, either functional or non-functional. | 
|  | [Arguments]  ${fan_name}  ${fan_state} | 
|  |  | 
|  | # Description of Argument(s): | 
|  | # fan_name     The name of the fan, e.g. "fan2". | 
|  | # fan_state    The state to set, 1 for functional, 2 for non-functional. | 
|  |  | 
|  | ${valueDict}=  Create Dictionary  data=${fan_state} | 
|  | Write Attribute | 
|  | ...  ${HOST_INVENTORY_URI}system/chassis/motherboard/${fan_name} | 
|  | ...  Functional  data=${valueDict} | 
|  |  | 
|  |  | 
|  | Set Fan Target Speed | 
|  | [Documentation]  Set the target speed of a fan. | 
|  | [Arguments]  ${fan_name}  ${fan_speed} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # fan_name    The name of the fan (e.g. "fan0"). | 
|  | # fan_speed   The target speed to set (e.g. "9000"). | 
|  |  | 
|  | ${valueDict}=  Create Dictionary  data=${fan_speed} | 
|  | Write Attribute  ${SENSORS_URI}fan_tach/${fan_name}_0 | 
|  | ...  Target  data=${valueDict} | 
|  |  | 
|  |  | 
|  | Get Target Speed Of Fans | 
|  | [Documentation]  Return the maximum target speed of the system fans. | 
|  |  | 
|  | ${max_target}=  Set Variable  0 | 
|  | ${paths}=  Get Endpoint Paths  ${SENSORS_URI}fan_tach/  0 | 
|  | FOR  ${path}  IN  @{paths} | 
|  | ${response}=  OpenBMC Get Request  ${path} | 
|  | ${target_speed}=  Set Variable  ${response.json()["data"]["Target"]} | 
|  | ${max_target}=  Run Keyword If  ${target_speed} > ${max_target} | 
|  | ...  Set Variable  ${target_speed}  ELSE  Set Variable  ${max_target} | 
|  | END | 
|  | RETURN  ${max_target} | 
|  |  | 
|  |  | 
|  | Get Target And Blade Speeds | 
|  | [Documentation]  Return the fan target speed setting, the speed of the | 
|  | ...  fan's clockwise blade, and the speed of the counter-clockwise blade. | 
|  | # Each fan unit has two counter-rotating fan blades | 
|  | # One blade is expected to be moving but the other blade may not be | 
|  | # moving whenever the fan unit is transitioning to a new target speed. | 
|  | [Arguments]  ${fan_name} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # fan_name       The name of a fan (e.g. "fan0") | 
|  |  | 
|  | # Get the fan target speed and the clockwise blade speed. | 
|  | ${path}=  Catenate  ${SENSORS_URI}fan_tach/${fan_name}_0 | 
|  | ${response}=  OpenBMC Get Request  ${path} | 
|  | ${fan_clockwise_speed}=  Set Variable  ${response.json()["data"]["Value"]} | 
|  | ${target_speed}=  Set Variable  ${response.json["data"]["Target"]} | 
|  |  | 
|  | # Get the counter-clockwise blade speed. | 
|  | ${path}=  Catenate  ${SENSORS_URI}fan_tach/${fan_name}_1 | 
|  | ${response}=  OpenBMC Get Request  ${path} | 
|  | ${fan_counterclockwise_speed}=  Set Variable  ${response.json()["data"]["Value"]} | 
|  |  | 
|  | RETURN  ${target_speed}  ${fan_clockwise_speed} | 
|  | ...  ${fan_counterclockwise_speed} | 
|  |  | 
|  |  | 
|  | Get Fan Target And Speed | 
|  | [Documentation]  Return the fan target speed setting and the | 
|  | ...  speed of the fastest blade. | 
|  | [Arguments]  ${fan_name} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # fan_name       The name of a fan (e.g. "fan0") | 
|  |  | 
|  | ${target_speed}  ${clockwise_speed}  ${counterclockwise_speed}= | 
|  | ...  Get Target And Blade Speeds  ${fan_name} | 
|  | ${blade_speed}=  Run Keyword If | 
|  | ...  ${clockwise_speed} > ${counterclockwise_speed} | 
|  | ...  Set Variable  ${clockwise_speed}  ELSE | 
|  | ...  Set Variable  ${counterclockwise_speed} | 
|  | RETURN  ${target_speed}  ${blade_speed} | 
|  |  | 
|  |  | 
|  | Set Fan Daemon State | 
|  | [Documentation]  Set the state of the fan control service. | 
|  | [Arguments]  ${state} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # state     The desired state of the service, usually | 
|  | #           "start", "stop", or "restart". | 
|  |  | 
|  | ${cmd}=  Catenate  systemctl  ${state}  phosphor-fan-control@0.service | 
|  | ${stdout}  ${stderr}  ${rc}=  BMC Execute Command  ${cmd} | 
|  |  | 
|  |  | 
|  | Verify Minimum Number Of Fans With Cooling Type | 
|  | [Documentation]  Verify minimum number of fans. | 
|  | [Arguments]  ${num_fans}  ${water_cooled} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # num_fans       The number of fans present in the system. | 
|  | # water_cooled   The value 1 if the system is water cooled, | 
|  | #                0 if air cooled. | 
|  |  | 
|  | # For a water cooled system. | 
|  | ${min_fans_water}=  Set Variable  2 | 
|  |  | 
|  | # For an air cooled system. | 
|  | ${min_fans_air}=  Set Variable  3 | 
|  |  | 
|  | Printn | 
|  | Rpvars  num_fans  water_cooled | 
|  |  | 
|  | # If water cooled must have at least min_fans_water fans, otherwise | 
|  | # issue Fatal Error and terminate testing. | 
|  | Run Keyword If  ${water_cooled} == 1 and ${num_fans} < ${min_fans_water} | 
|  | ...  Fatal Error | 
|  | ...  msg=Water cooled but less than ${min_fans_water} fans present. | 
|  |  | 
|  | # If air cooled must have at least min_fans_air fans. | 
|  | Run Keyword If  ${water_cooled} == 0 and ${num_fans} < ${min_fans_air} | 
|  | ...  Fatal Error | 
|  | ...  msg=Air cooled but less than ${min_fans_air} fans present. | 
|  |  | 
|  |  | 
|  | Verify Fan Monitors With State | 
|  | [Documentation]  Verify fan monitor daemons in the system state. | 
|  | [Arguments]  ${power_state} | 
|  | # The number of monitoring daemons is dependent upon the system | 
|  | # power state.  If power is off there should be 0, if power | 
|  | # is on there should be several. | 
|  |  | 
|  | # Description of argument(s): | 
|  | # power_state   Power staet of the system, either "On" or "Off" | 
|  |  | 
|  | ${cmd}=  Catenate  systemctl list-units | grep phosphor-fan | wc -l | 
|  | ${num_fan_daemons}  ${stderr}  ${rc}=  BMC Execute Command  ${cmd} | 
|  |  | 
|  | Rpvars  power_state  num_fan_daemons | 
|  |  | 
|  | # Fail if system is On and there are no fan monitors. | 
|  | Run Keyword If  '${power_state}' == 'On' and ${num_fan_daemons} == 0 | 
|  | ...  Fail  msg=No phosphor-fan monitors found at power on. | 
|  |  | 
|  | # Fail if system is Off and the fan monitors are present. | 
|  | Run Keyword If  '${power_state}' == 'Off' and ${num_fan_daemons} != 0 | 
|  | ...  Fail  msg=Phosphor-fan monitors found at power off. | 
|  |  | 
|  |  | 
|  | Get Fan Count And Names | 
|  | [Documentation]  Return the number of fans and the fan names. | 
|  |  | 
|  | # The @{fan_names} list holds the names of the fans in the system. | 
|  | @{fan_names}  Create List | 
|  | ${fan_names}=  Get Fan Names  ${fan_names} | 
|  |  | 
|  | ${number_of_fans}=  Get Length  ${fan_names} | 
|  |  | 
|  | RETURN  ${number_of_fans}  ${fan_names} | 
|  |  | 
|  |  | 
|  |  | 
|  | Reset Fans | 
|  | [Documentation]  Set the fans to functional state. | 
|  | # Set state of fans to functional by writing 1 to the Functional | 
|  | # attribute of each fan in the @{fan_names} list.  If @{fan_names} | 
|  | # is empty nothing is done. | 
|  | [Arguments]  ${fan_names} | 
|  |  | 
|  | # Description of Argument(s): | 
|  | # fan_names    A list containing the names of the fans (e.g. fan0 | 
|  | #              fan2 fan3). | 
|  |  | 
|  | FOR  ${fan_name}  IN  @{fan_names} | 
|  | Set Fan State  ${fan_name}  ${fan_functional} | 
|  | END | 
|  |  | 
|  | Verify Fan Speed | 
|  | [Documentation]  Verify fans are running at or near target speed. | 
|  | [Arguments]  ${tolerance}  ${fan_names} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # tolerance   The speed tolerance criteria. | 
|  | #             A tolerance value of .15 means that the fan's speed | 
|  | #             should be within 15% of its set target speed. | 
|  | #             Fans may be accelerating to meet a new target, so | 
|  | #             allow .10 extra. | 
|  | # fan_names   A list containing the names of the fans (e.g. fan0 fan1). | 
|  |  | 
|  | # Compare the fan's speed with its target speed. | 
|  | FOR  ${fan_name}  IN  @{fan_names} | 
|  | ${target_speed}  ${fan_speed}=  Get Fan Target And Speed  ${fan_name} | 
|  | Rpvars  fan_name  target_speed  fan_speed | 
|  | # Calculate tolerance, which is a % of the target speed. | 
|  | ${tolerance_value}=  Evaluate  ${tolerance}*${target_speed} | 
|  | # Calculate upper and lower speed limits. | 
|  | ${max_limit}=  Evaluate   ${target_speed}+${tolerance_value} | 
|  | ${min_limit}=  Evaluate   ${target_speed}-${tolerance_value} | 
|  | Run Keyword If | 
|  | ...  ${fan_speed} < ${min_limit} or ${fan_speed} > ${max_limit} | 
|  | ...  Fail  msg=${fan_name} speed of ${fan_speed} is out of range. | 
|  | END | 
|  |  | 
|  | Verify Direct Fan Control | 
|  | [Documentation]  Verify direct control of fans. | 
|  | [Arguments]  ${max_speed}  ${min_speed} | 
|  | ...  ${minutes_to_stabilize}  ${number_of_fans}  ${fan_names} | 
|  |  | 
|  | # Overview: | 
|  | # Turn off BMC's fan control daemon, then test to confirm | 
|  | # that fans can be controlled manually. | 
|  | # The app that takes data from sysfs and updates dbus is named hwmon. | 
|  | # Verify hwmon functionality by comparing with what's on dbus | 
|  | # (/xyz/openbmc_project/sensors/fan_tach/fan0_0, fan0_1, etc.) | 
|  | # with what's in the BMC's file system at | 
|  | # /sys/class/hwmon/hwmon9/fan*_input. | 
|  |  | 
|  | # Description of argument(s): | 
|  | # max_speed               Integer value of maximum fan speed. | 
|  | # min_speed               Integer value of minimum speed. | 
|  | # minutes_to_stabilize    Time to wait for fan daemons to | 
|  | #                         stabilize fan operation after | 
|  | #                         tests (e.g. "4"). | 
|  | # number_of_fans          The number of fans in the system. | 
|  | # fan_names               A list containing the names of the | 
|  | #                         fans (e.g. fan0 fan1). | 
|  |  | 
|  | # Login to BMC and disable the fan daemon. Disabling the daemon sets | 
|  | # manual mode. | 
|  | Open Connection And Log In | 
|  | Set Fan Daemon State  stop | 
|  |  | 
|  | # For each fan, set a new target speed and wait for the fan to | 
|  | # accelerate.  Then check that the fan is running near that speed. | 
|  | FOR  ${fan_name}  IN  @{fan_names} | 
|  | Set Fan Target Speed  ${fan_name}  ${max_speed} | 
|  | Run Key U  Sleep \ 60s | 
|  | ${target_speed}  ${cw_speed}  ${ccw_speed}= | 
|  | ...  Get Target And Blade Speeds  ${fan_name} | 
|  | Rpvars  fan_name  target_speed  cw_speed  ccw_speed | 
|  | Run Keyword If | 
|  | ...  ${cw_speed} < ${min_speed} or ${ccw_speed} < ${min_speed} | 
|  | ...  Fail  msg=${fan_name} failed manual speed test. | 
|  | END | 
|  |  | 
|  | # Check the fan speeds in the BMC file system. | 
|  |  | 
|  | # Get the location of the fan hwmon. | 
|  | ${controller_path}  ${stderr}  ${rc}=  BMC Execute Command | 
|  | ...  grep -ir max31785a /sys/class/hwmon/hwmon* | grep name | 
|  | # E.g., controller_path=/sys/class/hwmon/hwmon10/name:max31785a. | 
|  |  | 
|  | ${hwmon_path}  ${file_name}=  Split Path  ${controller_path} | 
|  | # E.g.,  /sys/class/hwmon/hwmon10  or  /sys/class/hwmon/hwmon9. | 
|  |  | 
|  | Rpvars  controller_path  hwmon_path | 
|  |  | 
|  | # Run the BMC command which gets fan speeds from the file system. | 
|  | ${cmd}=  Catenate  cat ${hwmon_path}/fan*_input | 
|  | ${stdout}  ${stderr}  ${rc}= | 
|  | ...  BMC Execute Command  ${cmd} | 
|  |  | 
|  | # Convert output to integer values. | 
|  | ${speeds}=  Evaluate  map(int, $stdout.split(${\n})) | 
|  | Rpvars  speeds | 
|  | # Count the number of speeds > ${min_speed}. | 
|  | ${count}=  Set Variable  ${0} | 
|  | FOR  ${speed}  IN  @{speeds} | 
|  | ${count}=  Run Keyword If  ${speed} > ${min_speed} | 
|  | ...  Evaluate  ${count}+1  ELSE  Set Variable  ${count} | 
|  | # Because each fan has two rotating fan blades, the count should be | 
|  | # equual to 2*${number_of_fans}.  On water-cooled systems some | 
|  | # speeds may be reported by hwmon as 0.  That is expected, | 
|  | # and the number_of_fans reported in the system will be less. | 
|  | END | 
|  | ${fail_test}=  Evaluate  (2*${number_of_fans})-${count} | 
|  |  | 
|  | # Re-enable the fan daemon | 
|  | Set Fan Daemon State  restart | 
|  |  | 
|  | Run Keyword If  ${fail_test} > ${0}  Fail | 
|  | ...  msg=hwmon did not properly report fan speeds. | 
|  |  | 
|  | # Wait for the daemon to take control and gracefully set fan speeds | 
|  | # back to normal. | 
|  | ${msg}=  Catenate  Waiting ${minutes_to_stabilize} minutes | 
|  | ...  for fan daemon to stabilize fans. | 
|  | Print Timen  ${msg} | 
|  | Run Key U  Sleep \ ${minutes_to_stabilize}m | 
|  |  | 
|  |  | 
|  | Verify Fan Speed Increase | 
|  | [Documentation]  Verify that the speed of working fans increase when | 
|  | ...  one fan is marked as disabled. | 
|  | #  A non-functional fan should cause an error log and | 
|  | #  an enclosure LED will light.  The other fans should speed up. | 
|  | [Arguments]  ${fan_names} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # fan_names   A list containing the names of the fans (e.g. fan0 fan1). | 
|  |  | 
|  | # Allow system_response_time before checking if there was a | 
|  | # response by the system to an applied fault. | 
|  | ${system_response_time}=  Set Variable  60s | 
|  |  | 
|  | # Choose a fan to test with, e.g., fan0. | 
|  | ${test_fan_name}=  Get From List  ${fan_names}  0 | 
|  |  | 
|  | ${initial_speed}=  Get Target Speed Of Fans | 
|  | Rpvars  test_fan_name  initial_speed | 
|  |  | 
|  | # If initial speed is not already at maximum, set expect_increase. | 
|  | # This flag is used later to determine if speed checking is | 
|  | # to be done or not. | 
|  | ${expect_increase}=  Run Keyword If | 
|  | ...  ${initial_speed} < ${max_speed} | 
|  | ...  Set Variable  1  ELSE  Set Variable  0 | 
|  |  | 
|  | Set Fan State  ${test_fan_name}  ${fan_nonfunctional} | 
|  |  | 
|  | Run Key U  Sleep \ ${system_response_time} | 
|  |  | 
|  | # A heavily loaded system may have powered-off. | 
|  | ${host_state}=  Get Host State | 
|  | Rpvars  host_state | 
|  | Run Keyword If  'Running' != '${host_state}'  Pass Execution | 
|  | ...  msg=System shutdown so skipping remainder of test. | 
|  |  | 
|  | ${new_fan_speed}=  Get Target Speed Of Fans | 
|  | Rpvars  expect_increase  initial_speed  new_fan_speed | 
|  |  | 
|  | # Fail if current fan speed did not increase past the initial | 
|  | # speed, but do this check only if not at maximum speed to begin with. | 
|  | Run Keyword If | 
|  | ...  ${expect_increase} == 1 and ${new_fan_speed} < ${initial_speed} | 
|  | ...  Fail  msg=Remaining fans did not increase speed with loss of one fan. | 
|  |  | 
|  |  | 
|  |  | 
|  | Verify System Shutdown Due To Fans | 
|  | [Documentation]  Shut down when not enough fans. | 
|  | [Arguments]  ${fan_names} | 
|  |  | 
|  | # Description of argument(s): | 
|  | # fan_names   A list containing the names of the fans (e.g. fan0 fan1). | 
|  |  | 
|  | ${wait_after_poweroff}=  Set Variable  15s | 
|  |  | 
|  | # Set fans to be non-functional. | 
|  | FOR  ${fan_name}  IN  @{fan_names} | 
|  | Set Fan State  ${fan_name}  ${fan_nonfunctional} | 
|  | END | 
|  |  | 
|  | # System should notice the non-functional fans and power-off. | 
|  | # The Wait For PowerOff keyword will time-out and report | 
|  | # an error if power off does not happen within a reasonable time. | 
|  | Wait For PowerOff |