DellEMC: Z9332F - Watchdog support, add platform.json, new platform API implementation and fixes (#6988)

Incorporate the below changes in DellEMC Z9332F platform:

- Implemented watchdog platform API support
- Implement ‘get_position_in_parent’, ‘is_replaceable’ methods for all device types
- Change return type of SFP methods to match specification in sonic_platform_common/sfp_base.py
- Added platform.json file in device directory.

Co-authored-by: V P Subramaniam <Subramaniam_Vellalap@dell.com>
This commit is contained in:
vpsubramaniam 2021-03-11 00:19:33 +05:30 committed by GitHub
parent 5221e68b99
commit 377ea1a229
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 645 additions and 142 deletions

View File

@ -0,0 +1,317 @@
{
"chassis": {
"name": "Z9332F-ON",
"components": [
{
"name": "BIOS"
},
{
"name": "FPGA"
},
{
"name": "BMC"
},
{
"name": "Baseboard CPLD"
},
{
"name": "Switch CPLD 1"
},
{
"name": "Switch CPLD 2"
}
],
"fans": [
{
"name": "FanTray1-Fan1"
},
{
"name": "FanTray1-Fan2"
},
{
"name": "FanTray2-Fan1"
},
{
"name": "FanTray2-Fan2"
},
{
"name": "FanTray3-Fan1"
},
{
"name": "FanTray3-Fan2"
},
{
"name": "FanTray4-Fan1"
},
{
"name": "FanTray4-Fan2"
},
{
"name": "FanTray5-Fan1"
},
{
"name": "FanTray5-Fan2"
},
{
"name": "FanTray6-Fan1"
},
{
"name": "FanTray6-Fan2"
},
{
"name": "FanTray7-Fan1"
},
{
"name": "FanTray7-Fan2"
}
],
"fan_drawers":[
{
"name": "FanTray1",
"fans": [
{
"name": "FanTray1-Fan1"
},
{
"name": "FanTray1-Fan2"
}
]
},
{
"name": "FanTray2",
"fans": [
{
"name": "FanTray2-Fan1"
},
{
"name": "FanTray2-Fan2"
}
]
},
{
"name": "FanTray3",
"fans": [
{
"name": "FanTray3-Fan1"
},
{
"name": "FanTray3-Fan2"
}
]
},
{
"name": "FanTray4",
"fans": [
{
"name": "FanTray4-Fan1"
},
{
"name": "FanTray4-Fan2"
}
]
},
{
"name": "FanTray5",
"fans": [
{
"name": "FanTray5-Fan1"
},
{
"name": "FanTray5-Fan2"
}
]
},
{
"name": "FanTray6",
"fans": [
{
"name": "FanTray6-Fan1"
},
{
"name": "FanTray6-Fan2"
}
]
},
{
"name": "FanTray7",
"fans": [
{
"name": "FanTray7-Fan1"
},
{
"name": "FanTray7-Fan2"
}
]
}
],
"psus": [
{
"name": "PSU1",
"fans": [
{
"name": "PSU1 Fan"
}
]
},
{
"name": "PSU2",
"fans": [
{
"name": "PSU2 Fan"
}
]
}
],
"thermals": [
{
"name": "CPU On-board"
},
{
"name": "Baseboard U3"
},
{
"name": "SW Internal"
},
{
"name": "Fan U52"
},
{
"name": "Fan U17"
},
{
"name": "SW U52"
},
{
"name": "SW U16"
},
{
"name": "PSU1 Inlet"
},
{
"name": "PSU1 Hotspot"
},
{
"name": "PSU2 Inlet"
},
{
"name": "PSU2 Hotspot"
},
{
"name": "SW U04"
},
{
"name": "SW U14"
},
{
"name": "SW U4403"
}
],
"modules": [],
"sfps": [
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "QSFP-DD Double Density 8X Pluggable Transceiver"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
}
]
},
"interfaces": {}
}

View File

@ -366,3 +366,19 @@ class Chassis(ChassisBase):
self.sys_ledcolor = color self.sys_ledcolor = color
return True 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 Chassis is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False

View File

