[Mellanox] Enhancement for fan led management (#4437)

This commit is contained in:
Junchao-Mellanox 2020-05-14 01:01:32 +08:00 committed by GitHub
parent d3c28a45d9
commit 5e6c20481d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 593 additions and 250 deletions

View File

@ -0,0 +1 @@
../x86_64-mlnx_msn2700-r0/thermal_policy.json

View File

@ -0,0 +1 @@
../x86_64-mlnx_msn2700-r0/thermal_policy.json

View File

@ -0,0 +1 @@
../x86_64-mlnx_msn2700-r0/thermal_policy.json

View File

@ -12,10 +12,10 @@ try:
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform_base.component_base import ComponentBase
from sonic_device_util import get_machine_info
from sonic_device_util import get_platform_info
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
@ -61,12 +61,14 @@ class Chassis(ChassisBase):
# Initialize SKU name
self.sku_name = self._get_sku_name()
self.platform_name = self._get_platform_name()
mi = get_machine_info()
if mi is not None:
self.name = mi['onie_platform']
self.platform_name = get_platform_info(mi)
else:
self.name = self.sku_name
self.platform_name = self._get_platform_name()
# move the initialization of each components to their dedicated initializer
# which will be called from platform
@ -86,36 +88,29 @@ class Chassis(ChassisBase):
# Initialize PSU list
self.psu_module = Psu
for index in range(MLNX_NUM_PSU):
psu = Psu(index, self.sku_name)
psu = Psu(index, self.platform_name)
self._psu_list.append(psu)
def initialize_fan(self):
from .device_data import DEVICE_DATA
from sonic_platform.fan import Fan
from sonic_platform.fan import FAN_PATH
self.fan_module = Fan
self.fan_path = FAN_PATH
# Initialize FAN list
multi_rotor_in_drawer = False
num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers()
multi_rotor_in_drawer = num_of_fan > num_of_drawer
from .fan_drawer import RealDrawer, VirtualDrawer
# 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(has_fan_dir, index, index/2, False, self.platform_name)
else:
fan = Fan(has_fan_dir, index, index, False, self.platform_name)
self._fan_list.append(fan)
fan_data = DEVICE_DATA[self.platform_name]['fans']
drawer_num = fan_data['drawer_num']
drawer_type = fan_data['drawer_type']
fan_num_per_drawer = fan_data['fan_num_per_drawer']
drawer_ctor = RealDrawer if drawer_type == 'real' else VirtualDrawer
fan_index = 0
for drawer_index in range(drawer_num):
drawer = drawer_ctor(drawer_index, fan_data)
self._fan_drawer_list.append(drawer)
for index in range(fan_num_per_drawer):
fan = Fan(fan_index, drawer)
fan_index += 1
drawer._fan_list.append(fan)
self._fan_list.append(fan)
def initialize_sfp(self):

View File

@ -5,6 +5,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:30":13, "31:40":14 , "41:120":15},
"unk_untrust": {"-127:25":13, "26:30":14 , "31:35":15, "36:120":16}
}
},
'fans': {
'drawer_num': 4,
'drawer_type': 'real',
'fan_num_per_drawer': 2,
'support_fan_direction': False,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn2740-r0': {
@ -13,6 +26,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:120":13},
"unk_untrust": {"-127:15":13, "16:25":14 , "26:30":15, "31:120":17},
}
},
'fans': {
'drawer_num': 4,
'drawer_type': 'real',
'fan_num_per_drawer': 1,
'support_fan_direction': False,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn2100-r0': {
@ -21,6 +47,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:40":12, "41:120":13},
"unk_untrust": {"-127:15":12, "16:25":13, "26:30":14, "31:35":15, "36:120":16}
}
},
'fans': {
'drawer_num': 1,
'drawer_type': 'virtual',
'fan_num_per_drawer': 4,
'support_fan_direction': False,
'hot_swappable': False
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': False,
'led_num': 2
}
},
'x86_64-mlnx_msn2410-r0': {
@ -29,6 +68,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:30":13, "31:40":14 , "41:120":15},
"unk_untrust": {"-127:25":13, "26:30":14 , "31:35":15, "36:120":16}
}
},
'fans': {
'drawer_num': 4,
'drawer_type': 'real',
'fan_num_per_drawer': 2,
'support_fan_direction': False,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn2010-r0': {
@ -37,6 +89,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:120":12},
"unk_untrust": {"-127:15":12, "16:20":13 , "21:30":14, "31:35":15, "36:120":16}
}
},
'fans': {
'drawer_num': 1,
'drawer_type': 'virtual',
'fan_num_per_drawer': 4,
'support_fan_direction': False,
'hot_swappable': False
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': False,
'led_num': 2
}
},
'x86_64-mlnx_msn3700-r0': {
@ -45,6 +110,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:25":12, "26:40":13 , "41:120":14},
"unk_untrust": {"-127:15":12, "16:30":13 , "31:35":14, "36:40":15, "41:120":16},
}
},
'fans': {
'drawer_num': 6,
'drawer_type': 'real',
'fan_num_per_drawer': 2,
'support_fan_direction': True,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn3700c-r0': {
@ -53,6 +131,19 @@ DEVICE_DATA = {
"unk_trust": {"-127:40":12, "41:120":13},
"unk_untrust": {"-127:10":12, "11:20":13 , "21:30":14, "31:35":15, "36:120":16},
}
},
'fans': {
'drawer_num': 4,
'drawer_type': 'real',
'fan_num_per_drawer': 2,
'support_fan_direction': True,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn3800-r0': {
@ -61,14 +152,82 @@ DEVICE_DATA = {
"unk_trust": {"-127:30":12, "31:40":13 , "41:120":14},
"unk_untrust": {"-127:0":12, "1:10":13 , "11:15":14, "16:20":15, "21:35":16, "36:120":17},
}
},
'fans': {
'drawer_num': 3,
'drawer_type': 'real',
'fan_num_per_drawer': 1,
'support_fan_direction': True,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn4700-r0': {
'thermal': {
'minimum_table': {
"unk_trust": {"-127:120":16},
"unk_untrust": {"-127:120":16},
}
},
'fans': {
'drawer_num': 6,
'drawer_type': 'real',
'fan_num_per_drawer': 2,
'support_fan_direction': True,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn3420-r0': {
'thermal': {
'minimum_table': {
"unk_trust": {"-127:120":16},
"unk_untrust": {"-127:120":16},
}
},
'fans': {
'drawer_num': 5,
'drawer_type': 'real',
'fan_num_per_drawer': 2,
'support_fan_direction': True,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
},
'x86_64-mlnx_msn4600c-r0': {
'thermal': {
'minimum_table': {
"unk_trust": {"-127:120":16},
"unk_untrust": {"-127:120":16},
}
},
'fans': {
'drawer_num': 3,
'drawer_type': 'real',
'fan_num_per_drawer': 1,
'support_fan_direction': True,
'hot_swappable': True
},
'psus': {
'psu_num': 2,
'fan_num_per_psu': 1,
'hot_swappable': True,
'led_num': 1
}
}
}

View File

@ -13,25 +13,18 @@ import subprocess
try:
from sonic_platform_base.fan_base import FanBase
from .led import FanLed, ComponentFaultyIndicator
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
LED_ON = '1'
LED_OFF = '0'
PWM_MAX = 255
FAN_PATH = "/var/run/hw-management/thermal/"
LED_PATH = "/var/run/hw-management/led/"
CONFIG_PATH = "/var/run/hw-management/config"
# 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"
COOLING_STATE_PATH = "/var/run/hw-management/thermal/cooling_cur_state"
# Platforms with unplugable FANs:
# 1. don't have fanX_status and should be treated as always present
platform_with_unplugable_fan = ['x86_64-mlnx_msn2010-r0', 'x86_64-mlnx_msn2100-r0']
class Fan(FanBase):
"""Platform-specific Fan class"""
@ -44,21 +37,28 @@ class Fan(FanBase):
PSU_FAN_SPEED = ['0x3c', '0x3c', '0x3c', '0x3c', '0x3c',
'0x3c', '0x3c', '0x46', '0x50', '0x5a', '0x64']
def __init__(self, has_fan_dir, fan_index, drawer_index = 1, psu_fan = False, platform = None):
def __init__(self, fan_index, fan_drawer, psu_fan = False):
super(Fan, self).__init__()
# API index is starting from 0, Mellanox platform index is starting from 1
self.index = fan_index + 1
self.drawer_index = drawer_index + 1
self.fan_drawer = fan_drawer
self.is_psu_fan = psu_fan
self.always_presence = False if platform not in platform_with_unplugable_fan else True
if self.fan_drawer:
self.led = ComponentFaultyIndicator(self.fan_drawer.get_led())
elif self.is_psu_fan:
from .psu import Psu
self.led = ComponentFaultyIndicator(Psu.get_shared_led())
else:
self.led = FanLed(self.index)
self.fan_min_speed_path = "fan{}_min".format(self.index)
if not self.is_psu_fan:
self.fan_speed_get_path = "fan{}_speed_get".format(self.index)
self.fan_speed_set_path = "fan{}_speed_set".format(self.index)
self.fan_presence_path = "fan{}_status".format(self.drawer_index)
self.fan_max_speed_path = "fan{}_max".format(self.index)
self._name = "fan{}".format(fan_index + 1)
self._name = "fan{}".format(self.index)
else:
self.fan_speed_get_path = "psu{}_fan1_speed_get".format(self.index)
self.fan_presence_path = "psu{}_fan1_speed_get".format(self.index)
@ -69,15 +69,7 @@ class Fan(FanBase):
self.psu_i2c_command_path = os.path.join(CONFIG_PATH, 'fan_command')
self.fan_status_path = "fan{}_fault".format(self.index)
self.fan_green_led_path = "led_fan{}_green".format(self.drawer_index)
self.fan_red_led_path = "led_fan{}_red".format(self.drawer_index)
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):
@ -99,20 +91,10 @@ class Fan(FanBase):
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 or not self.get_presence():
if 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().strip())
fan_mask = 1 << self.drawer_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)))
else:
return self.fan_drawer.get_direction()
def get_name(self):
return self._name
@ -150,17 +132,9 @@ class Fan(FanBase):
status = 1
else:
status = 0
return status == 1
else:
if self.always_presence:
status = 1
else:
try:
with open(os.path.join(FAN_PATH, self.fan_presence_path), 'r') as presence_status:
status = int(presence_status.read().strip())
except (ValueError, IOError):
status = 0
return status == 1
return self.fan_drawer.get_presence()
def _get_min_speed_in_rpm(self):
@ -281,18 +255,6 @@ class Fan(FanBase):
return status
def _get_led_capability(self):
cap_list = None
try:
with open(os.path.join(LED_PATH, self.fan_led_cap_path), 'r') as fan_led_cap:
caps = fan_led_cap.read()
cap_list = caps.split()
except (ValueError, IOError):
status = 0
return cap_list
def set_status_led(self, color):
"""
Set led to expected color
@ -304,48 +266,7 @@ class Fan(FanBase):
Returns:
bool: True if set success, False if fail.
"""
led_cap_list = self._get_led_capability()
if led_cap_list is None:
return False
if self.is_psu_fan:
# PSU fan led status is not able to set
return False
status = False
try:
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(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.fan_red_led_path)
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(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))
status = True
else:
status = False
except (ValueError, IOError):
status = False
return status
return self.led.set_status(color)
def get_status_led(self):
@ -355,26 +276,7 @@ class Fan(FanBase):
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
return self.led.get_status()
def get_speed_tolerance(self):

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
#############################################################################
# Mellanox
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fan Drawer status which are available in the platform
#
#############################################################################
import os
try:
from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform_base.fan_base import FanBase
from .led import FanLed, SharedLed
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
class MellanoxFanDrawer(FanDrawerBase):
def __init__(self, index, fan_data):
from .fan import FAN_PATH
super(MellanoxFanDrawer, self).__init__()
self._index = index + 1
self._fan_data = fan_data
self._presence_path = os.path.join(FAN_PATH, 'fan{}_status'.format(self._index))
self._led = None
def get_index(self):
return self._index
def get_led(self):
return self._led
def get_presence(self):
if not self._fan_data['hot_swappable']:
return True
status = 0
try:
with open(self._presence_path, 'r') as presence_status:
status = int(presence_status.read())
except (ValueError, IOError) as e:
status = 0
return status == 1
def get_direction(self):
if not self._fan_data['support_fan_direction'] or not self.get_presence():
return FanBase.FAN_DIRECTION_NOT_APPLICABLE
try:
from .fan import FAN_DIR
with open(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 FanBase.FAN_DIRECTION_INTAKE
else:
return FanBase.FAN_DIRECTION_EXHAUST
except (ValueError, IOError) as e:
raise RuntimeError("Failed to read fan direction status to {}".format(repr(e)))
def set_status_led(self, color):
"""
Sets the state of the fan drawer status LED
Args:
color: A string representing the color with which to set the
fan drawer status LED
Returns:
bool: True if status LED state is set successfully, False if not
"""
return True
def get_status_led(self):
"""
Gets the state of the fan drawer LED
Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings above
"""
return self._led.get_status()
class RealDrawer(MellanoxFanDrawer):
def __init__(self, index, fan_data):
super(RealDrawer, self).__init__(index, fan_data)
self._name = 'drawer{}'.format(self._index)
self._led = SharedLed(FanLed(self._index))
def get_name(self):
return self._name
class VirtualDrawer(MellanoxFanDrawer):
def __init__(self, index, fan_data):
super(VirtualDrawer, self).__init__(index, fan_data)
self._led = SharedLed(FanLed(None))
def get_name(self):
return 'N/A'

View File

@ -0,0 +1,201 @@
import os
class Led(object):
STATUS_LED_COLOR_GREEN = 'green'
STATUS_LED_COLOR_RED = 'red'
STATUS_LED_COLOR_ORANGE = 'orange'
STATUS_LED_COLOR_OFF = 'off'
LED_ON = '1'
LED_OFF = '0'
LED_PATH = "/var/run/hw-management/led/"
def set_status(self, color):
led_cap_list = self.get_capability()
if led_cap_list is None:
return False
status = False
try:
if color == Led.STATUS_LED_COLOR_GREEN:
with open(self.get_green_led_path(), 'w') as led:
led.write(Led.LED_ON)
status = True
elif color == Led.STATUS_LED_COLOR_RED:
# Some led don't support red led but support orange led, in this case we set led to orange
if Led.STATUS_LED_COLOR_RED in led_cap_list:
led_path = self.get_red_led_path()
elif Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
led_path = self.get_orange_led_path()
else:
return False
with open(led_path, 'w') as led:
led.write(Led.LED_ON)
status = True
elif color == Led.STATUS_LED_COLOR_OFF:
if Led.STATUS_LED_COLOR_GREEN in led_cap_list:
with open(self.get_green_led_path(), 'w') as led:
led.write(Led.LED_OFF)
if Led.STATUS_LED_COLOR_RED in led_cap_list:
with open(self.get_red_led_path(), 'w') as led:
led.write(Led.LED_OFF)
if Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
with open(self.get_orange_led_path(), 'w') as led:
led.write(Led.LED_OFF)
status = True
else:
status = False
except (ValueError, IOError):
status = False
return status
def get_status(self):
led_cap_list = self.get_capability()
if led_cap_list is None:
return Led.STATUS_LED_COLOR_OFF
try:
with open(self.get_green_led_path(), 'r') as led:
if Led.LED_OFF != led.read().rstrip('\n'):
return Led.STATUS_LED_COLOR_GREEN
if Led.STATUS_LED_COLOR_RED in led_cap_list:
with open(self.get_red_led_path(), 'r') as led:
if Led.LED_OFF != led.read().rstrip('\n'):
return Led.STATUS_LED_COLOR_RED
if Led.STATUS_LED_COLOR_ORANGE in led_cap_list:
with open(self.get_orange_led_path(), 'r') as led:
if Led.LED_OFF != led.read().rstrip('\n'):
return Led.STATUS_LED_COLOR_RED
except (ValueError, IOError) as e:
raise RuntimeError("Failed to read led status due to {}".format(repr(e)))
return Led.STATUS_LED_COLOR_OFF
def get_capability(self):
cap_list = None
try:
with open(self.get_led_cap_path(), 'r') as led_cap:
caps = led_cap.read()
cap_list = caps.split()
except (ValueError, IOError):
pass
return cap_list
def get_green_led_path(self):
pass
def get_red_led_path(self):
pass
def get_orange_led_path(self):
pass
def get_led_cap_path(self):
pass
class FanLed(Led):
LED_PATH = "/var/run/hw-management/led/"
def __init__(self, index):
if index is not None:
self._green_led_path = os.path.join(Led.LED_PATH, "led_fan{}_green".format(index))
self._red_led_path = os.path.join(Led.LED_PATH, "led_fan{}_red".format(index))
self._orange_led_path = os.path.join(Led.LED_PATH, "led_fan{}_orange".format(index))
self._led_cap_path = os.path.join(Led.LED_PATH, "led_fan{}_capability".format(index))
else:
self._green_led_path = os.path.join(Led.LED_PATH, "led_fan_green")
self._red_led_path = os.path.join(Led.LED_PATH, "led_fan_red")
self._orange_led_path = os.path.join(Led.LED_PATH, "led_fan_orange")
self._led_cap_path = os.path.join(Led.LED_PATH, "led_fan_capability")
self.set_status(Led.STATUS_LED_COLOR_GREEN)
def get_green_led_path(self):
return self._green_led_path
def get_red_led_path(self):
return self._red_led_path
def get_orange_led_path(self):
return self._orange_led_path
def get_led_cap_path(self):
return self._led_cap_path
class PsuLed(Led):
def __init__(self, index):
if index is not None:
self._green_led_path = os.path.join(Led.LED_PATH, "led_psu{}_green".format(index))
self._red_led_path = os.path.join(Led.LED_PATH, "led_psu{}_red".format(index))
self._orange_led_path = os.path.join(Led.LED_PATH, "led_psu{}_orange".format(index))
self._led_cap_path = os.path.join(Led.LED_PATH, "led_psu{}_capability".format(index))
else:
self._green_led_path = os.path.join(Led.LED_PATH, "led_psu_green")
self._red_led_path = os.path.join(Led.LED_PATH, "led_psu_red")
self._orange_led_path = os.path.join(Led.LED_PATH, "led_psu_orange")
self._led_cap_path = os.path.join(Led.LED_PATH, "led_psu_capability")
self.set_status(Led.STATUS_LED_COLOR_GREEN)
def get_green_led_path(self):
return self._green_led_path
def get_red_led_path(self):
return self._red_led_path
def get_orange_led_path(self):
return self._orange_led_path
def get_led_cap_path(self):
return self._led_cap_path
class SharedLed(object):
LED_PRIORITY = {
Led.STATUS_LED_COLOR_RED: 0,
Led.STATUS_LED_COLOR_GREEN: 1
}
def __init__(self, led):
self._led = led
self._virtual_leds = []
def add_virtual_leds(self, led):
self._virtual_leds.append(led)
def update_status_led(self):
target_color = Led.STATUS_LED_COLOR_GREEN
for virtual_led in self._virtual_leds:
if SharedLed.LED_PRIORITY[virtual_led.get_led_color()] < SharedLed.LED_PRIORITY[target_color]:
target_color = virtual_led.get_led_color()
return self._led.set_status(target_color)
def get_status(self):
return self._led.get_status()
class ComponentFaultyIndicator(object):
def __init__(self, shared_led):
self._color = Led.STATUS_LED_COLOR_GREEN
self._shared_led = shared_led
self._shared_led.add_virtual_leds(self)
def set_status(self, color):
self._color = color
return self._shared_led.update_status_led()
def get_led_color(self):
return self._color
def get_status(self):
return self._shared_led.get_status()

View File

@ -13,11 +13,11 @@ try:
from sonic_platform_base.psu_base import PsuBase
from sonic_daemon_base.daemon_base import Logger
from sonic_platform.fan import Fan
from .led import PsuLed, SharedLed, ComponentFaultyIndicator
from .device_data import DEVICE_DATA
except ImportError as e:
raise ImportError (str(e) + "- required module not found")
LED_ON = '1'
LED_OFF = '0'
# Global logger class instance
logger = Logger()
@ -28,16 +28,11 @@ 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
hwsku_dict_with_unplugable_psu = ['ACS-MSN2010', 'ACS-MSN2100']
# in most SKUs the file psuX_curr, psuX_volt and psuX_power contain current, voltage and power data respectively.
# in most platforms the file psuX_curr, psuX_volt and psuX_power contain current, voltage and power data respectively.
# but there are exceptions which will be handled by the following dictionary
hwsku_dict_psu = {'ACS-MSN3700': 1, 'ACS-MSN3700C': 1, 'ACS-MSN3800': 1, 'Mellanox-SN3800-D112C8': 1, 'ACS-MSN4700': 1, 'ACS-MSN3420': 1, 'ACS-MSN4600C': 1}
platform_dict_psu = {'x86_64-mlnx_msn3700-r0': 1, 'x86_64-mlnx_msn3700c-r0': 1, 'x86_64-mlnx_msn3800-r0': 1, 'x86_64-mlnx_msn4700-r0': 1}
psu_profile_list = [
# default filename convention
{
@ -56,9 +51,9 @@ psu_profile_list = [
class Psu(PsuBase):
"""Platform-specific Psu class"""
STATUS_LED_COLOR_ORANGE = "orange"
shared_led = None
def __init__(self, psu_index, sku):
def __init__(self, psu_index, platform):
global psu_list
PsuBase.__init__(self)
# PSU is 1-based on Mellanox platform
@ -66,17 +61,19 @@ class Psu(PsuBase):
psu_list.append(self.index)
self.psu_path = "/var/run/hw-management/"
psu_oper_status = "thermal/psu{}_pwr_status".format(self.index)
#psu_oper_status should always be present for all SKUs
#psu_oper_status should always be present for all platforms
self.psu_oper_status = os.path.join(self.psu_path, psu_oper_status)
self._name = "PSU{}".format(psu_index + 1)
if sku in hwsku_dict_psu:
filemap = psu_profile_list[hwsku_dict_psu[sku]]
if platform in platform_dict_psu:
filemap = psu_profile_list[platform_dict_psu[platform]]
else:
filemap = psu_profile_list[0]
if sku in hwsku_dict_with_unplugable_psu:
self.always_presence = True
self.psu_data = DEVICE_DATA[platform]['psus']
if not self.psu_data['hot_swappable']:
self.always_present = True
self.psu_voltage = None
self.psu_current = None
self.psu_power = None
@ -84,7 +81,7 @@ class Psu(PsuBase):
self.psu_temp = None
self.psu_temp_threshold = None
else:
self.always_presence = False
self.always_present = False
psu_voltage = filemap[PSU_VOLTAGE].format(self.index)
psu_voltage = os.path.join(self.psu_path, psu_voltage)
self.psu_voltage = psu_voltage
@ -105,14 +102,14 @@ class Psu(PsuBase):
self.psu_temp_threshold = os.path.join(self.psu_path, 'thermal/psu{}_temp_max'.format(self.index))
# unplugable PSU has no FAN
if sku not in hwsku_dict_with_unplugable_psu:
fan = Fan(False, psu_index, psu_index, True)
if self.psu_data['hot_swappable']:
fan = Fan(psu_index, None, True)
self._fan_list.append(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"
if self.psu_data['led_num'] == 1:
self.led = ComponentFaultyIndicator(Psu.get_shared_led())
else: # 2010/2100
self.led = PsuLed(self.index)
def get_name(self):
@ -151,8 +148,8 @@ class Psu(PsuBase):
Returns:
bool: True if PSU is present, False if not
"""
if self.always_presence:
return self.always_presence
if self.always_present:
return self.always_present
else:
status = self._read_generic_file(self.psu_presence, 0)
return status == 1
@ -199,19 +196,6 @@ class Psu(PsuBase):
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
@ -226,45 +210,7 @@ class Psu(PsuBase):
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
return self.led.set_status(color)
def get_status_led(self):
@ -274,26 +220,10 @@ class Psu(PsuBase):
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
if self.psu_data['led_num'] == 1:
return Psu.get_shared_led().get_status()
else:
return self.led.get_status()
def get_power_available_status(self):
@ -312,6 +242,12 @@ class Psu(PsuBase):
else:
return True, ""
@classmethod
def get_shared_led(cls):
if not cls.shared_led:
cls.shared_led = SharedLed(PsuLed(None))
return cls.shared_led
def get_temperature(self):
"""
Retrieves current temperature reading from PSU
@ -367,4 +303,3 @@ class Psu(PsuBase):
"""
# hw-management doesn't expose those sysfs for now
raise NotImplementedError

View File

@ -124,7 +124,7 @@ thermal_api_names = [
THERMAL_API_GET_HIGH_THRESHOLD
]
hwsku_dict_thermal = {'ACS-MSN2700': 0, 'LS-SN2700':0, 'ACS-MSN2740': 3, 'ACS-MSN2100': 1, 'ACS-MSN2410': 2, 'ACS-MSN2010': 4, 'ACS-MSN3700': 5, 'ACS-MSN3700C': 6, 'Mellanox-SN2700': 0, 'Mellanox-SN2700-D48C8': 0, 'ACS-MSN3800': 7, 'Mellanox-SN3800-D112C8': 7, 'ACS-MSN4700': 8, 'ACS-MSN3420': 9, 'ACS-MSN4600C': 9}
hwsku_dict_thermal = {'ACS-MSN2700': 0, 'LS-SN2700':0, 'ACS-MSN2740': 3, 'ACS-MSN2100': 1, 'ACS-MSN2410': 2, 'ACS-MSN2010': 4, 'ACS-MSN3700': 5, 'ACS-MSN3700C': 6, 'Mellanox-SN2700': 0, 'Mellanox-SN2700-D48C8': 0, 'ACS-MSN3800': 7, 'Mellanox-SN3800-D112C8': 7, 'ACS-MSN4700': 8, 'ACS-MSN3420': 9, 'ACS-MSN4600C': 10}
thermal_profile_list = [
# 2700
{
@ -267,7 +267,7 @@ thermal_profile_list = [
},
# 3420
{
THERMAL_DEV_CATEGORY_CPU_CORE:(0, 4),
THERMAL_DEV_CATEGORY_CPU_CORE:(0, 2),
THERMAL_DEV_CATEGORY_MODULE:(1, 60),
THERMAL_DEV_CATEGORY_PSU:(1, 2),
THERMAL_DEV_CATEGORY_CPU_PACK:(0,1),

View File

@ -2,6 +2,7 @@ class MockFan:
speed = 60
def __init__(self):
self.presence = True
self.name = None
self.status = True
def get_presence(self):
@ -16,6 +17,9 @@ class MockFan:
def get_target_speed(self):
return MockFan.speed
def get_name(self):
return self.name
class MockPsu:
def __init__(self):

View File

@ -1,17 +1,57 @@
import os
import sys
import pytest
from mock import MagicMock
from .mock_platform import MockFan
test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
sys.path.insert(0, modules_path)
from sonic_platform.fan import Fan
from sonic_platform.led import FanLed
from sonic_platform.fan_drawer import RealDrawer
from sonic_platform.device_data import DEVICE_DATA
def test_get_absence_fan_direction():
fan = Fan(True, 0, 0)
fan.get_presence = MagicMock(return_value=False)
assert fan.fan_dir is not None
fan_drawer = RealDrawer(0, DEVICE_DATA['x86_64-mlnx_msn2700-r0']['fans'])
fan = Fan(0, fan_drawer)
fan_drawer.get_presence = MagicMock(return_value=False)
assert not fan.is_psu_fan
assert fan.get_direction() == Fan.FAN_DIRECTION_NOT_APPLICABLE
def test_fan_drawer_set_status_led():
fan_drawer = RealDrawer(0, DEVICE_DATA['x86_64-mlnx_msn2700-r0']['fans'])
with pytest.raises(Exception):
fan_drawer.set_status_led(None, 'Invalid color')
with pytest.raises(Exception):
fan_drawer.set_status_led(None, Fan.STATUS_LED_COLOR_RED)
fan1 = Fan(0, fan_drawer)
fan2 = Fan(1, fan_drawer)
fan_list = fan_drawer.get_all_fans()
fan_list.append(fan1)
fan_list.append(fan2)
FanLed.set_status = MagicMock()
fan1.set_status_led(Fan.STATUS_LED_COLOR_RED)
fan_drawer.set_status_led(Fan.STATUS_LED_COLOR_RED)
FanLed.set_status.assert_called_with(Fan.STATUS_LED_COLOR_RED)
fan2.set_status_led(Fan.STATUS_LED_COLOR_GREEN)
fan_drawer.set_status_led(Fan.STATUS_LED_COLOR_GREEN)
FanLed.set_status.assert_called_with(Fan.STATUS_LED_COLOR_RED)
fan1.set_status_led(Fan.STATUS_LED_COLOR_GREEN)
fan_drawer.set_status_led(Fan.STATUS_LED_COLOR_GREEN)
FanLed.set_status.assert_called_with(Fan.STATUS_LED_COLOR_GREEN)
fan1.set_status_led(Fan.STATUS_LED_COLOR_RED)
fan_drawer.set_status_led(Fan.STATUS_LED_COLOR_RED)
FanLed.set_status.assert_called_with(Fan.STATUS_LED_COLOR_RED)

@ -1 +1 @@
Subproject commit 28c39c55666dcaef10f62492906c1399eec4ccba
Subproject commit 75698a8dd8f5a9ea2a0e72c5f7e2b3196d22571a