DellEMC S6100 : Platform2.0 API implementation [Module, Thermal]

This commit is contained in:
Arun Saravanan Balachandran 2019-08-19 09:35:30 -04:00
parent bf08a2caf3
commit c870b39589
7 changed files with 357 additions and 79 deletions

View File

@ -1,3 +1,3 @@
__all__ = ["platform", "chassis", "fan", "psu", "sfp"]
__all__ = ["platform", "chassis", "module", "fan", "psu", "sfp", "thermal"]
from sonic_platform import *

View File

@ -14,12 +14,16 @@ try:
from sonic_platform.sfp import Sfp
from sonic_platform.psu import Psu
from sonic_platform.fan import Fan
from sonic_platform.module import Module
from sonic_platform.thermal import Thermal
from eeprom import Eeprom
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
MAX_S6100_MODULE = 4
MAX_S6100_FAN = 4
MAX_S6100_PSU = 2
MAX_S6100_THERMAL = 10
class Chassis(ChassisBase):
@ -31,39 +35,6 @@ class Chassis(ChassisBase):
HWMON_NODE = os.listdir(HWMON_DIR)[0]
MAILBOX_DIR = HWMON_DIR + HWMON_NODE
PORT_START = 0
PORT_END = 63
PORTS_IN_BLOCK = (PORT_END + 1)
IOM1_PORT_START = 0
IOM2_PORT_START = 16
IOM3_PORT_START = 32
IOM4_PORT_START = 48
PORT_I2C_MAPPING = {}
# 0th Index = i2cLine, 1st Index = EepromIdx in i2cLine
EEPROM_I2C_MAPPING = {
# IOM 1
0: [6, 66], 1: [6, 67], 2: [6, 68], 3: [6, 69],
4: [6, 70], 5: [6, 71], 6: [6, 72], 7: [6, 73],
8: [6, 74], 9: [6, 75], 10: [6, 76], 11: [6, 77],
12: [6, 78], 13: [6, 79], 14: [6, 80], 15: [6, 81],
# IOM 2
16: [8, 50], 17: [8, 51], 18: [8, 52], 19: [8, 53],
20: [8, 54], 21: [8, 55], 22: [8, 56], 23: [8, 57],
24: [8, 58], 25: [8, 59], 26: [8, 60], 27: [8, 61],
28: [8, 62], 29: [8, 63], 30: [8, 64], 31: [8, 65],
# IOM 3
32: [7, 34], 33: [7, 35], 34: [7, 36], 35: [7, 37],
36: [7, 38], 37: [7, 39], 38: [7, 40], 39: [7, 41],
40: [7, 42], 41: [7, 43], 42: [7, 44], 43: [7, 45],
44: [7, 46], 45: [7, 47], 46: [7, 48], 47: [7, 49],
# IOM 4
48: [9, 18], 49: [9, 19], 50: [9, 20], 51: [9, 21],
52: [9, 22], 53: [9, 23], 54: [9, 24], 55: [9, 25],
56: [9, 26], 57: [9, 27], 58: [9, 28], 59: [9, 29],
60: [9, 30], 61: [9, 31], 62: [9, 32], 63: [9, 33]
}
reset_reason_dict = {}
reset_reason_dict[11] = ChassisBase.REBOOT_CAUSE_POWER_LOSS
reset_reason_dict[33] = ChassisBase.REBOOT_CAUSE_WATCHDOG
@ -81,6 +52,10 @@ class Chassis(ChassisBase):
ChassisBase.__init__(self)
# Initialize EEPROM
self.sys_eeprom = Eeprom()
for i in range(MAX_S6100_MODULE):
module = Module(i)
self._module_list.append(module)
for i in range(MAX_S6100_FAN):
fan = Fan(i)
self._fan_list.append(fan)
@ -89,38 +64,9 @@ class Chassis(ChassisBase):
psu = Psu(i)
self._psu_list.append(psu)
self._populate_port_i2c_mapping()
# sfp.py will read eeprom contents and retrive the eeprom data.
# It will also provide support sfp controls like reset and setting
# low power mode.
# We pass the eeprom path and sfp control path from chassis.py
# So that sfp.py implementation can be generic to all platforms
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/i2c-{1}/{1}-0050/eeprom"
sfp_ctrl_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-003e/"
for index in range(0, self.PORTS_IN_BLOCK):
eeprom_path = eeprom_base.format(self.EEPROM_I2C_MAPPING[index][0],
self.EEPROM_I2C_MAPPING[index][1])
sfp_control = sfp_ctrl_base.format(self.PORT_I2C_MAPPING[index])
sfp_node = Sfp(index, 'QSFP', eeprom_path, sfp_control, index)
self._sfp_list.append(sfp_node)
def _populate_port_i2c_mapping(self):
# port_num and i2c match
for port_num in range(0, self.PORTS_IN_BLOCK):
if((port_num >= self.IOM1_PORT_START) and
(port_num < self.IOM2_PORT_START)):
i2c_line = 14
elif((port_num >= self.IOM2_PORT_START) and
(port_num < self.IOM3_PORT_START)):
i2c_line = 16
elif((port_num >= self.IOM3_PORT_START) and
(port_num <self.IOM4_PORT_START)):
i2c_line = 15
elif((port_num >= self.IOM4_PORT_START) and
(port_num < self.PORTS_IN_BLOCK)):
i2c_line = 17
self.PORT_I2C_MAPPING[port_num] = i2c_line
for i in range(MAX_S6100_THERMAL):
thermal = Thermal(i)
self._thermal_list.append(thermal)
def _get_pmc_register(self, reg_name):
# On successful read, returns the value read from given
@ -200,6 +146,7 @@ class Chassis(ChassisBase):
OCP ONIE TlvInfo EEPROM format and values are their corresponding
values.
"""
return self.sys_eeprom.system_eeprom_info()
def get_reboot_cause(self):
"""