@ -19,7 +19,8 @@ except ImportError as e:
def get_bios_version(): def get_bios_version():
return subprocess.check_output(['dmidecode', '-s', 'bios-version']).strip() return subprocess.check_output(
['dmidecode', '-s', 'bios-version']).decode('utf-8').strip()
def get_fpga_version(): def get_fpga_version():
val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:09:00.0/resource0', 0) val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:09:00.0/resource0', 0)
@ -28,7 +29,7 @@ def get_fpga_version():
def get_bmc_version(): def get_bmc_version():
return subprocess.check_output( return subprocess.check_output(
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision'] ['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
).strip() ).decode('utf-8').strip()
def get_cpld_version(bus, i2caddr): def get_cpld_version(bus, i2caddr):
return '{}'.format(hwaccess.i2c_get(bus, i2caddr, 0)) return '{}'.format(hwaccess.i2c_get(bus, i2caddr, 0))
@ -120,3 +121,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

@ -9,7 +9,7 @@
######################################################################## ########################################################################
try: try:
from sonic_platform_base.fan_base import FanBase from sonic_platform_base.fan_base import FanBase
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru, get_ipmitool_raw_output from sonic_platform.ipmihelper import IpmiSensor, get_ipmitool_raw_output
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -33,10 +33,6 @@ class Fan(FanBase):
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x2f, "Speed": 0x33}, PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x2f, "Speed": 0x33},
2: {"State": 0x39, "Speed": 0x3d} } 2: {"State": 0x39, "Speed": 0x3d} }
# { FANTRAY-ID: FRU-ID }
FAN_FRU_MAPPING = { 1: 6, 2: 7, 3: 8, 4: 9, 5: 10, 6: 11, 7: 12 }
PSU_FRU_MAPPING = { 1: 3, 2: 4 }
def __init__(self, fantray_index=1, fan_index=1, psu_fan=False, dependency=None): def __init__(self, fantray_index=1, fan_index=1, psu_fan=False, dependency=None):
FanBase.__init__(self) FanBase.__init__(self)
self.is_psu_fan = psu_fan self.is_psu_fan = psu_fan
@ -51,7 +47,6 @@ class Fan(FanBase):
self.state_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["State"], self.state_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["State"],
is_discrete=True) is_discrete=True)
self.speed_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Speed"]) self.speed_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Speed"])
self.fru = IpmiFru(self.FAN_FRU_MAPPING[self.fantrayindex])
self.fan_dir_raw_cmd = "0x3a 0x0a {}".format(fantray_index) self.fan_dir_raw_cmd = "0x3a 0x0a {}".format(fantray_index)
else: else:
self.dependency = dependency self.dependency = dependency
@ -59,7 +54,6 @@ class Fan(FanBase):
self.state_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["State"], self.state_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["State"],
is_discrete=True) is_discrete=True)
self.speed_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["Speed"]) self.speed_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["Speed"])
self.fru = IpmiFru(self.PSU_FRU_MAPPING[self.fanindex])
self.fan_dir_raw_cmd = "0x3a 0x0a {}".format(7+(fan_index-1)) self.fan_dir_raw_cmd = "0x3a 0x0a {}".format(7+(fan_index-1))
self.max_speed = 23500 self.max_speed = 23500
@ -80,10 +74,7 @@ class Fan(FanBase):
Returns: Returns:
String: Part number of FAN String: Part number of FAN
""" """
if self.is_psu_fan: return 'NA'
return None
else:
return self.fru.get_board_part_number()
def get_serial(self): def get_serial(self):
""" """
@ -91,10 +82,7 @@ class Fan(FanBase):
Returns: Returns:
String: Serial number of FAN String: Serial number of FAN
""" """
if self.is_psu_fan: return 'NA'
return None
else:
return self.fru.get_board_serial()
def get_presence(self): def get_presence(self):
""" """
@ -159,7 +147,7 @@ class Fan(FanBase):
if not is_valid or self.max_speed == 0: if not is_valid or self.max_speed == 0:
return None return None
else: else:
speed = (100 * fan_speed)/self.max_speed speed = (100 * fan_speed)//self.max_speed
return speed return speed
def get_speed_rpm(self): def get_speed_rpm(self):
@ -170,3 +158,48 @@ class Fan(FanBase):
""" """
is_valid, fan_speed = self.speed_sensor.get_reading() is_valid, fan_speed = self.speed_sensor.get_reading()
return fan_speed if is_valid else None return fan_speed if is_valid else None
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.fanindex
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

