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 Danny Allen
parent ad05c98d34
commit 7d98a3fe47
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
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():
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():
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():
return subprocess.check_output(
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
).strip()
).decode('utf-8').strip()
def get_cpld_version(bus, i2caddr):
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
"""
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:
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:
raise ImportError(str(e) + "- required module not found")
@ -33,10 +33,6 @@ class Fan(FanBase):
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x2f, "Speed": 0x33},
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):
FanBase.__init__(self)
self.is_psu_fan = psu_fan
@ -51,7 +47,6 @@ class Fan(FanBase):
self.state_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["State"],
is_discrete=True)
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)
else:
self.dependency = dependency
@ -59,7 +54,6 @@ class Fan(FanBase):
self.state_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["State"],
is_discrete=True)
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.max_speed = 23500
@ -80,10 +74,7 @@ class Fan(FanBase):
Returns:
String: Part number of FAN
"""
if self.is_psu_fan:
return None
else:
return self.fru.get_board_part_number()
return 'NA'
def get_serial(self):
"""
@ -91,10 +82,7 @@ class Fan(FanBase):
Returns:
String: Serial number of FAN
"""
if self.is_psu_fan:
return None
else:
return self.fru.get_board_serial()
return 'NA'
def get_presence(self):
"""
@ -159,7 +147,7 @@ class Fan(FanBase):
if not is_valid or self.max_speed == 0:
return None
else:
speed = (100 * fan_speed)/self.max_speed
speed = (100 * fan_speed)//self.max_speed
return speed
def get_speed_rpm(self):
@ -170,3 +158,48 @@ class Fan(FanBase):
"""
is_valid, fan_speed = self.speed_sensor.get_reading()
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:
from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.fan import Fan
from sonic_platform.ipmihelper import IpmiFru
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
@ -20,6 +21,7 @@ Z9332F_FANS_PER_FANTRAY = 2
class FanDrawer(FanDrawerBase):
"""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):
FanDrawerBase.__init__(self)
@ -27,6 +29,7 @@ class FanDrawer(FanDrawerBase):
self.fantrayindex = fantray_index + 1
for i in range(Z9332F_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i))
self.fru = IpmiFru(self.FAN_FRU_MAPPING[self.fantrayindex])
def get_name(self):
"""
@ -35,3 +38,68 @@ class FanDrawer(FanDrawerBase):
string: The name of the device
"""
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 :
return psu_type[type_res[0]]
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:
import os
import re
import time
import subprocess
import struct
@ -75,7 +76,8 @@ info_dict_keys = ['type', 'hardware_rev', 'serial',
'manufacturer', 'model', 'connector',
'encoding', 'ext_identifier', 'ext_rateselect_compliance',
'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',
'power_lpmode', 'tx_disable', 'tx_disable_channel',
@ -236,6 +238,15 @@ class Sfp(SfpBase):
def get_eeprom_sysfs_path(self):
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):
mm.seek(offset)
read_data_stream = mm.read(4)
@ -356,8 +367,8 @@ class Sfp(SfpBase):
compliance_code_dict = {}
transceiver_info_dict = dict.fromkeys(info_dict_keys, 'N/A')
self.media_type = self.set_media_type()
if self.reinit_sfp_driver() == False:
return transceiver_info_dict
if not self.reinit_sfp_driver():
return transceiver_info_dict
# BaseInformation
try:
@ -663,59 +674,59 @@ class Sfp(SfpBase):
"""
Retrieves the RX LOS (lost-of-signal) status of SFP
"""
rx_los = False
rx_los_list = []
try:
if self.media_type.startswith('QSFP'):
rx_los_data = self._get_eeprom_data('rx_los')
# As the function expects a single boolean, if any one channel experience LOS,
# is considered LOS for QSFP
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:
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)
rx_los = sffbase().test_bit(data, 1) != 0
rx_los_list.append(sffbase().test_bit(data, 1) != 0)
except (TypeError, ValueError):
return 'N/A'
return rx_los
return rx_los_list
def get_tx_fault(self):
"""
Retrieves the TX fault status of SFP
"""
tx_fault = False
tx_fault_list = []
try:
if self.media_type.startswith('QSFP'):
tx_fault_data = self._get_eeprom_data('tx_fault')
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:
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)
tx_fault = (sffbase().test_bit(data, 2) != 0)
tx_fault_list.append(sffbase().test_bit(data, 2) != 0)
except (TypeError, ValueError):
return 'N/A'
return tx_fault
return tx_fault_list
def get_tx_disable(self):
"""
Retrieves the tx_disable status of this SFP
"""
tx_disable = False
tx_disable_list = []
try:
if self.media_type.startswith('QSFP'):
tx_disable_data = self._get_eeprom_data('tx_disable')
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:
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)
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 = tx_disable_hard | tx_disable_soft
tx_disable_list.append(tx_disable_hard | tx_disable_soft)
except (TypeError, ValueError):
return 'N/A'
return tx_disable
return tx_disable_list
def get_tx_disable_channel(self):
"""
@ -727,7 +738,7 @@ class Sfp(SfpBase):
tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
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):
return 'N/A'
return tx_disable_channel
@ -762,7 +773,7 @@ class Sfp(SfpBase):
if self.media_type.startswith('QSFP'):
power_override_data = self._get_eeprom_data('power_override')
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
return power_override_state
@ -773,7 +784,7 @@ class Sfp(SfpBase):
temperature = None
try :
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):
return None
return temperature
@ -785,7 +796,7 @@ class Sfp(SfpBase):
voltage = None
try:
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):
return None
return voltage
@ -799,11 +810,11 @@ class Sfp(SfpBase):
tx_bias_data = self._get_eeprom_data('ChannelMonitor')
if self.media_type.startswith('QSFP'):
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)
else:
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):
return None
return tx_bias_list
@ -817,11 +828,11 @@ class Sfp(SfpBase):
rx_power_data = self._get_eeprom_data('ChannelMonitor')
if self.media_type.startswith('QSFP'):
for rx_power_id in ('RX1Power', 'RX2Power', 'RX3Power', 'RX4Power'):
rx_power = rx_power_data['data'][rx_power_id]['value']
rx_power_list.append(rx_power)
rx_power = self._strip_unit_from_str(rx_power_data['data'][rx_power_id]['value'])
rx_power_list.append(self._strip_unit_from_str(rx_power))
else:
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):
return None
return rx_power_list
@ -849,11 +860,11 @@ class Sfp(SfpBase):
channel_monitor_data = self._get_eeprom_data('ChannelMonitor_TxPower')
for tx_power_id in ('TX1Power', 'TX2Power', 'TX3Power', 'TX4Power'):
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:
channel_monitor_data = self._get_eeprom_data('ChannelMonitor')
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):
return None
return tx_power_list
@ -1012,7 +1023,7 @@ class Sfp(SfpBase):
if not os.path.isfile(driver_path):
print(driver_path, "does not exist")
return False
try:
with os.fdopen(os.open(driver_path, os.O_RDONLY)) as fd:
driver_name = fd.read()
@ -1038,3 +1049,23 @@ class Sfp(SfpBase):
except IOError as 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):
"""DellEMC Platform-specific Thermal class"""
# [ Sensor-Name, Sensor-ID ]
# [ Sensor-Name, Sensor-ID, high threshold, high critical_threshold ]
SENSOR_MAPPING = [
['CPU On-board', 0x5],
['Baseboard U3', 0x4],
['SW Internal', 0x61],
['Fan U52', 0x0],
['Fan U17', 0x1],
['SW U52', 0x2],
['SW U16', 0x3],
['PSU1 Inlet', 0x34],
['PSU1 Hotspot', 0x35],
['PSU2 Inlet', 0x3E],
['PSU2 Hotspot', 0x3F],
['SW U04', 0x4F],
['SW U14', 0x56],
['SW U4403', 0x5D]
['CPU On-board', 0x5, False, True],
['Baseboard U3', 0x4, False, False],
['SW Internal', 0x61, True, True],
['Fan U52', 0x0, True, True],
['Fan U17', 0x1, False, False],
['SW U52', 0x2, False, False],
['SW U16', 0x3, True, True],
['PSU1 Inlet', 0x34, False, False],
['PSU1 Hotspot', 0x35, False, False],
['PSU2 Inlet', 0x3E, False, False],
['PSU2 Hotspot', 0x3F, False, False],
['SW U04', 0x4F, False, False],
['SW U14', 0x56, False, False],
['SW U4403', 0x5D, False, False]
]
def __init__(self, thermal_index=0):
ThermalBase.__init__(self)
self.index = thermal_index + 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):
"""
@ -111,11 +113,12 @@ class Thermal(ThermalBase):
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
is_valid, high_threshold = self.sensor.get_threshold("UpperNonCritical")
if not is_valid:
return super(Thermal, self).get_high_threshold()
if self.has_high_threshold:
is_valid, high_threshold = self.sensor.get_threshold("UpperNonCritical")
if is_valid:
return float(high_threshold)
return float(high_threshold)
return super(Thermal, self).get_high_threshold()
def get_low_threshold(self):
"""
@ -126,11 +129,7 @@ class Thermal(ThermalBase):
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
is_valid, low_threshold = self.sensor.get_threshold("LowerNonRecoverable")
if not is_valid:
low_threshold = 0
return float(low_threshold)
return 0.0
def get_high_critical_threshold(self):
"""
@ -140,12 +139,12 @@ class Thermal(ThermalBase):
thermal in Celsius up to nearest thousandth of one degree
Celsius, e.g. 30.125
"""
is_valid, high_crit_threshold = self.sensor.get_threshold("UpperCritical")
if not is_valid:
return super(Thermal, self).get_high_critical_threshold()
if self.has_high_crit_threshold:
is_valid, high_crit_threshold = self.sensor.get_threshold("UpperCritical")
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):
"""
@ -174,3 +173,20 @@ class Thermal(ThermalBase):
"""
# Thermal threshold values are pre-defined based on HW.
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:
import ctypes
import subprocess
from sonic_platform_base.watchdog_base import WatchdogBase
from sonic_platform.hwaccess import io_reg_read, io_reg_write
except ImportError as e:
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
"""
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
timeout = 0
@ -41,34 +48,6 @@ class Watchdog(WatchdogBase):
self._clock_gettime = self._librt.clock_gettime
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):
"""
To get clock monotonic time
@ -94,7 +73,7 @@ class Watchdog(WatchdogBase):
"""
timer_offset = -1
for key,timer_seconds in enumerate(self.TIMERS):
if seconds <= timer_seconds:
if seconds > 0 and seconds <= timer_seconds:
timer_offset = key
seconds = timer_seconds
break
@ -102,43 +81,25 @@ class Watchdog(WatchdogBase):
if timer_offset == -1:
return -1
# Extracting 5th to 7th bits for WD timer values
# 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
wd_timer_val = io_reg_read(self.io_resource, self.wd_timer_offset)
if wd_timer_offset != timer_offset:
# Setting 5th to 7th bits
# value from timer_offset
if wd_timer_val != timer_offset:
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():
# Setting last bit to WD Timer punch
# Last bit = WD Timer punch
self._set_reg_val(reg_val & 0xFE)
# Setting the WD timer punch
io_reg_write(self.io_resource, self.wd_timer_punch_offset, self.wd_punch_enable)
self.armed_time = self._get_time()
self.timeout = seconds
return seconds
else:
# Setting 4th bit to enable WD
# 4th bit = Enable WD
reg_val = self._get_reg_val()
self._set_reg_val(reg_val | 0x8)
# Enable WD
io_reg_write(self.io_resource, self.wd_status_offset, self.wd_enable)
self.armed_time = self._get_time()
self.timeout = seconds
return seconds
def disarm(self):
"""
Disarm the hardware watchdog
@ -148,11 +109,8 @@ class Watchdog(WatchdogBase):
if not
"""
if self.is_armed():
# Setting 4th bit to disable WD
# 4th bit = Disable WD
reg_val = self._get_reg_val()
self._set_reg_val(reg_val & 0xF7)
# Disable WD
io_reg_write(self.io_resource, self.wd_status_offset, self.wd_disable)
self.armed_time = 0
self.timeout = 0
return True
@ -166,14 +124,11 @@ class Watchdog(WatchdogBase):
Returns:
A boolean, True if watchdog is armed, False if not
"""
# Extracting 4th bit to get WD Enable/Disable status
# Getting the WD Enable/Disable status
# 0 - Disabled WD
# 1 - Enabled WD
reg_val = self._get_reg_val()
wd_offset = (reg_val >> 3) & 1
return bool(wd_offset)
wd_status = io_reg_read(self.io_resource, self.wd_status_offset)
return bool(wd_status)
def get_remaining_time(self):
"""
@ -185,7 +140,7 @@ class Watchdog(WatchdogBase):
their watchdog timer. If the watchdog is not armed, returns
-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.
This API would return correct software time difference if it
is called from the process which armed the watchdog timer.