View File

@ -21,11 +21,42 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self):
self.eeprom_path = "/sys/class/i2c-adapter/i2c-2/2-0050/eeprom"
super(Eeprom, self).__init__(self.eeprom_path, 0, '', True)
self.eeprom_tlv_dict = dict()
try:
self.eeprom_data = self.read_eeprom()
except:
self.eeprom_data = "N/A"
raise RuntimeError("Eeprom is not Programmed")
else:
eeprom = self.eeprom_data
if not self.is_valid_tlvinfo_header(eeprom):
return
total_length = (ord(eeprom[9]) << 8) | ord(eeprom[10])
tlv_index = self._TLV_INFO_HDR_LEN
tlv_end = self._TLV_INFO_HDR_LEN + total_length
while (tlv_index + 2) < len(eeprom) and tlv_index < tlv_end:
if not self.is_valid_tlv(eeprom[tlv_index:]):
break
tlv = eeprom[tlv_index:tlv_index + 2
+ ord(eeprom[tlv_index + 1])]
code = "0x%02X" % (ord(tlv[0]))
if ord(tlv[0]) == self._TLV_CODE_VENDOR_EXT:
value = str((ord(tlv[2]) << 24) | (ord(tlv[3]) << 16) |
(ord(tlv[4]) << 8) | ord(tlv[5]))
value += str(tlv[6:6 + ord(tlv[1])])
else:
name, value = self.decoder(None, tlv)
self.eeprom_tlv_dict[code] = value
if ord(eeprom[tlv_index]) == self._TLV_CODE_CRC_32:
break
tlv_index += ord(eeprom[tlv_index+1]) + 2
def serial_number_str(self):
@ -76,3 +107,10 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
return results[2]
def system_eeprom_info(self):
"""
Returns a dictionary, where keys are the type code defined in
ONIE EEPROM format and values are their corresponding values
found in the system EEPROM.
"""
return self.eeprom_tlv_dict

View File

@ -26,7 +26,7 @@ class Fan(FanBase):
HWMON_NODE = os.listdir(HWMON_DIR)[0]
MAILBOX_DIR = HWMON_DIR + HWMON_NODE
def __init__(self, fantray_index, fan_index=1, psu_fan=False):
def __init__(self, fantray_index=1, fan_index=1, psu_fan=False):
self.is_psu_fan = psu_fan
if not self.is_psu_fan:
# API index is starting from 0, DellEMC platform index is starting
@ -74,7 +74,7 @@ class Fan(FanBase):
return "FanTray{}-Fan{}".format(
self.fantrayindex, self.fanindex - 1)
else:
return "PSU{} Fan".format(self.index - 10)
return "PSU{} Fan".format(self.fanindex - 10)
def get_model(self):
"""
@ -209,7 +209,7 @@ class Fan(FanBase):
status = False
return status
def get_target_speed(self):
def get_target_speed(self):
"""
Retrieves the target (expected) speed of the fan
Returns:

