DellEMC: N3248TE Platform API 2.0 changes (#9735)

Why I did it
N3248TE - Platform API 2.0 changes

How I did it
Implemented the functional API's needed for Platform API 2.0

How to verify it
Used the API 2.0 test suite to validate the test cases.
This commit is contained in:
arunlk-dell 2022-03-05 07:23:35 +05:30 committed by GitHub
parent d959c4adcd
commit b2409be2f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 388 additions and 38 deletions

View File

@ -19,7 +19,6 @@ try:
from sonic_platform.psu import Psu from sonic_platform.psu import Psu
from sonic_platform.thermal import Thermal from sonic_platform.thermal import Thermal
from sonic_platform.watchdog import Watchdog from sonic_platform.watchdog import Watchdog
from sonic_platform.fan import Fan
from sonic_platform.fan_drawer import FanDrawer from sonic_platform.fan_drawer import FanDrawer
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -62,6 +61,12 @@ class Chassis(ChassisBase):
53: 24, 53: 24,
54: 25, 54: 25,
} }
SYSTEM_LED_COLORS = {
"green",
"blink_green",
"yellow",
"blink_yellow"
}
def __init__(self): def __init__(self):
ChassisBase.__init__(self) ChassisBase.__init__(self)
@ -89,8 +94,6 @@ class Chassis(ChassisBase):
self._watchdog = Watchdog() self._watchdog = Watchdog()
self._num_sfps = 54 self._num_sfps = 54
self._num_fans = MAX_N3248TE_FANTRAY * MAX_N3248TE_FAN self._num_fans = MAX_N3248TE_FANTRAY * MAX_N3248TE_FAN
self._fan_list = [Fan(i, j) for i in range(MAX_N3248TE_FANTRAY) \
for j in range(MAX_N3248TE_FAN)]
for k in range(MAX_N3248TE_FANTRAY): for k in range(MAX_N3248TE_FANTRAY):
fandrawer = FanDrawer(k) fandrawer = FanDrawer(k)
self._fan_drawer_list.append(fandrawer) self._fan_drawer_list.append(fandrawer)
@ -105,6 +108,7 @@ class Chassis(ChassisBase):
self._global_port_pres_dict[port_num] = '1' if presence else '0' self._global_port_pres_dict[port_num] = '1' if presence else '0'
self._watchdog = Watchdog() self._watchdog = Watchdog()
self.status_led_reg = "system_led"
self.locator_led_reg = "locator_led" self.locator_led_reg = "locator_led"
self.LOCATOR_LED_ON = "blink_blue" self.LOCATOR_LED_ON = "blink_blue"
self.LOCATOR_LED_OFF = self.STATUS_LED_COLOR_OFF self.LOCATOR_LED_OFF = self.STATUS_LED_COLOR_OFF
@ -125,7 +129,6 @@ class Chassis(ChassisBase):
cpld_reg_file = self.CPLD_DIR + '/' + reg_name cpld_reg_file = self.CPLD_DIR + '/' + reg_name
if (not os.path.isfile(cpld_reg_file)): if (not os.path.isfile(cpld_reg_file)):
#print "open error"
return rv return rv
try: try:
@ -136,6 +139,40 @@ class Chassis(ChassisBase):
return rv return rv
def get_status_led(self):
"""
Gets the current system LED color
Returns:
A string that represents the supported color
"""
color = self._get_cpld_register(self.status_led_reg)
if color not in list(self.SYSTEM_LED_COLORS):
return self.sys_ledcolor
return color
def initizalize_system_led(self):
self.sys_ledcolor = "green"
def set_status_led(self,color):
"""
Set system LED status based on the color type passed in the argument.
Argument: Color to be set
Returns:
bool: True is specified color is set, Otherwise return False
"""
if color not in list(self.SYSTEM_LED_COLORS):
return False
if(not self._set_cpld_register(self.status_led_reg, color)):
return False
self.sys_ledcolor = color
return True
# check for this event change for sfp / do we need to handle timeout/sleep # check for this event change for sfp / do we need to handle timeout/sleep
def get_change_event(self, timeout=0): def get_change_event(self, timeout=0):
@ -193,7 +230,7 @@ class Chassis(ChassisBase):
Returns: Returns:
string: The name of the chassis string: The name of the chassis
""" """
return self._eeprom.modelstr().decode() return self._eeprom.modelstr()
def get_presence(self): def get_presence(self):
""" """
@ -348,3 +385,28 @@ class Chassis(ChassisBase):
return self.LOCATOR_LED_OFF return self.LOCATOR_LED_OFF
else: else:
return self.LOCATOR_LED_OFF return self.LOCATOR_LED_OFF
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return -1
def is_replaceable(self):
"""
Indicate whether Chassis is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_revision(self):
"""
Retrives the hardware revision of the device
Returns:
string: Revision value of device
"""
return self._eeprom.revision_str()

View File

@ -80,3 +80,52 @@ class Component(ComponentBase):
A boolean, True if install was successful, False if not A boolean, True if install was successful, False if not
""" """
return False return False
def get_presence(self):
"""
Retrieves the presence of the component
Returns:
bool: True if present, False if not
"""
return True
def get_model(self):
"""
Retrieves the part number of the component
Returns:
string: Part number of component
"""
return 'NA'
def get_serial(self):
"""
Retrieves the serial number of the component
Returns:
string: Serial number of component
"""
return 'NA'
def get_status(self):
"""
Retrieves the operational status of the component
Returns:
bool: True if component is operating properly, False if not
"""
return True
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return -1
def is_replaceable(self):
"""
Indicate whether component is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
############################################################################# #############################################################################
# DellEmc Z9332F # DellEmc N3248TE
# #
# Platform and model specific eeprom subclass, inherits from the base class, # Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings: # and provides the followings:
@ -11,7 +11,6 @@
try: try:
import os.path import os.path
from sonic_eeprom import eeprom_tlvinfo from sonic_eeprom import eeprom_tlvinfo
import binascii
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -75,9 +74,9 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
(is_valid, t) = self.get_tlv_field( (is_valid, t) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_MAC_BASE) self.eeprom_data, self._TLV_CODE_MAC_BASE)
if not is_valid or t[1] != 6: if not is_valid or t[1] != 6:
return super(eeprom_tlvinfo.TlvInfoDecoder, self).switchaddrstr(t) return super(TlvInfoDecoder, self).switchaddrstr(e)
return ":".join([binascii.b2a_hex(T) for T in t[2]]) return ":".join(["{:02x}".format(T) for T in t[2]]).upper()
def modelstr(self): def modelstr(self):
""" """
@ -88,7 +87,7 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
if not is_valid: if not is_valid:
return "N/A" return "N/A"
return results[2] return results[2].decode('ascii')
def part_number_str(self): def part_number_str(self):
""" """
@ -99,7 +98,7 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
if not is_valid: if not is_valid:
return "N/A" return "N/A"
return results[2] return results[2].decode('ascii')
def serial_str(self): def serial_str(self):
""" """
@ -110,7 +109,7 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
if not is_valid: if not is_valid:
return "N/A" return "N/A"
return results[2] return results[2].decode('ascii')
def revision_str(self): def revision_str(self):
""" """
@ -121,7 +120,7 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
if not is_valid: if not is_valid:
return "N/A" return "N/A"
return results[2] return results[2].decode('ascii')
def system_eeprom_info(self): def system_eeprom_info(self):
""" """
@ -130,5 +129,3 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
found in the system EEPROM. found in the system EEPROM.
""" """
return self.eeprom_tlv_dict return self.eeprom_tlv_dict

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
######################################################################## ########################################################################
# DellEMC Z9332F # DellEMC N3248TE
# #
# Module contains an implementation of SONiC Platform Base API and # Module contains an implementation of SONiC Platform Base API and
# provides the Fans' information which are available in the platform. # provides the Fans' information which are available in the platform.
@ -26,15 +26,19 @@ class Fan(FanBase):
self.presence_reg = "fan{}_prs".format(fantray_index) self.presence_reg = "fan{}_prs".format(fantray_index)
self.dir_reg = "fan{}_dir".format(fantray_index) self.dir_reg = "fan{}_dir".format(fantray_index)
self.rpm_file = "/sys/bus/i2c/devices/7-002c/fan{}_input".format(fantray_index+1) self.rpm_file = "/sys/bus/i2c/devices/7-002c/fan{}_input".format(fantray_index+1)
self.status_file = "/sys/bus/i2c/devices/7-002c/fan{}_fault".format(fantray_index+1)
self.eeprom = "/sys/bus/i2c/devices/{}-0050/eeprom".format(15 + fantray_index) self.eeprom = "/sys/bus/i2c/devices/{}-0050/eeprom".format(15 + fantray_index)
self.fantray_index = fantray_index self.fantray_index = fantray_index
self.fan_index = fan_index
else: else:
self.presence_reg = "psu{}_prs".format(fantray_index) self.psu_index = fan_index - 1
self.psu_index = fantray_index
self.dependancy = dependency self.dependancy = dependency
self.presence_reg = "psu{}_prs".format(self.psu_index)
self.status_reg = "psu{}_status".format(self.psu_index)
self.dir_reg = "" self.dir_reg = ""
self.dps_hwmon = "/sys/bus/i2c/devices/{}-005e/hwmon/".format(fantray_index+10) self.dps_hwmon = "/sys/bus/i2c/devices/{}-005e/hwmon/".format(self.psu_index + 10)
self.eeprom = "/sys/bus/i2c/devices/{}-0056/eeprom".format(10 + fantray_index) self.eeprom = "/sys/bus/i2c/devices/{}-0056/eeprom".format(self.psu_index + 10)
self.fan_index = fan_index
self.max_speed = 0 self.max_speed = 0
def _get_cpld_register(self, reg_name): def _get_cpld_register(self, reg_name):
@ -55,9 +59,9 @@ class Fan(FanBase):
String: The name of the device String: The name of the device
""" """
if self.is_psu_fan: if self.is_psu_fan:
return "PSU{} Fan".format(self.psu_index) return "PSU{} Fan".format(self.psu_index+1)
else: else:
return "Fan{}".format(self.fantray_index+1) return "FanTray{}-Fan{}".format(self.fantray_index+1, self.fan_index+1)
def get_model(self): def get_model(self):
""" """
@ -95,6 +99,8 @@ class Fan(FanBase):
return False return False
if int(presence,0) == 1: if int(presence,0) == 1:
return True return True
else:
return False
def get_status(self): def get_status(self):
""" """
@ -102,7 +108,20 @@ class Fan(FanBase):
Returns: Returns:
bool: True if FAN is operating properly, False if not bool: True if FAN is operating properly, False if not
""" """
return True if not self.is_psu_fan:
status = open(self.status_file, "rb").read()
if int(status, 0) == 1:
return False
else:
return True
else:
status = self._get_cpld_register(self.status_reg)
if status == 'ERR':
return False
if int(status, 0) == 1:
return True
else:
return False
def get_direction(self): def get_direction(self):
""" """
@ -118,7 +137,7 @@ class Fan(FanBase):
""" """
if not self.is_psu_fan: if not self.is_psu_fan:
val = self._get_cpld_register(self.dir_reg) val = self._get_cpld_register(self.dir_reg)
direction = 'Exhaust' if val == 'F2B' else 'Intake' direction = 'exhaust' if val == 'F2B' else 'intake'
if direction == 'ERR': if direction == 'ERR':
return None return None
else: else:
@ -126,7 +145,7 @@ class Fan(FanBase):
val = open(self.eeprom, "rb").read()[0xe1:0xe8] val = open(self.eeprom, "rb").read()[0xe1:0xe8]
except Exception: except Exception:
return None return None
direction = 'Exhaust' if val == 'FORWARD' else 'Intake' direction = 'exhaust' if val == 'FORWARD' else 'intake'
return direction return direction
def get_speed(self): def get_speed(self):
@ -167,3 +186,49 @@ class Fan(FanBase):
except Exception: except Exception:
return None return None
return fan_speed return fan_speed
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.fan_index
def is_replaceable(self):
"""
Indicate whether Fan is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_speed_tolerance(self):
"""
Retrieves the speed tolerance of the fan
Returns:
An integer, the percentage of variance from target speed which is
considered tolerable
"""
if self.get_presence():
# The tolerance value is fixed as 20% for all the DellEMC platforms
tolerance = 20
else:
tolerance = 0
return tolerance
def set_status_led(self, color):
"""
Set led to expected color
Args:
color: A string representing the color with which to set the
fan status LED
Returns:
bool: True if set success, False if fail.
"""
# Fan tray status LED controlled by HW
# Return True to avoid thermalctld alarm
return True

View File

@ -14,20 +14,51 @@ 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")
N3248TE_FANS_PER_FANTRAY = 2 N3248TE_FANS_PER_FANTRAY = 1
class FanDrawer(FanDrawerBase): class FanDrawer(FanDrawerBase):
"""DellEMC Platform-specific Fan class""" """DellEMC Platform-specific Fan class"""
FANTRAY_LED_COLORS = {
"off",
"green",
"yellow"
}
def __init__(self, fantray_index): def __init__(self, fantray_index):
FanDrawerBase.__init__(self) FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms # FanTray is 1-based in DellEMC platforms
self.fantray_led_reg = "fan{}_led".format(fantray_index)
self.fantrayindex = fantray_index + 1 self.fantrayindex = fantray_index + 1
for i in range(N3248TE_FANS_PER_FANTRAY): for i in range(N3248TE_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i)) self._fan_list.append(Fan(fantray_index, i))
def _get_cpld_register(self, reg_name):
# On successful read, returns the value read from given
# reg name and on failure rethrns 'ERR'
cpld_dir = "/sys/devices/platform/dell-n3248te-cpld.0/"
cpld_reg_file = cpld_dir + '/' + reg_name
try:
rv = open(cpld_reg_file, 'r').read()
except IOError : return 'ERR'
return rv.strip('\r\n').lstrip(' ')
def _set_cpld_register(self, reg_name, value):
# On successful write, returns the value will be written on
# reg_name and on failure returns 'ERR'
cpld_dir = "/sys/devices/platform/dell-n3248te-cpld.0/"
cpld_reg_file = cpld_dir + '/' + reg_name
try:
with open(cpld_reg_file, 'w') as fd:
rv = fd.write(str(value))
except Exception:
rv = 'ERR'
return rv
def get_name(self): def get_name(self):
""" """
Retrieves the fan drawer name Retrieves the fan drawer name
@ -35,3 +66,93 @@ class FanDrawer(FanDrawerBase):
string: The name of the device string: The name of the device
""" """
return "FanTray{}".format(self.fantrayindex) return "FanTray{}".format(self.fantrayindex)
def get_status_led(self):
"""
Gets the current system LED color
Returns:
A string that represents the supported color
"""
color = self._get_cpld_register(self.fantray_led_reg)
#if color not in list(self.FANTRAY_LED_COLORS):
# return self.sys_ledcolor
return color
def set_status_led(self,color):
"""
Set system LED status based on the color type passed in the argument.
Argument: Color to be set
Returns:
bool: True is specified color is set, Otherwise return False
"""
if color not in list(self.FANTRAY_LED_COLORS):
return False
if(not self._set_cpld_register(self.fantray_led_reg, color)):
return False
return True
def get_presence(self):
"""
Retrives the presence of the fan drawer
Returns:
bool: True if fan_tray is present, False if not
"""
return self.get_fan(0).get_presence()
def get_model(self):
"""
Retrieves the part number of the fan drawer
Returns:
string: Part number of fan drawer
"""
return "NA"
def get_serial(self):
"""
Retrieves the serial number of the fan drawer
Returns:
string: Serial number of the fan drawer
"""
return "NA"
def get_status(self):
"""
Retrieves the operational status of the fan drawer
Returns:
bool: True if fan drawer is operating properly, False if not
"""
return True
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.fantrayindex
def is_replaceable(self):
"""
Indicate whether this fan drawer is replaceable.
Returns:
bool: True if it is replaceable, False if not
"""
return True
def get_maximum_consumed_power(self):
"""
Retrives the maximum power drawn by Fan Drawer
Returns:
A float, with value of the maximum consumable power of the
component.
"""
return 0.0

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
######################################################################## ########################################################################
# DellEMC Z9332F # DellEMC N3248TE
# #
# Module contains an implementation of SONiC Platform Base API and # Module contains an implementation of SONiC Platform Base API and
# provides the PSUs' information which are available in the platform # provides the PSUs' information which are available in the platform
@ -91,7 +91,9 @@ class Psu(PsuBase):
self.dps_hwmon_exist = os.path.exists(self.dps_hwmon) self.dps_hwmon_exist = os.path.exists(self.dps_hwmon)
if not self.dps_hwmon_exist: if not self.dps_hwmon_exist:
self._reload_dps_module() self._reload_dps_module()
return int(presence, 0) if int(presence, 0) == 1:
return True
return False
def get_model(self): def get_model(self):
""" """
@ -103,7 +105,7 @@ class Psu(PsuBase):
try: val = open(self.eeprom, "rb").read()[0x50:0x62] try: val = open(self.eeprom, "rb").read()[0x50:0x62]
except Exception: except Exception:
val = None val = None
return val.decode() return val.decode('ascii')
def get_serial(self): def get_serial(self):
""" """
@ -115,7 +117,22 @@ class Psu(PsuBase):
try: val = open(self.eeprom, "rb").read()[0xc4:0xd9] try: val = open(self.eeprom, "rb").read()[0xc4:0xd9]
except Exception: except Exception:
val = None val = None
return val.decode() return val.decode('ascii')
def get_revision(self):
"""
Retrieves the serial number of the PSU
Returns:
string: Serial number of PSU
"""
try: val = open(self.eeprom, "rb").read()[0xc4:0xd9]
except Exception:
val = None
if val != "NA" and len(val) == 23:
return val[-3:]
else:
return "NA"
def get_status(self): def get_status(self):
""" """
@ -126,7 +143,9 @@ class Psu(PsuBase):
""" """
status = self._get_cpld_register(self.psu_status).strip() status = self._get_cpld_register(self.psu_status).strip()
if status == 'ERR' : return False if status == 'ERR' : return False
return int(status, 0) if int(status, 0) == 1:
return True
return False
def get_voltage(self): def get_voltage(self):
""" """
@ -141,7 +160,7 @@ class Psu(PsuBase):
voltage = int(volt_reading)/1000 voltage = int(volt_reading)/1000
except Exception: except Exception:
return None return None
return "{:.1f}".format(voltage) return float(voltage)
def get_current(self): def get_current(self):
""" """
@ -156,7 +175,7 @@ class Psu(PsuBase):
current = int(curr_reading)/1000 current = int(curr_reading)/1000
except Exception: except Exception:
return None return None
return "{:.1f}".format(current) return float(current)
def get_power(self): def get_power(self):
""" """
@ -168,10 +187,10 @@ class Psu(PsuBase):
""" """
power_reading = self._get_dps_register(self.psu_power_reg) power_reading = self._get_dps_register(self.psu_power_reg)
try: try:
power = int(power_reading)/1000 power = int(power_reading)/(1000*1000)
except Exception: except Exception:
return None return None
return "{:.1f}".format(power) return float(power)
def get_powergood_status(self): def get_powergood_status(self):
""" """
@ -204,4 +223,21 @@ class Psu(PsuBase):
try: val = open(self.eeprom, "rb").read()[0xe8:0xea] try: val = open(self.eeprom, "rb").read()[0xe8:0xea]
except Exception: except Exception:
return None return None
return val return val.decode()
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.index
def is_replaceable(self):
"""
Indicate whether this PSU is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
############################################################################# #############################################################################
# DELLEMC S5248F # DELLEMC N3248TE
# #
# Module contains an implementation of SONiC Platform Base API and # Module contains an implementation of SONiC Platform Base API and
# provides the platform information # provides the platform information
@ -17,6 +17,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")
SFP_PORT_START = 49
SFP_PORT_END = 54
class Sfp(SfpOptoeBase): class Sfp(SfpOptoeBase):
""" """
DELLEMC Platform-specific Sfp class DELLEMC Platform-specific Sfp class

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
######################################################################## ########################################################################
# DellEMC Z9332F # DellEMC N3248TE
# #
# Module contains an implementation of SONiC Platform Base API and # Module contains an implementation of SONiC Platform Base API and
# provides the Thermals' information which are available in the platform # provides the Thermals' information which are available in the platform
@ -144,3 +144,20 @@ class Thermal(ThermalBase):
""" """
# Thermal threshold values are pre-defined based on HW. # Thermal threshold values are pre-defined based on HW.
return False return False
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.index
def is_replaceable(self):
"""
Indicate whether this Thermal is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False