[Mellanox]support led for fan/psu and fan's direction (#3795)

This commit is contained in:
Stephen Sun 2019-12-05 03:40:42 +08:00 committed by Joe LeVeque
parent cda61290ac
commit d5aa0d4382
3 changed files with 243 additions and 24 deletions

View File

@ -14,6 +14,7 @@ try:
from sonic_daemon_base.daemon_base import Logger
from os import listdir
from os.path import isfile, join
from glob import glob
import sys
import io
import re
@ -33,6 +34,10 @@ EEPROM_CACHE_FILE = 'syseeprom_cache'
HWMGMT_SYSTEM_ROOT = '/var/run/hw-management/system/'
MST_DEVICE_NAME_PATTERN = '/dev/mst/mt[0-9]*_pciconf0'
MST_DEVICE_RE_PATTERN = '/dev/mst/mt([0-9]*)_pciconf0'
SPECTRUM1_CHIP_ID = '52100'
#reboot cause related definitions
REBOOT_CAUSE_ROOT = HWMGMT_SYSTEM_ROOT
@ -87,11 +92,21 @@ class Chassis(ChassisBase):
num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers()
multi_rotor_in_drawer = num_of_fan > num_of_drawer
# Fan's direction isn't supported on spectrum 1 devices for now
mst_dev_list = glob(MST_DEVICE_NAME_PATTERN)
if not mst_dev_list:
raise RuntimeError("Can't get chip type due to {} not found".format(MST_DEVICE_NAME_PATTERN))
m = re.search(MST_DEVICE_RE_PATTERN, mst_dev_list[0])
if m.group(1) == SPECTRUM1_CHIP_ID:
has_fan_dir = False
else:
has_fan_dir = True
for index in range(num_of_fan):
if multi_rotor_in_drawer:
fan = Fan(index, index/2)
fan = Fan(has_fan_dir, index, index/2)
else:
fan = Fan(index, index)
fan = Fan(has_fan_dir, index, index)
self._fan_list.append(fan)

View File