@ -11,6 +11,7 @@
try: try:
from sonic_platform_base.fan_drawer_base import FanDrawerBase from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.fan import Fan from sonic_platform.fan import Fan
from sonic_platform.ipmihelper import IpmiFru
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -20,6 +21,7 @@ Z9332F_FANS_PER_FANTRAY = 2
class FanDrawer(FanDrawerBase): class FanDrawer(FanDrawerBase):
"""DellEMC Platform-specific Fan class""" """DellEMC Platform-specific Fan class"""
FAN_FRU_MAPPING = { 1: 6, 2: 7, 3: 8, 4: 9, 5: 10, 6: 11, 7: 12 }
def __init__(self, fantray_index): def __init__(self, fantray_index):
FanDrawerBase.__init__(self) FanDrawerBase.__init__(self)
@ -27,6 +29,7 @@ class FanDrawer(FanDrawerBase):
self.fantrayindex = fantray_index + 1 self.fantrayindex = fantray_index + 1
for i in range(Z9332F_FANS_PER_FANTRAY): for i in range(Z9332F_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i)) self._fan_list.append(Fan(fantray_index, i))
self.fru = IpmiFru(self.FAN_FRU_MAPPING[self.fantrayindex])
def get_name(self): def get_name(self):
""" """
@ -35,3 +38,68 @@ 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_presence(self):
"""
Retrieves 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 self.fru.get_board_part_number()
def get_serial(self):
"""
Retrieves the serial number of the fan drawer
Returns:
string: Serial number of the fan drawer
"""
return self.fru.get_board_serial()
def get_status(self):
"""
Retrieves the operational status of the fan drawer
Returns:
bool: True if fan drawer is operating properly, False if not
"""
status = True
for fan in self.get_all_fans():
status &= fan.get_status()
return status
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 set_status_led(self, color):
"""
Set led to expected color
Args:
color: A string representing the color with which to set the
fan module status LED
Returns:
bool: True if set success, False if fail.
"""
# Fan tray status LED controlled by BMC
# Return True to avoid thermalctld alarm
return True

View File

@ -212,3 +212,20 @@ class Psu(PsuBase):
if type_res is not None and len(type_res) == 1 : if type_res is not None and len(type_res) == 1 :
return psu_type[type_res[0]] return psu_type[type_res[0]]
return None return None
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

@ -10,6 +10,7 @@
try: try:
import os import os
import re
import time import time
import subprocess import subprocess
import struct import struct
@ -75,7 +76,8 @@ info_dict_keys = ['type', 'hardware_rev', 'serial',
'manufacturer', 'model', 'connector', 'manufacturer', 'model', 'connector',
'encoding', 'ext_identifier', 'ext_rateselect_compliance', 'encoding', 'ext_identifier', 'ext_rateselect_compliance',
'cable_type', 'cable_length', 'nominal_bit_rate', 'cable_type', 'cable_length', 'nominal_bit_rate',
'specification_compliance', 'type_abbrv_name','vendor_date', 'vendor_oui'] 'specification_compliance', 'type_abbrv_name', 'vendor_date',
'vendor_oui', 'application_advertisement']
dom_dict_keys = ['rx_los', 'tx_fault', 'reset_status', dom_dict_keys = ['rx_los', 'tx_fault', 'reset_status',
'power_lpmode', 'tx_disable', 'tx_disable_channel', 'power_lpmode', 'tx_disable', 'tx_disable_channel',
@ -236,6 +238,15 @@ class Sfp(SfpBase):
def get_eeprom_sysfs_path(self): def get_eeprom_sysfs_path(self):
return self.eeprom_path return self.eeprom_path
def _strip_unit_from_str(self, value_str):
match = re.match(r'(.*)C$|(.*)Volts$|(.*)mA$|(.*)dBm$', value_str)
if match:
for value in match.groups():
if value is not None:
return float(value)
return None
def pci_mem_read(self, mm, offset): def pci_mem_read(self, mm, offset):
mm.seek(offset) mm.seek(offset)
read_data_stream = mm.read(4) read_data_stream = mm.read(4)
@ -356,8 +367,8 @@ class Sfp(SfpBase):
compliance_code_dict = {} compliance_code_dict = {}
transceiver_info_dict = dict.fromkeys(info_dict_keys, 'N/A') transceiver_info_dict = dict.fromkeys(info_dict_keys, 'N/A')
self.media_type = self.set_media_type() self.media_type = self.set_media_type()
if self.reinit_sfp_driver() == False: if not self.reinit_sfp_driver():
return transceiver_info_dict return transceiver_info_dict
# BaseInformation # BaseInformation
try: try:
@ -663,59 +674,59 @@ class Sfp(SfpBase):
""" """
Retrieves the RX LOS (lost-of-signal) status of SFP Retrieves the RX LOS (lost-of-signal) status of SFP
""" """
rx_los = False rx_los_list = []
try: try:
if self.media_type.startswith('QSFP'): if self.media_type.startswith('QSFP'):
rx_los_data = self._get_eeprom_data('rx_los') rx_los_data = self._get_eeprom_data('rx_los')
# As the function expects a single boolean, if any one channel experience LOS, # As the function expects a single boolean, if any one channel experience LOS,
# is considered LOS for QSFP # is considered LOS for QSFP
for rx_los_id in ('Rx1LOS', 'Rx2LOS', 'Rx3LOS', 'Rx4LOS') : for rx_los_id in ('Rx1LOS', 'Rx2LOS', 'Rx3LOS', 'Rx4LOS') :
rx_los |= (rx_los_data['data'][rx_los_id]['value'] is 'On') rx_los_list.append(rx_los_data['data'][rx_los_id]['value'] == 'On')
else: else:
rx_los_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) rx_los_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
data = int(rx_los_data[0], 16) data = int(rx_los_data[0], 16)
rx_los = sffbase().test_bit(data, 1) != 0 rx_los_list.append(sffbase().test_bit(data, 1) != 0)
except (TypeError, ValueError): except (TypeError, ValueError):
return 'N/A' return 'N/A'
return rx_los return rx_los_list
def get_tx_fault(self): def get_tx_fault(self):
""" """
Retrieves the TX fault status of SFP Retrieves the TX fault status of SFP
""" """
tx_fault = False tx_fault_list = []
try: try:
if self.media_type.startswith('QSFP'): if self.media_type.startswith('QSFP'):
tx_fault_data = self._get_eeprom_data('tx_fault') tx_fault_data = self._get_eeprom_data('tx_fault')
for tx_fault_id in ('Tx1Fault', 'Tx2Fault', 'Tx3Fault', 'Tx4Fault') : for tx_fault_id in ('Tx1Fault', 'Tx2Fault', 'Tx3Fault', 'Tx4Fault') :
tx_fault |= (tx_fault_data['data'][tx_fault_id]['value'] is 'On') tx_fault_list.append(tx_fault_data['data'][tx_fault_id]['value'] == 'On')
else: else:
tx_fault_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) tx_fault_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
data = int(tx_fault_data[0], 16) data = int(tx_fault_data[0], 16)
tx_fault = (sffbase().test_bit(data, 2) != 0) tx_fault_list.append(sffbase().test_bit(data, 2) != 0)
except (TypeError, ValueError): except (TypeError, ValueError):
return 'N/A' return 'N/A'
return tx_fault return tx_fault_list
def get_tx_disable(self): def get_tx_disable(self):
""" """
Retrieves the tx_disable status of this SFP Retrieves the tx_disable status of this SFP
""" """
tx_disable = False tx_disable_list = []
try: try:
if self.media_type.startswith('QSFP'): if self.media_type.startswith('QSFP'):
tx_disable_data = self._get_eeprom_data('tx_disable') tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'): for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
tx_disable |= (tx_disable_data['data'][tx_disable_id]['value'] is 'On') tx_disable_list.append(tx_disable_data['data'][tx_disable_id]['value'] == 'On')
else: else:
tx_disable_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH) tx_disable_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
data = int(tx_disable_data[0], 16) data = int(tx_disable_data[0], 16)
tx_disable_hard = (sffbase().test_bit(data, SFP_TX_DISABLE_HARD_BIT) != 0) tx_disable_hard = (sffbase().test_bit(data, SFP_TX_DISABLE_HARD_BIT) != 0)
tx_disable_soft = (sffbase().test_bit(data, SFP_TX_DISABLE_SOFT_BIT) != 0) tx_disable_soft = (sffbase().test_bit(data, SFP_TX_DISABLE_SOFT_BIT) != 0)
tx_disable = tx_disable_hard | tx_disable_soft tx_disable_list.append(tx_disable_hard | tx_disable_soft)
except (TypeError, ValueError): except (TypeError, ValueError):
return 'N/A' return 'N/A'
return tx_disable return tx_disable_list
def get_tx_disable_channel(self): def get_tx_disable_channel(self):
""" """
@ -727,7 +738,7 @@ class Sfp(SfpBase):
tx_disable_data = self._get_eeprom_data('tx_disable') tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'): for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
tx_disable_channel <<= 1 tx_disable_channel <<= 1
tx_disable_channel |= (tx_disable_data['data']['Tx1Disable']['value'] is 'On') tx_disable_channel |= (tx_disable_data['data']['Tx1Disable']['value'] == 'On')
except (TypeError, ValueError): except (TypeError, ValueError):
return 'N/A' return 'N/A'
return tx_disable_channel return tx_disable_channel
@ -762,7 +773,7 @@ class Sfp(SfpBase):
if self.media_type.startswith('QSFP'): if self.media_type.startswith('QSFP'):
power_override_data = self._get_eeprom_data('power_override') power_override_data = self._get_eeprom_data('power_override')
power_override = power_override_data['data']['PowerOverRide']['value'] power_override = power_override_data['data']['PowerOverRide']['value']
power_override_state = (power_override is 'On') power_override_state = (power_override == 'On')
except (TypeError, ValueError): pass except (TypeError, ValueError): pass
return power_override_state return power_override_state
@ -773,7 +784,7 @@ class Sfp(SfpBase):
temperature = None temperature = None
try : try :
temperature_data = self._get_eeprom_data('Temperature') temperature_data = self._get_eeprom_data('Temperature')
temperature = temperature_data['data']['Temperature']['value'] temperature = self._strip_unit_from_str(temperature_data['data']['Temperature']['value'])
except (TypeError, ValueError): except (TypeError, ValueError):
return None return None
return temperature return temperature
@ -785,7 +796,7 @@ class Sfp(SfpBase):
voltage = None voltage = None
try: try:
voltage_data = self._get_eeprom_data('Voltage') voltage_data = self._get_eeprom_data('Voltage')
voltage = voltage_data['data']['Vcc']['value'] voltage = self._strip_unit_from_str(voltage_data['data']['Vcc']['value'])
except (TypeError, ValueError): except (TypeError, ValueError):
return None return None
return voltage return voltage
@ -799,11 +810,11 @@ class Sfp(SfpBase):
tx_bias_data = self._get_eeprom_data('ChannelMonitor') tx_bias_data = self._get_eeprom_data('ChannelMonitor')
if self.media_type.startswith('QSFP'): if self.media_type.startswith('QSFP'):
for tx_bias_id in ('TX1Bias', 'TX2Bias', 'TX3Bias', 'TX4Bias') : for tx_bias_id in ('TX1Bias', 'TX2Bias', 'TX3Bias', 'TX4Bias') :
tx_bias = tx_bias_data['data'][tx_bias_id]['value'] tx_bias = self._strip_unit_from_str(tx_bias_data['data'][tx_bias_id]['value'])
tx_bias_list.append(tx_bias) tx_bias_list.append(tx_bias)
else: else:
tx1_bias = tx_bias_data['data']['TXBias']['value'] tx1_bias = tx_bias_data['data']['TXBias']['value']
tx_bias_list = [tx1_bias, "N/A", "N/A", "N/A"] tx_bias_list.append(self._strip_unit_from_str(tx1_bias))
except (TypeError, ValueError): except (TypeError, ValueError):
return None return None
return tx_bias_list return tx_bias_list
@ -817,11 +828,11 @@ class Sfp(SfpBase):
rx_power_data = self._get_eeprom_data('ChannelMonitor') rx_power_data = self._get_eeprom_data('ChannelMonitor')
if self.media_type.startswith('QSFP'): if self.media_type.startswith('QSFP'):
for rx_power_id in ('RX1Power', 'RX2Power', 'RX3Power', 'RX4Power'): for rx_power_id in ('RX1Power', 'RX2Power', 'RX3Power', 'RX4Power'):
rx_power = rx_power_data['data'][rx_power_id]['value'] rx_power = self._strip_unit_from_str(rx_power_data['data'][rx_power_id]['value'])
rx_power_list.append(rx_power) rx_power_list.append(self._strip_unit_from_str(rx_power))
else: else:
rx1_pw = rx_power_data['data']['RXPower']['value'] rx1_pw = rx_power_data['data']['RXPower']['value']
rx_power_list = [rx1_pw, "N/A", "N/A", "N/A"] rx_power_list.append(self._strip_unit_from_str(rx1_pw))
except (TypeError, ValueError): except (TypeError, ValueError):
return None return None
return rx_power_list return rx_power_list
@ -849,11 +860,11 @@ class Sfp(SfpBase):
channel_monitor_data = self._get_eeprom_data('ChannelMonitor_TxPower') channel_monitor_data = self._get_eeprom_data('ChannelMonitor_TxPower')
for tx_power_id in ('TX1Power', 'TX2Power', 'TX3Power', 'TX4Power'): for tx_power_id in ('TX1Power', 'TX2Power', 'TX3Power', 'TX4Power'):
tx_pw = channel_monitor_data['data'][tx_power_id]['value'] tx_pw = channel_monitor_data['data'][tx_power_id]['value']
tx_power_list.append(tx_pw) tx_power_list.append(self._strip_unit_from_str(tx_pw))
else: else:
channel_monitor_data = self._get_eeprom_data('ChannelMonitor') channel_monitor_data = self._get_eeprom_data('ChannelMonitor')
tx1_pw = channel_monitor_data['data']['TXPower']['value'] tx1_pw = channel_monitor_data['data']['TXPower']['value']
tx_power_list = [tx1_pw, 'N/A', 'N/A', 'N/A'] tx_power_list.append(self._strip_unit_from_str(tx1_pw))
except (TypeError, ValueError): except (TypeError, ValueError):
return None return None
return tx_power_list return tx_power_list
@ -1038,3 +1049,23 @@ class Sfp(SfpBase):
except IOError as e: except IOError as e:
print("Error: Unable to open file: %s" % str(e)) print("Error: Unable to open file: %s" % str(e))
return False
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.index
def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True

View File

@ -19,28 +19,30 @@ except ImportError as e:
class Thermal(ThermalBase): class Thermal(ThermalBase):
"""DellEMC Platform-specific Thermal class""" """DellEMC Platform-specific Thermal class"""
# [ Sensor-Name, Sensor-ID ] # [ Sensor-Name, Sensor-ID, high threshold, high critical_threshold ]
SENSOR_MAPPING = [ SENSOR_MAPPING = [
['CPU On-board', 0x5], ['CPU On-board', 0x5, False, True],
['Baseboard U3', 0x4], ['Baseboard U3', 0x4, False, False],
['SW Internal', 0x61], ['SW Internal', 0x61, True, True],
['Fan U52', 0x0], ['Fan U52', 0x0, True, True],
['Fan U17', 0x1], ['Fan U17', 0x1, False, False],
['SW U52', 0x2], ['SW U52', 0x2, False, False],
['SW U16', 0x3], ['SW U16', 0x3, True, True],
['PSU1 Inlet', 0x34], ['PSU1 Inlet', 0x34, False, False],
['PSU1 Hotspot', 0x35], ['PSU1 Hotspot', 0x35, False, False],
['PSU2 Inlet', 0x3E], ['PSU2 Inlet', 0x3E, False, False],
['PSU2 Hotspot', 0x3F], ['PSU2 Hotspot', 0x3F, False, False],
['SW U04', 0x4F], ['SW U04', 0x4F, False, False],
['SW U14', 0x56], ['SW U14', 0x56, False, False],
['SW U4403', 0x5D] ['SW U4403', 0x5D, False, False]
] ]
def __init__(self, thermal_index=0): def __init__(self, thermal_index=0):
ThermalBase.__init__(self) ThermalBase.__init__(self)
self.index = thermal_index + 1 self.index = thermal_index + 1
self.sensor = IpmiSensor(self.SENSOR_MAPPING[self.index - 1][1]) self.sensor = IpmiSensor(self.SENSOR_MAPPING[self.index - 1][1])
self.has_high_threshold = self.SENSOR_MAPPING[self.index - 1][2]
self.has_high_crit_threshold = self.SENSOR_MAPPING[self.index - 1][3]
def get_name(self): def get_name(self):
""" """
@ -111,11 +113,12 @@ class Thermal(ThermalBase):
Celsius up to nearest thousandth of one degree Celsius, Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125 e.g. 30.125
""" """
is_valid, high_threshold = self.sensor.get_threshold("UpperNonCritical") if self.has_high_threshold:
if not is_valid: is_valid, high_threshold = self.sensor.get_threshold("UpperNonCritical")
return super(Thermal, self).get_high_threshold() if is_valid:
return float(high_threshold)
return float(high_threshold) return super(Thermal, self).get_high_threshold()
def get_low_threshold(self): def get_low_threshold(self):
""" """
@ -126,11 +129,7 @@ class Thermal(ThermalBase):
Celsius up to nearest thousandth of one degree Celsius, Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125 e.g. 30.125
""" """
is_valid, low_threshold = self.sensor.get_threshold("LowerNonRecoverable") return 0.0
if not is_valid:
low_threshold = 0
return float(low_threshold)
def get_high_critical_threshold(self): def get_high_critical_threshold(self):
""" """
@ -140,12 +139,12 @@ class Thermal(ThermalBase):
thermal in Celsius up to nearest thousandth of one degree thermal in Celsius up to nearest thousandth of one degree
Celsius, e.g. 30.125 Celsius, e.g. 30.125
""" """
is_valid, high_crit_threshold = self.sensor.get_threshold("UpperCritical") if self.has_high_crit_threshold:
if not is_valid: is_valid, high_crit_threshold = self.sensor.get_threshold("UpperCritical")
return super(Thermal, self).get_high_critical_threshold() if is_valid:
return float(high_crit_threshold)
return float(high_crit_threshold)
return super(Thermal, self).get_high_critical_threshold()
def set_high_threshold(self, temperature): def set_high_threshold(self, temperature):
""" """
@ -174,3 +173,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

View File

@ -11,8 +11,8 @@
try: try:
import ctypes import ctypes
import subprocess
from sonic_platform_base.watchdog_base import WatchdogBase from sonic_platform_base.watchdog_base import WatchdogBase
from sonic_platform.hwaccess import io_reg_read, io_reg_write
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -29,7 +29,14 @@ class Watchdog(WatchdogBase):
Abstract base class for interfacing with a hardware watchdog module Abstract base class for interfacing with a hardware watchdog module
""" """
TIMERS = [15,20,30,40,50,60,65,70] TIMERS = [0.2, 30, 60, 180, 240, 300, 420, 600]
io_resource = "/dev/port"
wd_timer_offset = 0xA181
wd_status_offset = 0xA182
wd_timer_punch_offset = 0xA184
wd_enable = 1
wd_disable = 0
wd_punch_enable = 0
armed_time = 0 armed_time = 0
timeout = 0 timeout = 0
@ -41,34 +48,6 @@ class Watchdog(WatchdogBase):
self._clock_gettime = self._librt.clock_gettime self._clock_gettime = self._librt.clock_gettime
self._clock_gettime.argtypes=[ctypes.c_int, ctypes.POINTER(_timespec)] self._clock_gettime.argtypes=[ctypes.c_int, ctypes.POINTER(_timespec)]
def _get_command_result(self, cmdline):
try:
proc = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
result = stdout.rstrip('\n')
except OSError:
result = None
return result
def _get_reg_val(self):
# 0x31 = CPLD I2C Base Address
# 0x07 = Watchdog Function Register
value = self._get_command_result("/usr/sbin/i2cget -y 601 0x31 0x07")
if not value:
return None
else:
return int(value, 16)
def _set_reg_val(self,val):
# 0x31 = CPLD I2C Base Address
# 0x07 = Watchdog Function Register
value = self._get_command_result("/usr/sbin/i2cset -y 601 0x31 0x07 %s"
% (val))
return value
def _get_time(self): def _get_time(self):
""" """
To get clock monotonic time To get clock monotonic time
@ -94,7 +73,7 @@ class Watchdog(WatchdogBase):
""" """
timer_offset = -1 timer_offset = -1
for key,timer_seconds in enumerate(self.TIMERS): for key,timer_seconds in enumerate(self.TIMERS):
if seconds <= timer_seconds: if seconds > 0 and seconds <= timer_seconds:
timer_offset = key timer_offset = key
seconds = timer_seconds seconds = timer_seconds
break break
@ -102,43 +81,25 @@ class Watchdog(WatchdogBase):
if timer_offset == -1: if timer_offset == -1:
return -1 return -1
# Extracting 5th to 7th bits for WD timer values wd_timer_val = io_reg_read(self.io_resource, self.wd_timer_offset)
# 000 - 15 sec
# 001 - 20 sec
# 010 - 30 sec
# 011 - 40 sec
# 100 - 50 sec
# 101 - 60 sec
# 110 - 65 sec
# 111 - 70 sec
reg_val = self._get_reg_val()
wd_timer_offset = (reg_val >> 4) & 0x7
if wd_timer_offset != timer_offset: if wd_timer_val != timer_offset:
# Setting 5th to 7th bits
# value from timer_offset
self.disarm() self.disarm()
self._set_reg_val(reg_val | (timer_offset << 4)) io_reg_write(self.io_resource, self.wd_timer_offset, timer_offset)
if self.is_armed(): if self.is_armed():
# Setting last bit to WD Timer punch # Setting the WD timer punch
# Last bit = WD Timer punch io_reg_write(self.io_resource, self.wd_timer_punch_offset, self.wd_punch_enable)
self._set_reg_val(reg_val & 0xFE)
self.armed_time = self._get_time() self.armed_time = self._get_time()
self.timeout = seconds self.timeout = seconds
return seconds return seconds
else: else:
# Setting 4th bit to enable WD # Enable WD
# 4th bit = Enable WD io_reg_write(self.io_resource, self.wd_status_offset, self.wd_enable)
reg_val = self._get_reg_val()
self._set_reg_val(reg_val | 0x8)
self.armed_time = self._get_time() self.armed_time = self._get_time()
self.timeout = seconds self.timeout = seconds
return seconds return seconds
def disarm(self): def disarm(self):
""" """
Disarm the hardware watchdog Disarm the hardware watchdog
@ -148,11 +109,8 @@ class Watchdog(WatchdogBase):
if not if not
""" """
if self.is_armed(): if self.is_armed():
# Setting 4th bit to disable WD # Disable WD
# 4th bit = Disable WD io_reg_write(self.io_resource, self.wd_status_offset, self.wd_disable)
reg_val = self._get_reg_val()
self._set_reg_val(reg_val & 0xF7)
self.armed_time = 0 self.armed_time = 0
self.timeout = 0 self.timeout = 0
return True return True
@ -166,14 +124,11 @@ class Watchdog(WatchdogBase):
Returns: Returns:
A boolean, True if watchdog is armed, False if not A boolean, True if watchdog is armed, False if not
""" """
# Getting the WD Enable/Disable status
# Extracting 4th bit to get WD Enable/Disable status
# 0 - Disabled WD # 0 - Disabled WD
# 1 - Enabled WD # 1 - Enabled WD
reg_val = self._get_reg_val() wd_status = io_reg_read(self.io_resource, self.wd_status_offset)
wd_offset = (reg_val >> 3) & 1 return bool(wd_status)
return bool(wd_offset)
def get_remaining_time(self): def get_remaining_time(self):
""" """
@ -185,7 +140,7 @@ class Watchdog(WatchdogBase):
their watchdog timer. If the watchdog is not armed, returns their watchdog timer. If the watchdog is not armed, returns
-1. -1.
S5232 doesnot have hardware support to show remaining time. Z9332 does not have hardware support to show remaining time.
Due to this limitation, this API is implemented in software. Due to this limitation, this API is implemented in software.
This API would return correct software time difference if it This API would return correct software time difference if it
is called from the process which armed the watchdog timer. is called from the process which armed the watchdog timer.