View File

@ -0,0 +1,136 @@
#!/usr/bin/env python
########################################################################
# DellEMC S6100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Modules' information which are available in the platform
#
########################################################################
try:
import os
from sonic_platform_base.module_base import ModuleBase
from sonic_platform.sfp import Sfp
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Module(ModuleBase):
"""DellEMC Platform-specific Module class"""
HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/"
HWMON_NODE = os.listdir(HWMON_DIR)[0]
MAILBOX_DIR = HWMON_DIR + HWMON_NODE
IOM_I2C_MAPPING = { 1: 14, 2: 16, 3: 15, 4: 17 }
EEPROM_I2C_MAPPING = {
# IOM 1
0: [6, 66], 1: [6, 67], 2: [6, 68], 3: [6, 69],
4: [6, 70], 5: [6, 71], 6: [6, 72], 7: [6, 73],
8: [6, 74], 9: [6, 75], 10: [6, 76], 11: [6, 77],
12: [6, 78], 13: [6, 79], 14: [6, 80], 15: [6, 81],
# IOM 2
16: [8, 50], 17: [8, 51], 18: [8, 52], 19: [8, 53],
20: [8, 54], 21: [8, 55], 22: [8, 56], 23: [8, 57],
24: [8, 58], 25: [8, 59], 26: [8, 60], 27: [8, 61],
28: [8, 62], 29: [8, 63], 30: [8, 64], 31: [8, 65],
# IOM 3
32: [7, 34], 33: [7, 35], 34: [7, 36], 35: [7, 37],
36: [7, 38], 37: [7, 39], 38: [7, 40], 39: [7, 41],
40: [7, 42], 41: [7, 43], 42: [7, 44], 43: [7, 45],
44: [7, 46], 45: [7, 47], 46: [7, 48], 47: [7, 49],
# IOM 4
48: [9, 18], 49: [9, 19], 50: [9, 20], 51: [9, 21],
52: [9, 22], 53: [9, 23], 54: [9, 24], 55: [9, 25],
56: [9, 26], 57: [9, 27], 58: [9, 28], 59: [9, 29],
60: [9, 30], 61: [9, 31], 62: [9, 32], 63: [9, 33]
}
def __init__(self, module_index):
# Modules are 1-based in DellEMC platforms
self.index = module_index + 1
self.port_start = (self.index - 1) * 16
self.port_end = (self.index * 16) - 1
self.port_i2c_line = self.IOM_I2C_MAPPING[self.index]
self.iom_status_reg = "iom_status"
self.iom_presence_reg = "iom_presence"
# Overriding _sfp_list class variable defined in ModuleBase, to
# make it unique per Module object
self._sfp_list = []
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/i2c-{1}/{1}-0050/eeprom"
sfp_ctrl_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-003e/"
# sfp.py will read eeprom contents and retrive the eeprom data.
# It will also provide support sfp controls like reset and setting
# low power mode.
for index in range(self.port_start, self.port_end + 1):
eeprom_path = eeprom_base.format(self.EEPROM_I2C_MAPPING[index][0],
self.EEPROM_I2C_MAPPING[index][1])
sfp_control = sfp_ctrl_base.format(self.port_i2c_line)
sfp_node = Sfp(index, 'QSFP', eeprom_path, sfp_control, index)
self._sfp_list.append(sfp_node)
def _get_pmc_register(self, reg_name):
# On successful read, returns the value read from given
# reg_name and on failure returns 'ERR'
rv = 'ERR'
mb_reg_file = self.MAILBOX_DIR + '/' + reg_name
if (not os.path.isfile(mb_reg_file)):
return rv
try:
with open(mb_reg_file, 'r') as fd:
rv = fd.read()
except Exception as error:
rv = 'ERR'
rv = rv.rstrip('\r\n')
rv = rv.lstrip(" ")
return rv
def get_name(self):
"""
Retrieves the name of the device
Returns:
string: The name of the device
"""
return "IOM{}: 16xQSFP+".format(self.index)
def get_presence(self):
"""
Retrieves the presence of the Module
Returns:
bool: True if Module is present, False if not
"""
status = False
iom_presence = self._get_pmc_register(self.iom_presence_reg)
if (iom_presence != 'ERR'):
iom_presence = int(iom_presence,16)
if (~iom_presence & (1 << (self.index - 1))):
status = True
return status
def get_status(self):
"""
Retrieves the operational status of the Module
Returns:
bool: True if Module is operating properly, False if not
"""
status = False
iom_status = self._get_pmc_register(self.iom_status_reg)
if (iom_status != 'ERR'):
iom_status = int(iom_status,16)
if (~iom_status & (1 << (self.index - 1))):
status = True
return status

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
########################################################################
# DellEMC
# DellEMC S6100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the PSUs' information which are available in the platform
@ -43,10 +43,10 @@ class Psu(PsuBase):
self._fan_list = []
# Passing True to specify it is a PSU fan
psu_fan = Fan(self.index, True)
psu_fan = Fan(fan_index=self.index, psu_fan=True)
self._fan_list.append(psu_fan)
def get_pmc_register(self, reg_name):
def _get_pmc_register(self, reg_name):
# On successful read, returns the value read from given
# reg_name and on failure returns 'ERR'
rv = 'ERR'
@ -82,7 +82,7 @@ class Psu(PsuBase):
bool: True if PSU is present, False if not
"""
status = False
psu_presence = self.get_pmc_register(self.psu_presence_reg)
psu_presence = self._get_pmc_register(self.psu_presence_reg)
if (psu_presence != 'ERR'):
psu_presence = int(psu_presence, 16)
# Checking whether bit 0 is not set
@ -100,7 +100,7 @@ class Psu(PsuBase):
"""
# For Serial number "US-01234D-54321-25A-0123-A00", the part
# number is "01234D"
psu_serialno = self.get_pmc_register(self.psu_serialno_reg)
psu_serialno = self._get_pmc_register(self.psu_serialno_reg)
if (psu_serialno != 'ERR') and self.get_presence():
if (len(psu_serialno.split('-')) > 1):
psu_partno = psu_serialno.split('-')[1]
@ -119,7 +119,7 @@ class Psu(PsuBase):
string: Serial number of PSU
"""
# Sample Serial number format "US-01234D-54321-25A-0123-A00"
psu_serialno = self.get_pmc_register(self.psu_serialno_reg)
psu_serialno = self._get_pmc_register(self.psu_serialno_reg)
if (psu_serialno == 'ERR') or not self.get_presence():
psu_serialno = 'NA'
@ -133,7 +133,7 @@ class Psu(PsuBase):
bool: True if PSU is operating properly, False if not
"""
status = False
psu_status = self.get_pmc_register(self.psu_presence_reg)
psu_status = self._get_pmc_register(self.psu_presence_reg)
if (psu_status != 'ERR'):
psu_status = int(psu_status, 16)
# Checking whether both bit 3 and bit 2 are not set
@ -150,7 +150,7 @@ class Psu(PsuBase):
A float number, the output voltage in volts,
e.g. 12.1
"""
psu_voltage = self.get_pmc_register(self.psu_voltage_reg)
psu_voltage = self._get_pmc_register(self.psu_voltage_reg)
if (psu_voltage != 'ERR') and self.get_presence():
# Converting the value returned by driver which is in
# millivolts to volts
@ -168,7 +168,7 @@ class Psu(PsuBase):
A float number, electric current in amperes,
e.g. 15.4
"""
psu_current = self.get_pmc_register(self.psu_current_reg)
psu_current = self._get_pmc_register(self.psu_current_reg)
if (psu_current != 'ERR') and self.get_presence():
# Converting the value returned by driver which is in
# milliamperes to amperes
@ -186,7 +186,7 @@ class Psu(PsuBase):
A float number, the power in watts,
e.g. 302.6
"""
psu_power = self.get_pmc_register(self.psu_power_reg)
psu_power = self._get_pmc_register(self.psu_power_reg)
if (psu_power != 'ERR') and self.get_presence():
# Converting the value returned by driver which is in
# microwatts to watts
@ -196,6 +196,20 @@ class Psu(PsuBase):
return psu_power
def get_powergood_status(self):
"""
Retrieves the powergood status of PSU
Returns:
A boolean, True if PSU has stablized its output voltages and
passed all its internal self-tests, False if not.
"""
status = False
if self.get_status() and self._fan_list[0].get_status():
status = True
return status
def set_status_led(self):
"""
Sets the state of the PSU status LED

