sonic-buildimage/device/celestica/x86_64-cel_e1031-r0/sonic_platform/fan.py
Joe LeVeque 7f4ab8fbd8
[sonic-utilities] Update submodule; Build and install as a Python 3 wheel (#5926)
Submodule updates include the following commits:

* src/sonic-utilities 9dc58ea...f9eb739 (18):
  > Remove unnecessary calls to str.encode() now that the package is Python 3; Fix deprecation warning (#1260)
  > [generate_dump] Ignoring file/directory not found Errors (#1201)
  > Fixed porstat rate and util issues (#1140)
  > fix error: interface counters is mismatch after warm-reboot (#1099)
  > Remove unnecessary calls to str.decode() now that the package is Python 3 (#1255)
  > [acl-loader] Make list sorting compliant with Python 3 (#1257)
  > Replace hard-coded fast-reboot with variable. And some typo corrections (#1254)
  > [configlet][portconfig] Remove calls to dict.has_key() which is not available in Python 3 (#1247)
  > Remove unnecessary conversions to list() and calls to dict.keys() (#1243)
  > Clean up LGTM alerts (#1239)
  > Add 'requests' as install dependency in setup.py (#1240)
  > Convert to Python 3 (#1128)
  > Fix mock SonicV2Connector in python3: use decode_responses mode so caller code will be the same as python2 (#1238)
  > [tests] Do not trim from PATH if we did not append to it; Clean up/fix shebangs in scripts (#1233)
  > Updates to bgp config and show commands with BGP_INTERNAL_NEIGHBOR table (#1224)
  > [cli]: NAT show commands newline issue after migrated to Python3 (#1204)
  > [doc]: Update Command-Reference.md (#1231)
  > Added 'import sys' in feature.py file (#1232)

* src/sonic-py-swsssdk 9d9f0c6...1664be9 (2):
  > Fix: no need to decode() after redis client scan, so it will work for both python2 and python3 (#96)
  > FieldValueMap `contains`(`in`)  will also work when migrated to libswsscommon(C++ with SWIG wrapper) (#94)

- Also fix Python 3-related issues:
    - Use integer (floor) division in config_samples.py (sonic-config-engine)
    - Replace print statement with print function in eeprom.py plugin for x86_64-kvm_x86_64-r0 platform
    - Update all platform plugins to be compatible with both Python 2 and Python 3
    - Remove shebangs from plugins files which are not intended to be executable
    - Replace tabs with spaces in Python plugin files and fix alignment, because Python 3 is more strict
    - Remove trailing whitespace from plugins files
2020-11-25 10:28:36 -08:00

250 lines
8.4 KiB
Python

#############################################################################
# Celestica
#
# Module contains an implementation of SONiC Platform Base API and
# provides the fan status which are available in the platform
#
#############################################################################
import json
import math
import os.path
try:
from sonic_platform_base.fan_base import FanBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
EMC2305_PATH = "/sys/bus/i2c/drivers/emc2305/"
FAN_PATH = "/sys/devices/platform/e1031.smc/"
EMC2305_MAX_PWM = 255
EMC2305_FAN_PWM = "pwm{}"
EMC2305_FAN_TARGET = "fan{}_target"
EMC2305_FAN_INPUT = "pwm{}"
FAN_NAME_LIST = ["FAN-1", "FAN-2", "FAN-3"]
PSU_FAN_MAX_RPM = 11000
PSU_HWMON_PATH = "/sys/bus/i2c/devices/i2c-{0}/{0}-00{1}/hwmon"
PSU_I2C_MAPPING = {
0: {
"num": 13,
"addr": "5b"
},
1: {
"num": 12,
"addr": "5a"
},
}
class Fan(FanBase):
"""Platform-specific Fan class"""
def __init__(self, fan_tray_index, fan_index=0, is_psu_fan=False, psu_index=0):
self.fan_index = fan_index
self.fan_tray_index = fan_tray_index
self.is_psu_fan = is_psu_fan
if self.is_psu_fan:
self.psu_index = psu_index
self.psu_i2c_num = PSU_I2C_MAPPING[self.psu_index]["num"]
self.psu_i2c_addr = PSU_I2C_MAPPING[self.psu_index]["addr"]
self.psu_hwmon_path = PSU_HWMON_PATH.format(
self.psu_i2c_num, self.psu_i2c_addr)
# e1031 fan attributes
# Single emc2305 chip located at i2c-23-4d
# to control a fan module
self.emc2305_chip_mapping = [
{
'device': "23-004d",
'index_map': [1, 2, 4]
}
]
self.fan_e1031_presence = "fan{}_prs"
self.fan_e1031_direction = "fan{}_dir"
self.fan_e1031_led = "fan{}_led"
self.fan_e1031_led_col_map = {
self.STATUS_LED_COLOR_GREEN: "green",
self.STATUS_LED_COLOR_RED: "amber",
self.STATUS_LED_COLOR_OFF: "off"
}
FanBase.__init__(self)
def __read_txt_file(self, file_path):
try:
with open(file_path, 'r') as fd:
data = fd.read()
return data.strip()
except IOError:
pass
return ""
def __write_txt_file(self, file_path, value):
try:
with open(file_path, 'w') as fd:
fd.write(str(value))
except:
return False
return True
def __search_file_by_name(self, directory, file_name):
for dirpath, dirnames, files in os.walk(directory):
for name in files:
file_path = os.path.join(dirpath, name)
if name in file_name:
return file_path
return None
def get_direction(self):
"""
Retrieves the direction of fan
Returns:
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
depending on fan direction
"""
direction = self.FAN_DIRECTION_EXHAUST
if not self.is_psu_fan:
fan_direction_file = (FAN_PATH +
self.fan_e1031_direction.format(self.fan_tray_index+1))
raw = self.__read_txt_file(fan_direction_file).strip('\r\n')
direction = self.FAN_DIRECTION_INTAKE if str(
raw).upper() == "F2B" else self.FAN_DIRECTION_EXHAUST
return direction
def get_speed(self):
"""
Retrieves the speed of fan as a percentage of full speed
Returns:
An integer, the percentage of full fan speed, in the range 0 (off)
to 100 (full speed)
Note:
speed = pwm_in/255*100
"""
speed = 0
if self.is_psu_fan:
fan_speed_sysfs_name = "fan{}_input".format(self.fan_index+1)
fan_speed_sysfs_path = self.__search_file_by_name(
self.psu_hwmon_path, fan_speed_sysfs_name)
fan_speed_rpm = self.__read_txt_file(fan_speed_sysfs_path) or 0
fan_speed_raw = float(fan_speed_rpm)/PSU_FAN_MAX_RPM * 100
speed = math.ceil(float(fan_speed_rpm) * 100 / PSU_FAN_MAX_RPM)
elif self.get_presence():
chip = self.emc2305_chip_mapping[self.fan_index]
device = chip['device']
fan_index = chip['index_map']
sysfs_path = "%s%s/%s" % (
EMC2305_PATH, device, EMC2305_FAN_INPUT)
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
raw = self.__read_txt_file(sysfs_path).strip('\r\n')
pwm = int(raw, 10) if raw else 0
speed = math.ceil(float(pwm * 100 / EMC2305_MAX_PWM))
return int(speed)
def get_target_speed(self):
"""
Retrieves the target (expected) speed of the fan
Returns:
An integer, the percentage of full fan speed, in the range 0 (off)
to 100 (full speed)
Note:
speed_pc = pwm_target/255*100
0 : when PWM mode is use
pwm : when pwm mode is not use
"""
target = 0
if not self.is_psu_fan:
chip = self.emc2305_chip_mapping[self.fan_index]
device = chip['device']
fan_index = chip['index_map']
sysfs_path = "%s%s/%s" % (
EMC2305_PATH, device, EMC2305_FAN_TARGET)
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
raw = self.__read_txt_file(sysfs_path).strip('\r\n')
pwm = int(raw, 10) if raw else 0
target = math.ceil(float(pwm) * 100 / EMC2305_MAX_PWM)
return target
def get_speed_tolerance(self):
"""
Retrieves the speed tolerance of the fan
Returns:
An integer, the percentage of variance from target speed which is
considered tolerable
"""
return 10
def set_speed(self, speed):
"""
Sets the fan speed
Args:
speed: An integer, the percentage of full fan speed to set fan to,
in the range 0 (off) to 100 (full speed)
Returns:
A boolean, True if speed is set successfully, False if not
Note:
Depends on pwm or target mode is selected:
1) pwm = speed_pc * 255 <-- Currently use this mode.
2) target_pwm = speed_pc * 100 / 255
2.1) set pwm{}_enable to 3
"""
pwm = speed * 255 / 100
if not self.is_psu_fan and self.get_presence():
chip = self.emc2305_chip_mapping[self.fan_index]
device = chip['device']
fan_index = chip['index_map']
sysfs_path = "%s%s/%s" % (
EMC2305_PATH, device, EMC2305_FAN_PWM)
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
return self.__write_txt_file(sysfs_path, int(pwm))
return False
def set_status_led(self, color):
"""
Sets the state of the fan module status LED
Args:
color: A string representing the color with which to set the
fan module status LED
Returns:
bool: True if status LED state is set successfully, False if not
"""
set_status_led = False
if not self.is_psu_fan:
fan_led_file = (FAN_PATH +
self.fan_e1031_led.format(self.fan_tray_index+1))
set_status_led = self.__write_txt_file(
fan_led_file, self.fan_e1031_led_col_map[color]) if self.get_presence() else False
return set_status_led
def get_name(self):
"""
Retrieves the name of the device
Returns:
string: The name of the device
"""
fan_name = FAN_NAME_LIST[self.fan_tray_index] if not self.is_psu_fan else "PSU-{} FAN-{}".format(
self.psu_index+1, self.fan_index+1)
return fan_name
def get_presence(self):
"""
Retrieves the presence of the PSU
Returns:
bool: True if PSU is present, False if not
"""
fan_direction_file = (FAN_PATH +
self.fan_e1031_presence.format(self.fan_tray_index+1))
present_str = self.__read_txt_file(fan_direction_file) or '1'
return int(present_str) == 0 if not self.is_psu_fan else True