@ -15,17 +15,22 @@ try:
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
LED_ON = 1
LED_OFF = 0
LED_ON = '1'
LED_OFF = '0'
PWM_MAX = 255
FAN_PATH = "/var/run/hw-management/thermal/"
LED_PATH = "/var/run/hw-management/led/"
# fan_dir isn't supported on Spectrum 1. It is supported on Spectrum 2 and later switches
FAN_DIR = "/var/run/hw-management/system/fan_dir"
class Fan(FanBase):
"""Platform-specific Fan class"""
def __init__(self, fan_index, drawer_index = 1, psu_fan = False):
STATUS_LED_COLOR_ORANGE = "orange"
def __init__(self, has_fan_dir, fan_index, drawer_index = 1, psu_fan = False):
# API index is starting from 0, Mellanox platform index is starting from 1
self.index = fan_index + 1
self.drawer_index = drawer_index + 1
@ -48,6 +53,45 @@ class Fan(FanBase):
self.fan_orange_led_path = "led_fan{}_orange".format(self.drawer_index)
self.fan_pwm_path = "pwm1"
self.fan_led_cap_path = "led_fan{}_capability".format(self.drawer_index)
if has_fan_dir:
self.fan_dir = FAN_DIR
else:
self.fan_dir = None
def get_direction(self):
"""
Retrieves the fan's direction
Returns:
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
depending on fan direction
Notes:
What Mellanox calls forward:
Air flows from fans side to QSFP side, for example: MSN2700-CS2F
which means intake in community
What Mellanox calls reverse:
Air flow from QSFP side to fans side, for example: MSN2700-CS2R
which means exhaust in community
According to hw-mgmt:
1 stands for forward, in other words intake
0 stands for reverse, in other words exhaust
"""
if not self.fan_dir or self.is_psu_fan:
return self.FAN_DIRECTION_NOT_APPLICABLE
try:
with open(os.path.join(self.fan_dir), 'r') as fan_dir:
fan_dir_bits = int(fan_dir.read())
fan_mask = 1 << self.index - 1
if fan_dir_bits & fan_mask:
return self.FAN_DIRECTION_INTAKE
else:
return self.FAN_DIRECTION_EXHAUST
except (ValueError, IOError) as e:
raise RuntimeError("Failed to read fan direction status to {}".format(repr(e)))
def get_status(self):
"""
@ -68,6 +112,7 @@ class Fan(FanBase):
return status == 1
def get_presence(self):
"""
Retrieves the presence status of fan
@ -90,6 +135,7 @@ class Fan(FanBase):
return status == 1
def _get_min_speed_in_rpm(self):
speed = 0
try:
@ -100,6 +146,7 @@ class Fan(FanBase):
return speed
def _get_max_speed_in_rpm(self):
speed = 0
try:
@ -110,6 +157,7 @@ class Fan(FanBase):
return speed
def get_speed(self):
"""
Retrieves the speed of fan
@ -129,6 +177,7 @@ class Fan(FanBase):
return speed
def get_target_speed(self):
"""
Retrieves the expected speed of fan
@ -151,6 +200,7 @@ class Fan(FanBase):
return speed
def set_speed(self, speed):
"""
Set fan speed to expected value
@ -177,6 +227,7 @@ class Fan(FanBase):
return status
def _get_led_capability(self):
cap_list = None
try:
@ -188,6 +239,7 @@ class Fan(FanBase):
return cap_list
def set_status_led(self, color):
"""
Set led to expected color
@ -208,32 +260,70 @@ class Fan(FanBase):
return False
status = False
try:
if color == 'green':
if color == self.STATUS_LED_COLOR_GREEN:
with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led:
fan_led.write(str(LED_ON))
elif color == 'red':
fan_led.write(LED_ON)
status = True
elif color == self.STATUS_LED_COLOR_RED:
# Some fan don't support red led but support orange led, in this case we set led to orange
if 'red' in led_cap_list:
if self.STATUS_LED_COLOR_RED in led_cap_list:
led_path = os.path.join(LED_PATH, self.fan_red_led_path)
elif 'orange' in led_cap_list:
elif self.STATUS_LED_COLOR_ORANGE in led_cap_list:
led_path = os.path.join(LED_PATH, self.fan_orange_led_path)
else:
return False
with open(led_path, 'w') as fan_led:
fan_led.write(str(LED_ON))
fan_led.write(LED_ON)
status = True
elif color == self.STATUS_LED_COLOR_OFF:
if self.STATUS_LED_COLOR_GREEN in led_cap_list:
with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF))
if self.STATUS_LED_COLOR_RED in led_cap_list:
with open(os.path.join(LED_PATH, self.fan_red_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF))
if self.STATUS_LED_COLOR_ORANGE in led_cap_list:
with open(os.path.join(LED_PATH, self.fan_orange_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF))
elif color == 'off':
with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF))
with open(os.path.join(LED_PATH, self.fan_red_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF))
status = True
else:
status = False
except (ValueError, IOError):
status = False
status = False
return status
def get_status_led(self):
"""
Gets the state of the fan status LED
Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings above
"""
led_cap_list = self._get_led_capability()
if led_cap_list is None:
return self.STATUS_LED_COLOR_OFF
try:
with open(os.path.join(LED_PATH, self.fan_green_led_path), 'r') as fan_led:
if LED_OFF != fan_led.read().rstrip('\n'):
return self.STATUS_LED_COLOR_GREEN
if self.STATUS_LED_COLOR_RED in led_cap_list:
with open(os.path.join(LED_PATH, self.fan_red_led_path), 'r') as fan_led:
if LED_OFF != fan_led.read().rstrip('\n'):
return self.STATUS_LED_COLOR_RED
if self.STATUS_LED_COLOR_ORANGE in led_cap_list:
with open(os.path.join(LED_PATH, self.fan_orange_led_path), 'r') as fan_led:
if LED_OFF != fan_led.read().rstrip('\n'):
return self.STATUS_LED_COLOR_RED
except (ValueError, IOError) as e:
raise RuntimeError("Failed to read led status for fan {} due to {}".format(self.index, repr(e)))
return self.STATUS_LED_COLOR_OFF
def get_speed_tolerance(self):
"""
Retrieves the speed tolerance of the fan

View File

@ -16,6 +16,9 @@ try:
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
LED_ON = '1'
LED_OFF = '0'
# Global logger class instance
logger = Logger()
@ -25,6 +28,8 @@ PSU_CURRENT = "current"
PSU_VOLTAGE = "voltage"
PSU_POWER = "power"
LED_PATH = "/var/run/hw-management/led/"
# SKUs with unplugable PSUs:
# 1. don't have psuX_status and should be treated as always present
# 2. don't have voltage, current and power values
@ -50,6 +55,9 @@ psu_profile_list = [
class Psu(PsuBase):
"""Platform-specific Psu class"""
STATUS_LED_COLOR_ORANGE = "orange"
def __init__(self, psu_index, sku):
global psu_list
PsuBase.__init__(self)
@ -90,10 +98,16 @@ class Psu(PsuBase):
psu_presence = os.path.join(self.psu_path, psu_presence)
self.psu_presence = psu_presence
fan = Fan(psu_index, psu_index, True)
fan = Fan(sku, psu_index, psu_index, True)
if fan.get_presence():
self._fan = fan
self.psu_green_led_path = "led_psu_green"
self.psu_red_led_path = "led_psu_red"
self.psu_orange_led_path = "led_psu_orange"
self.psu_led_cap_path = "led_psu_capability"
def _read_generic_file(self, filename, len):
"""
Read a generic file, returns the contents of the file
@ -106,6 +120,7 @@ class Psu(PsuBase):
logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
return result
def get_powergood_status(self):
"""
Retrieves the operational status of power supply unit (PSU) defined
@ -117,6 +132,7 @@ class Psu(PsuBase):
return status == 1
def get_presence(self):
"""
Retrieves the presence status of power supply unit (PSU) defined
@ -130,6 +146,7 @@ class Psu(PsuBase):
status = self._read_generic_file(self.psu_presence, 0)
return status == 1
def get_voltage(self):
"""
Retrieves current PSU voltage output
@ -144,6 +161,7 @@ class Psu(PsuBase):
else:
return None
def get_current(self):
"""
Retrieves present electric current supplied by PSU
@ -169,3 +187,99 @@ class Psu(PsuBase):
return float(power) / 1000000
else:
return None
def _get_led_capability(self):
cap_list = None
try:
with open(os.path.join(LED_PATH, self.psu_led_cap_path), 'r') as psu_led_cap:
caps = psu_led_cap.read()
cap_list = caps.split()
except (ValueError, IOError):
status = 0
return cap_list
def set_status_led(self, color):
"""
Sets the state of the PSU status LED
Args:
color: A string representing the color with which to set the
PSU status LED
Returns:
bool: True if status LED state is set successfully, False if not
Notes:
Only one led for all PSUs.
"""
led_cap_list = self._get_led_capability()
if led_cap_list is None:
return False
status = False
try:
if color == self.STATUS_LED_COLOR_GREEN:
with open(os.path.join(LED_PATH, self.psu_green_led_path), 'w') as psu_led:
psu_led.write(LED_ON)
status = True
elif color == self.STATUS_LED_COLOR_RED:
# Some fan don't support red led but support orange led, in this case we set led to orange
if self.STATUS_LED_COLOR_RED in led_cap_list:
led_path = os.path.join(LED_PATH, self.psu_red_led_path)
elif self.STATUS_LED_COLOR_ORANGE in led_cap_list:
led_path = os.path.join(LED_PATH, self.psu_orange_led_path)
else:
return False
with open(led_path, 'w') as psu_led:
psu_led.write(LED_ON)
status = True
elif color == self.STATUS_LED_COLOR_OFF:
if self.STATUS_LED_COLOR_GREEN in led_cap_list:
with open(os.path.join(LED_PATH, self.psu_green_led_path), 'w') as psu_led:
psu_led.write(str(LED_OFF))
if self.STATUS_LED_COLOR_RED in led_cap_list:
with open(os.path.join(LED_PATH, self.psu_red_led_path), 'w') as psu_led:
psu_led.write(str(LED_OFF))
if self.STATUS_LED_COLOR_ORANGE in led_cap_list:
with open(os.path.join(LED_PATH, self.psu_orange_led_path), 'w') as psu_led:
psu_led.write(str(LED_OFF))
status = True
else:
status = False
except (ValueError, IOError):
status = False
return status
def get_status_led(self):
"""
Gets the state of the PSU status LED
Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings above
"""
led_cap_list = self._get_led_capability()
if led_cap_list is None:
return self.STATUS_LED_COLOR_OFF
try:
with open(os.path.join(LED_PATH, self.psu_green_led_path), 'r') as psu_led:
if LED_OFF != psu_led.read().rstrip('\n'):
return self.STATUS_LED_COLOR_GREEN
if self.STATUS_LED_COLOR_RED in led_cap_list:
with open(os.path.join(LED_PATH, self.psu_red_led_path), 'r') as psu_led:
if LED_OFF != psu_led.read().rstrip('\n'):
return self.STATUS_LED_COLOR_RED
if self.STATUS_LED_COLOR_ORANGE in led_cap_list:
with open(os.path.join(LED_PATH, self.psu_orange_led_path), 'r') as psu_led:
if LED_OFF != psu_led.read().rstrip('\n'):
return self.STATUS_LED_COLOR_RED
except (ValueError, IOError) as e:
raise RuntimeError("Failed to read led status for psu due to {}".format(repr(e)))
return self.STATUS_LED_COLOR_OFF