View File

@ -0,0 +1,143 @@
#!/usr/bin/env python
########################################################################
# DellEMC S6100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Thermals' information which are available in the platform
#
########################################################################
try:
import os
from sonic_platform_base.thermal_base import ThermalBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Thermal(ThermalBase):
"""DellEMC Platform-specific Thermal class"""
THERMAL_NAME = (
'CPU On-board', 'ASIC On-board Front', 'System Front',
'ASIC On-board Rear', 'Front GE board', 'Front SFP+ board',
'CPU Core 0', 'CPU Core 1', 'CPU Core 2', 'CPU Core 3'
)
def __init__(self, thermal_index):
self.is_cpu_thermal = False
self.index = thermal_index + 1
if self.index < 7:
if self.index < 5:
hwmon_temp_index = self.index
else:
hwmon_temp_index = self.index + 5
dev_path = "/sys/devices/platform/SMF.512/hwmon/"
else:
hwmon_temp_index = self.index - 5
self.is_cpu_thermal = True
dev_path = "/sys/devices/platform/coretemp.0/hwmon/"
hwmon_node = os.listdir(dev_path)[0]
self.HWMON_DIR = dev_path + hwmon_node + '/'
self.thermal_status_file = self.HWMON_DIR \
+ "temp{}_alarm".format(hwmon_temp_index)
self.thermal_temperature_file = self.HWMON_DIR \
+ "temp{}_input".format(hwmon_temp_index)
self.thermal_high_threshold_file = self.HWMON_DIR \
+ "temp{}_crit".format(hwmon_temp_index)
def _read_sysfs_file(self, sysfs_file):
# On successful read, returns the value read from given
# sysfs_file and on failure returns 'ERR'
rv = 'ERR'
if (not os.path.isfile(sysfs_file)):
return rv
try:
with open(sysfs_file, 'r') as fd:
rv = fd.read()
except Exception as error:
rv = 'ERR'
rv = rv.rstrip('\r\n')
rv = rv.lstrip(" ")
return rv
def get_name(self):
"""
Retrieves the name of the thermal
Returns:
string: The name of the thermal
"""
return self.THERMAL_NAME[self.index - 1]
def get_presence(self):
"""
Retrieves the presence of the thermal
Returns:
bool: True if thermal is present, False if not
"""
return True
def get_status(self):
"""
Retrieves the operational status of the thermal
Returns:
A boolean value, True if thermal is operating properly,
False if not
"""
status = False
if self.is_cpu_thermal:
status = True
else:
thermal_status = self._read_sysfs_file(self.thermal_status_file)
if (thermal_status != 'ERR'):
thermal_status = int(thermal_status, 16)
if thermal_status != 5:
status = True
return status
def get_temperature(self):
"""
Retrieves current temperature reading from thermal
Returns:
A float number of current temperature in Celsius up to
nearest thousandth of one degree Celsius, e.g. 30.125
"""
thermal_temperature = self._read_sysfs_file(
self.thermal_temperature_file)
if (thermal_temperature != 'ERR'):
thermal_temperature = float(thermal_temperature) / 1000
else:
thermal_temperature = 0
return "{:.3f}".format(thermal_temperature)
def get_high_threshold(self):
"""
Retrieves the high threshold temperature of thermal
Returns:
A float number, the high threshold temperature of thermal in
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
thermal_high_threshold = self._read_sysfs_file(
self.thermal_high_threshold_file)
if (thermal_high_threshold != 'ERR'):
thermal_high_threshold = float(thermal_high_threshold) / 1000
else:
thermal_high_threshold = 0
return "{:.3f}".format(thermal_high_threshold)