Skip to content

Commit 1d15022

Browse files
stephenxslguohan
authored andcommitted
[Mellanox] support new platform api, thermal and psu part (#3175)
* support new platform api, thermal and psu part for psu, all APIs are supported. for thermal, we support get_temperature, get_high_threshold for the thermal sensors of cpu core, cpu pack, psu and sfp module and get_temperature for the ambient thermal sensors around the asic, port, fan, comex and board. * 1. address review comments 2. improve the handling of PSU inserting/removal 3. tolerance diverse psu thermal sensor file name conventions * 1. adjust thermal code according to the latest version of hw-management 2. check power_good_status rather than whether file existing ahead of reading voltage, current and power of PSU
1 parent 40c8bc1 commit 1d15022

File tree

3 files changed

+498
-34
lines changed

3 files changed

+498
-34
lines changed

platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88
#
99
#############################################################################
1010

11-
import sys
12-
1311
try:
1412
from sonic_platform_base.chassis_base import ChassisBase
1513
from sonic_platform.psu import Psu
1614
from sonic_platform.fan import Fan
1715
from sonic_platform.fan import FAN_PATH
1816
from sonic_platform.sfp import SFP
17+
from sonic_platform.thermal import Thermal, initialize_thermals
1918
from sonic_platform.watchdog import get_watchdog
2019
from sonic_daemon_base.daemon_base import Logger
2120
from eeprom import Eeprom
2221
from os import listdir
2322
from os.path import isfile, join
23+
import sys
2424
import io
2525
import re
2626
import subprocess
@@ -64,12 +64,12 @@
6464
COMPONENT_CPLD2 = "CPLD2"
6565

6666
# Global logger class instance
67-
SYSLOG_IDENTIFIER = "mlnx-chassis"
67+
SYSLOG_IDENTIFIER = "mlnx-chassis-api"
6868
logger = Logger(SYSLOG_IDENTIFIER)
6969

7070
# magic code defnition for port number, qsfp port position of each hwsku
7171
# port_position_tuple = (PORT_START, QSFP_PORT_START, PORT_END, PORT_IN_BLOCK, EEPROM_OFFSET)
72-
hwsku_dict = {'ACS-MSN2700': 0, "LS-SN2700":0, 'ACS-MSN2740': 0, 'ACS-MSN2100': 1, 'ACS-MSN2410': 2, 'ACS-MSN2010': 3, 'ACS-MSN3700': 0, 'ACS-MSN3700C': 0, 'Mellanox-SN2700': 0, 'Mellanox-SN2700-D48C8': 0}
72+
hwsku_dict_port = {'ACS-MSN2700': 0, "LS-SN2700":0, 'ACS-MSN2740': 0, 'ACS-MSN2100': 1, 'ACS-MSN2410': 2, 'ACS-MSN2010': 3, 'ACS-MSN3700': 0, 'ACS-MSN3700C': 0, 'Mellanox-SN2700': 0, 'Mellanox-SN2700-D48C8': 0}
7373
port_position_tuple_list = [(0, 0, 31, 32, 1), (0, 0, 15, 16, 1), (0, 48, 55, 56, 1),(0, 18, 21, 22, 1)]
7474

7575
class Chassis(ChassisBase):
@@ -78,9 +78,12 @@ class Chassis(ChassisBase):
7878
def __init__(self):
7979
super(Chassis, self).__init__()
8080

81+
# Initialize SKU name
82+
self.sku_name = self._get_sku_name()
83+
8184
# Initialize PSU list
8285
for index in range(MLNX_NUM_PSU):
83-
psu = Psu(index)
86+
psu = Psu(index, self.sku_name)
8487
self._psu_list.append(psu)
8588

8689
# Initialize watchdog
@@ -112,6 +115,9 @@ def __init__(self):
112115
sfp_module = SFP(index, 'SFP')
113116
self._sfp_list.append(sfp_module)
114117

118+
# Initialize thermals
119+
initialize_thermals(self.sku_name, self._thermal_list, self._psu_list)
120+
115121
# Initialize EEPROM
116122
self.eeprom = Eeprom()
117123

@@ -137,10 +143,13 @@ def _extract_num_of_fans_and_fan_drawers(self):
137143

138144
return num_of_fan, num_of_drawer
139145

140-
def _get_port_position_tuple_by_sku_name(self):
146+
def _get_sku_name(self):
141147
p = subprocess.Popen(GET_HWSKU_CMD, shell=True, stdout=subprocess.PIPE)
142148
out, err = p.communicate()
143-
position_tuple = port_position_tuple_list[hwsku_dict[out.rstrip('\n')]]
149+
return out.rstrip('\n')
150+
151+
def _get_port_position_tuple_by_sku_name(self):
152+
position_tuple = port_position_tuple_list[hwsku_dict_port[self.sku_name]]
144153
return position_tuple
145154

146155
def get_base_mac(self):
@@ -183,8 +192,8 @@ def _read_generic_file(self, filename, len):
183192
result = fileobj.read(len)
184193
fileobj.close()
185194
return result
186-
except:
187-
logger.log_warning("Fail to read file {}, maybe it doesn't exist".format(filename))
195+
except Exception as e:
196+
logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
188197
return ''
189198

190199
def _verify_reboot_cause(self, filename):

platform/mellanox/mlnx-platform-api/sonic_platform/psu.py

Lines changed: 123 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,48 +8,113 @@
88
#
99
#############################################################################
1010

11-
import os.path
12-
1311
try:
12+
import os.path
1413
from sonic_platform_base.psu_base import PsuBase
14+
from sonic_daemon_base.daemon_base import Logger
1515
from sonic_platform.fan import Fan
1616
except ImportError as e:
1717
raise ImportError (str(e) + "- required module not found")
1818

19+
# Global logger class instance
20+
SYSLOG_IDENTIFIER = "mlnx-psu-api"
21+
logger = Logger(SYSLOG_IDENTIFIER)
22+
1923
psu_list = []
2024

25+
PSU_CURRENT = "current"
26+
PSU_VOLTAGE = "voltage"
27+
PSU_POWER = "power"
28+
29+
# SKUs with unplugable PSUs:
30+
# 1. don't have psuX_status and should be treated as always present
31+
# 2. don't have voltage, current and power values
32+
hwsku_dict_with_unplugable_psu = ['ACS-MSN2010', 'ACS-MSN2100']
33+
34+
# in most SKUs the file psuX_curr, psuX_volt and psuX_power contain current, voltage and power data respectively.
35+
# but there are exceptions which will be handled by the following dictionary
36+
hwsku_dict_psu = {'ACS-MSN3700': 1, 'ACS-MSN3700C': 1, 'ACS-MSN3800': 1}
37+
psu_profile_list = [
38+
# default filename convention
39+
{
40+
PSU_CURRENT : "power/psu{}_curr",
41+
PSU_VOLTAGE : "power/psu{}_volt",
42+
PSU_POWER : "power/psu{}_power"
43+
},
44+
# for 3700, 3700c, 3800
45+
{
46+
PSU_CURRENT : "power/psu{}_curr",
47+
PSU_VOLTAGE : "power/psu{}_volt_out2",
48+
PSU_POWER : "power/psu{}_power"
49+
}
50+
]
51+
2152
class Psu(PsuBase):
2253
"""Platform-specific Psu class"""
23-
def __init__(self, psu_index):
54+
def __init__(self, psu_index, sku):
2455
global psu_list
2556
PsuBase.__init__(self)
2657
# PSU is 1-based on Mellanox platform
2758
self.index = psu_index + 1
2859
psu_list.append(self.index)
29-
self.psu_path = "/var/run/hw-management/thermal/"
30-
self.psu_oper_status = "psu{}_pwr_status".format(self.index)
31-
self.psu_presence = "psu{}_status".format(self.index)
32-
if os.path.exists(os.path.join(self.psu_path, self.psu_presence)):
33-
self.presence_file_exists = True
60+
self.psu_path = "/var/run/hw-management/"
61+
psu_oper_status = "thermal/psu{}_pwr_status".format(self.index)
62+
#psu_oper_status should always be present for all SKUs
63+
self.psu_oper_status = os.path.join(self.psu_path, psu_oper_status)
64+
65+
if sku in hwsku_dict_psu:
66+
filemap = psu_profile_list[hwsku_dict_psu[sku]]
67+
else:
68+
filemap = psu_profile_list[0]
69+
70+
if sku in hwsku_dict_with_unplugable_psu:
71+
self.always_presence = True
72+
self.psu_voltage = None
73+
self.psu_current = None
74+
self.psu_power = None
75+
self.psu_presence = None
3476
else:
35-
self.presence_file_exists = False
77+
self.always_presence = False
78+
psu_voltage = filemap[PSU_VOLTAGE].format(self.index)
79+
psu_voltage = os.path.join(self.psu_path, psu_voltage)
80+
self.psu_voltage = psu_voltage
81+
82+
psu_current = filemap[PSU_CURRENT].format(self.index)
83+
psu_current = os.path.join(self.psu_path, psu_current)
84+
self.psu_current = psu_current
85+
86+
psu_power = filemap[PSU_POWER].format(self.index)
87+
psu_power = os.path.join(self.psu_path, psu_power)
88+
self.psu_power = psu_power
89+
90+
psu_presence = "thermal/psu{}_status".format(self.index)
91+
psu_presence = os.path.join(self.psu_path, psu_presence)
92+
self.psu_presence = psu_presence
93+
3694
fan = Fan(psu_index, psu_index, True)
3795
if fan.get_presence():
3896
self._fan = fan
3997

40-
def get_status(self):
98+
def _read_generic_file(self, filename, len):
99+
"""
100+
Read a generic file, returns the contents of the file
101+
"""
102+
result = 0
103+
try:
104+
with open(filename, 'r') as fileobj:
105+
result = int(fileobj.read())
106+
except Exception as e:
107+
logger.log_info("Fail to read file {} due to {}".format(filename, repr(e)))
108+
return result
109+
110+
def get_powergood_status(self):
41111
"""
42112
Retrieves the operational status of power supply unit (PSU) defined
43113
44114
Returns:
45115
bool: True if PSU is operating properly, False if not
46116
"""
47-
status = 0
48-
try:
49-
with open(os.path.join(self.psu_path, self.psu_oper_status), 'r') as power_status:
50-
status = int(power_status.read())
51-
except (ValueError, IOError):
52-
status = 0
117+
status = self._read_generic_file(os.path.join(self.psu_path, self.psu_oper_status), 0)
53118

54119
return status == 1
55120

@@ -60,15 +125,48 @@ def get_presence(self):
60125
Returns:
61126
bool: True if PSU is present, False if not
62127
"""
63-
status = 0
64-
if self.presence_file_exists:
65-
try:
66-
with open(os.path.join(self.psu_path, self.psu_presence), 'r') as presence_status:
67-
status = int(presence_status.read())
68-
except (ValueError, IOError):
69-
status = 0
128+
if self.always_presence:
129+
return self.always_presence
70130
else:
71-
status = self.index in psu_list
131+
status = self._read_generic_file(self.psu_presence, 0)
132+
return status == 1
72133

73-
return status == 1
134+
def get_voltage(self):
135+
"""
136+
Retrieves current PSU voltage output
74137
138+
Returns:
139+
A float number, the output voltage in volts,
140+
e.g. 12.1
141+
"""
142+
if self.psu_voltage is not None and self.get_powergood_status():
143+
voltage = self._read_generic_file(self.psu_voltage, 0)
144+
return float(voltage) / 1000
145+
else:
146+
return None
147+
148+
def get_current(self):
149+
"""
150+
Retrieves present electric current supplied by PSU
151+
152+
Returns:
153+
A float number, the electric current in amperes, e.g 15.4
154+
"""
155+
if self.psu_current is not None and self.get_powergood_status():
156+
amperes = self._read_generic_file(self.psu_current, 0)
157+
return float(amperes) / 1000
158+
else:
159+
return None
160+
161+
def get_power(self):
162+
"""
163+
Retrieves current energy supplied by PSU
164+
165+
Returns:
166+
A float number, the power in watts, e.g. 302.6
167+
"""
168+
if self.psu_power is not None and self.get_powergood_status():
169+
power = self._read_generic_file(self.psu_power, 0)
170+
return float(power) / 1000000
171+
else:
172+
return None

0 commit comments

Comments
 (0)