[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 sonic_daemon_base.daemon_base import Logger
from os import listdir from os import listdir
from os.path import isfile, join from os.path import isfile, join
from glob import glob
import sys import sys
import io import io
import re import re
@ -33,6 +34,10 @@ EEPROM_CACHE_FILE = 'syseeprom_cache'
HWMGMT_SYSTEM_ROOT = '/var/run/hw-management/system/' 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 related definitions
REBOOT_CAUSE_ROOT = HWMGMT_SYSTEM_ROOT 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() num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers()
multi_rotor_in_drawer = num_of_fan > num_of_drawer 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): for index in range(num_of_fan):
if multi_rotor_in_drawer: if multi_rotor_in_drawer:
fan = Fan(index, index/2) fan = Fan(has_fan_dir, index, index/2)
else: else:
fan = Fan(index, index) fan = Fan(has_fan_dir, index, index)
self._fan_list.append(fan) self._fan_list.append(fan)

View File

@ -15,17 +15,22 @@ try:
except ImportError as e: except ImportError as e:
raise ImportError (str(e) + "- required module not found") raise ImportError (str(e) + "- required module not found")
LED_ON = 1 LED_ON = '1'
LED_OFF = 0 LED_OFF = '0'
PWM_MAX = 255 PWM_MAX = 255
FAN_PATH = "/var/run/hw-management/thermal/" FAN_PATH = "/var/run/hw-management/thermal/"
LED_PATH = "/var/run/hw-management/led/" 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): class Fan(FanBase):
"""Platform-specific Fan class""" """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 # API index is starting from 0, Mellanox platform index is starting from 1
self.index = fan_index + 1 self.index = fan_index + 1
self.drawer_index = drawer_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_orange_led_path = "led_fan{}_orange".format(self.drawer_index)
self.fan_pwm_path = "pwm1" self.fan_pwm_path = "pwm1"
self.fan_led_cap_path = "led_fan{}_capability".format(self.drawer_index) 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): def get_status(self):
""" """
@ -68,6 +112,7 @@ class Fan(FanBase):
return status == 1 return status == 1
def get_presence(self): def get_presence(self):
""" """
Retrieves the presence status of fan Retrieves the presence status of fan
@ -90,6 +135,7 @@ class Fan(FanBase):
return status == 1 return status == 1
def _get_min_speed_in_rpm(self): def _get_min_speed_in_rpm(self):
speed = 0 speed = 0
try: try:
@ -100,6 +146,7 @@ class Fan(FanBase):
return speed return speed
def _get_max_speed_in_rpm(self): def _get_max_speed_in_rpm(self):
speed = 0 speed = 0
try: try:
@ -110,6 +157,7 @@ class Fan(FanBase):
return speed return speed
def get_speed(self): def get_speed(self):
""" """
Retrieves the speed of fan Retrieves the speed of fan
@ -129,6 +177,7 @@ class Fan(FanBase):
return speed return speed
def get_target_speed(self): def get_target_speed(self):
""" """
Retrieves the expected speed of fan Retrieves the expected speed of fan
@ -151,6 +200,7 @@ class Fan(FanBase):
return speed return speed
def set_speed(self, speed): def set_speed(self, speed):
""" """
Set fan speed to expected value Set fan speed to expected value
@ -177,6 +227,7 @@ class Fan(FanBase):
return status return status
def _get_led_capability(self): def _get_led_capability(self):
cap_list = None cap_list = None
try: try:
@ -188,6 +239,7 @@ class Fan(FanBase):
return cap_list return cap_list
def set_status_led(self, color): def set_status_led(self, color):
""" """
Set led to expected color Set led to expected color
@ -208,32 +260,70 @@ class Fan(FanBase):
return False return False
status = False status = False
try: 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: with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led:
fan_led.write(str(LED_ON)) fan_led.write(LED_ON)
elif color == 'red': 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 # 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) 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) led_path = os.path.join(LED_PATH, self.fan_orange_led_path)
else: else:
return False return False
with open(led_path, 'w') as fan_led: with open(led_path, 'w') as fan_led:
fan_led.write(str(LED_ON)) fan_led.write(LED_ON)
status = True
elif color == 'off': 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: with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF)) 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: with open(os.path.join(LED_PATH, self.fan_red_led_path), 'w') as fan_led:
fan_led.write(str(LED_OFF)) 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))
status = True
else: else:
status = False status = False
except (ValueError, IOError): except (ValueError, IOError):
status = False status = False
return status 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): def get_speed_tolerance(self):
""" """
Retrieves the speed tolerance of the fan Retrieves the speed tolerance of the fan

View File

@ -16,6 +16,9 @@ try:
except ImportError as e: except ImportError as e:
raise ImportError (str(e) + "- required module not found") raise ImportError (str(e) + "- required module not found")
LED_ON = '1'
LED_OFF = '0'
# Global logger class instance # Global logger class instance
logger = Logger() logger = Logger()
@ -25,6 +28,8 @@ PSU_CURRENT = "current"
PSU_VOLTAGE = "voltage" PSU_VOLTAGE = "voltage"
PSU_POWER = "power" PSU_POWER = "power"
LED_PATH = "/var/run/hw-management/led/"
# SKUs with unplugable PSUs: # SKUs with unplugable PSUs:
# 1. don't have psuX_status and should be treated as always present # 1. don't have psuX_status and should be treated as always present
# 2. don't have voltage, current and power values # 2. don't have voltage, current and power values
@ -50,6 +55,9 @@ psu_profile_list = [
class Psu(PsuBase): class Psu(PsuBase):
"""Platform-specific Psu class""" """Platform-specific Psu class"""
STATUS_LED_COLOR_ORANGE = "orange"
def __init__(self, psu_index, sku): def __init__(self, psu_index, sku):
global psu_list global psu_list
PsuBase.__init__(self) PsuBase.__init__(self)
@ -90,10 +98,16 @@ class Psu(PsuBase):
psu_presence = os.path.join(self.psu_path, psu_presence) psu_presence = os.path.join(self.psu_path, psu_presence)
self.psu_presence = 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(): if fan.get_presence():
self._fan = fan 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): def _read_generic_file(self, filename, len):
""" """
Read a generic file, returns the contents of the file 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))) logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
return result return result
def get_powergood_status(self): def get_powergood_status(self):
""" """
Retrieves the operational status of power supply unit (PSU) defined Retrieves the operational status of power supply unit (PSU) defined
@ -117,6 +132,7 @@ class Psu(PsuBase):
return status == 1 return status == 1
def get_presence(self): def get_presence(self):
""" """
Retrieves the presence status of power supply unit (PSU) defined 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) status = self._read_generic_file(self.psu_presence, 0)
return status == 1 return status == 1
def get_voltage(self): def get_voltage(self):
""" """
Retrieves current PSU voltage output Retrieves current PSU voltage output
@ -144,6 +161,7 @@ class Psu(PsuBase):
else: else:
return None return None
def get_current(self): def get_current(self):
""" """
Retrieves present electric current supplied by PSU Retrieves present electric current supplied by PSU
@ -169,3 +187,99 @@ class Psu(PsuBase):
return float(power) / 1000000 return float(power) / 1000000
else: else:
return None 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