[201911] DellEMC platform API 2.0 for Z9264f, S5232f (#5637)
Add platform API 2.0 support for Z9264f, S5232f in 201911 branch
This commit is contained in:
parent
5a802533b5
commit
3734bf326b
@ -18,5 +18,13 @@ except ImportError, e:
|
|||||||
class board(eeprom_tlvinfo.TlvInfoDecoder):
|
class board(eeprom_tlvinfo.TlvInfoDecoder):
|
||||||
|
|
||||||
def __init__(self, name, path, cpld_root, ro):
|
def __init__(self, name, path, cpld_root, ro):
|
||||||
self.eeprom_path = "/sys/class/i2c-adapter/i2c-0/0-0050/eeprom"
|
self.eeprom_path = None
|
||||||
|
for b in (0,1):
|
||||||
|
f = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'.format(b)
|
||||||
|
if os.path.exists(f):
|
||||||
|
self.eeprom_path = f
|
||||||
|
break
|
||||||
|
if self.eeprom_path is None:
|
||||||
|
return
|
||||||
|
|
||||||
super(board, self).__init__(self.eeprom_path, 0, '', True)
|
super(board, self).__init__(self.eeprom_path, 0, '', True)
|
||||||
|
@ -44,6 +44,8 @@ SFP_VOLT_OFFSET = 98
|
|||||||
SFP_VOLT_WIDTH = 2
|
SFP_VOLT_WIDTH = 2
|
||||||
SFP_MODULE_THRESHOLD_OFFSET = 0
|
SFP_MODULE_THRESHOLD_OFFSET = 0
|
||||||
SFP_MODULE_THRESHOLD_WIDTH = 56
|
SFP_MODULE_THRESHOLD_WIDTH = 56
|
||||||
|
SFP_CHANNL_MON_OFFSET = 100
|
||||||
|
SFP_CHANNL_MON_WIDTH = 6
|
||||||
|
|
||||||
XCVR_DOM_CAPABILITY_OFFSET = 92
|
XCVR_DOM_CAPABILITY_OFFSET = 92
|
||||||
XCVR_DOM_CAPABILITY_WIDTH = 1
|
XCVR_DOM_CAPABILITY_WIDTH = 1
|
||||||
@ -226,8 +228,8 @@ class SfpUtil(SfpUtilBase):
|
|||||||
if (reg_value == ""):
|
if (reg_value == ""):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Mask off 4th bit for presence
|
# Mask off 4th bit for reset
|
||||||
mask = (1 << 6)
|
mask = (1 << 4)
|
||||||
|
|
||||||
# ResetL is active low
|
# ResetL is active low
|
||||||
reg_value = reg_value & ~mask
|
reg_value = reg_value & ~mask
|
||||||
@ -347,7 +349,7 @@ class SfpUtil(SfpUtilBase):
|
|||||||
]
|
]
|
||||||
transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A')
|
transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A')
|
||||||
|
|
||||||
if port_num in self.qsfp_ports:
|
if port_num in self.qsfp_ports:
|
||||||
offset = 0
|
offset = 0
|
||||||
offset_xcvr = 128
|
offset_xcvr = 128
|
||||||
file_path = self._get_port_eeprom_path(port_num, self.IDENTITY_EEPROM_ADDR)
|
file_path = self._get_port_eeprom_path(port_num, self.IDENTITY_EEPROM_ADDR)
|
||||||
@ -379,7 +381,7 @@ class SfpUtil(SfpUtilBase):
|
|||||||
return transceiver_dom_info_dict
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH)
|
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH)
|
||||||
if dom_temperature_raw is not None:
|
if dom_temperature_raw is not None:
|
||||||
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
|
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
|
||||||
else:
|
else:
|
||||||
return transceiver_dom_info_dict
|
return transceiver_dom_info_dict
|
||||||
@ -411,10 +413,22 @@ class SfpUtil(SfpUtilBase):
|
|||||||
else:
|
else:
|
||||||
return transceiver_dom_info_dict
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
transceiver_dom_info_dict['tx1power'] = 'N/A'
|
transceiver_dom_info_dict['tx1power'] = 'N/A'
|
||||||
transceiver_dom_info_dict['tx2power'] = 'N/A'
|
transceiver_dom_info_dict['tx2power'] = 'N/A'
|
||||||
transceiver_dom_info_dict['tx3power'] = 'N/A'
|
transceiver_dom_info_dict['tx3power'] = 'N/A'
|
||||||
transceiver_dom_info_dict['tx4power'] = 'N/A'
|
transceiver_dom_info_dict['tx4power'] = 'N/A'
|
||||||
|
else:
|
||||||
|
dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH)
|
||||||
|
if dom_channel_monitor_raw is not None:
|
||||||
|
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value']
|
||||||
|
transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value']
|
||||||
|
transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value']
|
||||||
|
transceiver_dom_info_dict['tx4power'] = dom_channel_monitor_data['data']['TX4Power']['value']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sysfsfile_eeprom.close()
|
sysfsfile_eeprom.close()
|
||||||
except IOError:
|
except IOError:
|
||||||
@ -433,64 +447,66 @@ class SfpUtil(SfpUtilBase):
|
|||||||
transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value']
|
transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value']
|
||||||
|
|
||||||
else:
|
else:
|
||||||
offset = 256
|
offset = 256
|
||||||
file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR)
|
file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR)
|
||||||
if not self._sfp_eeprom_present(file_path, 0):
|
if not self._sfp_eeprom_present(file_path, 0):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sysfsfile_eeprom = io.open(file_path,"rb",0)
|
sysfsfile_eeprom = io.open(file_path,"rb",0)
|
||||||
except IOError:
|
except IOError:
|
||||||
print("Error: reading sysfs file %s" % file_path)
|
print("Error: reading sysfs file %s" % file_path)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sfpd_obj = sff8472Dom(None,1)
|
|
||||||
if sfpd_obj is None:
|
|
||||||
return None
|
|
||||||
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_TEMPE_OFFSET),
|
|
||||||
SFP_TEMPE_WIDTH)
|
|
||||||
if dom_temperature_raw is not None:
|
|
||||||
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
|
|
||||||
else:
|
|
||||||
return transceiver_dom_info_dict
|
|
||||||
|
|
||||||
dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_VOLT_OFFSET),
|
sfpd_obj = sff8472Dom(None,1)
|
||||||
SFP_VOLT_WIDTH)
|
if sfpd_obj is None:
|
||||||
if dom_voltage_raw is not None:
|
return None
|
||||||
dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0)
|
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_TEMPE_OFFSET),
|
||||||
else:
|
SFP_TEMPE_WIDTH)
|
||||||
return transceiver_dom_info_dict
|
|
||||||
|
|
||||||
dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_MODULE_THRESHOLD_OFFSET),
|
if dom_temperature_raw is not None:
|
||||||
SFP_MODULE_THRESHOLD_WIDTH)
|
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
|
||||||
if dom_channel_monitor_raw is not None:
|
else:
|
||||||
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0)
|
return transceiver_dom_info_dict
|
||||||
else:
|
|
||||||
return transceiver_dom_info_dict
|
|
||||||
|
|
||||||
try:
|
dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_VOLT_OFFSET),
|
||||||
sysfsfile_eeprom.close()
|
SFP_VOLT_WIDTH)
|
||||||
except IOError:
|
|
||||||
print("Error: closing sysfs file %s" % file_path)
|
|
||||||
return None
|
|
||||||
|
|
||||||
transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value']
|
if dom_voltage_raw is not None:
|
||||||
transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value']
|
dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0)
|
||||||
transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value']
|
else:
|
||||||
transceiver_dom_info_dict['rx2power'] = 'N/A'
|
return transceiver_dom_info_dict
|
||||||
transceiver_dom_info_dict['rx3power'] = 'N/A'
|
|
||||||
transceiver_dom_info_dict['rx4power'] = 'N/A'
|
dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_CHANNL_MON_OFFSET),
|
||||||
transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value']
|
SFP_CHANNL_MON_WIDTH)
|
||||||
transceiver_dom_info_dict['tx2bias'] = 'N/A'
|
if dom_channel_monitor_raw is not None:
|
||||||
transceiver_dom_info_dict['tx3bias'] = 'N/A'
|
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0)
|
||||||
transceiver_dom_info_dict['tx4bias'] = 'N/A'
|
else:
|
||||||
transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value']
|
return transceiver_dom_info_dict
|
||||||
transceiver_dom_info_dict['tx2power'] = 'N/A'
|
|
||||||
transceiver_dom_info_dict['tx3power'] = 'N/A'
|
try:
|
||||||
transceiver_dom_info_dict['tx4power'] = 'N/A'
|
sysfsfile_eeprom.close()
|
||||||
|
except IOError:
|
||||||
|
print("Error: closing sysfs file %s" % file_path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value']
|
||||||
|
transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value']
|
||||||
|
transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value']
|
||||||
|
transceiver_dom_info_dict['rx2power'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['rx3power'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['rx4power'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value']
|
||||||
|
transceiver_dom_info_dict['tx2bias'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['tx3bias'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['tx4bias'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value']
|
||||||
|
transceiver_dom_info_dict['tx2power'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['tx3power'] = 'N/A'
|
||||||
|
transceiver_dom_info_dict['tx4power'] = 'N/A'
|
||||||
|
|
||||||
|
return transceiver_dom_info_dict
|
||||||
|
|
||||||
return transceiver_dom_info_dict
|
|
||||||
|
|
||||||
def get_transceiver_dom_threshold_info_dict(self, port_num):
|
def get_transceiver_dom_threshold_info_dict(self, port_num):
|
||||||
transceiver_dom_threshold_info_dict = {}
|
transceiver_dom_threshold_info_dict = {}
|
||||||
dom_info_dict_keys = ['temphighalarm', 'temphighwarning',
|
dom_info_dict_keys = ['temphighalarm', 'temphighwarning',
|
||||||
|
@ -1807,6 +1807,16 @@ static ssize_t show_psu(struct device *dev,
|
|||||||
}
|
}
|
||||||
ret = pow/10;
|
ret = pow/10;
|
||||||
break;
|
break;
|
||||||
|
case 11:
|
||||||
|
psu_status = smf_read_reg(data, PSU_1_STATUS);
|
||||||
|
if (psu_status &(2))
|
||||||
|
ret=1;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
psu_status = smf_read_reg(data, PSU_2_STATUS);
|
||||||
|
if (psu_status &(2))
|
||||||
|
ret=1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -2055,7 +2065,8 @@ static SENSOR_DEVICE_ATTR(psu2_presence, S_IRUGO, show_psu, NULL, 6);
|
|||||||
static SENSOR_DEVICE_ATTR(psu1_serialno, S_IRUGO, show_ppid, NULL, 10);
|
static SENSOR_DEVICE_ATTR(psu1_serialno, S_IRUGO, show_ppid, NULL, 10);
|
||||||
static SENSOR_DEVICE_ATTR(psu2_serialno, S_IRUGO, show_ppid, NULL, 11);
|
static SENSOR_DEVICE_ATTR(psu2_serialno, S_IRUGO, show_ppid, NULL, 11);
|
||||||
static SENSOR_DEVICE_ATTR(current_total_power, S_IRUGO, show_psu, NULL, 10);
|
static SENSOR_DEVICE_ATTR(current_total_power, S_IRUGO, show_psu, NULL, 10);
|
||||||
|
static SENSOR_DEVICE_ATTR(psu1_type, S_IRUGO, show_psu, NULL, 11);
|
||||||
|
static SENSOR_DEVICE_ATTR(psu2_type, S_IRUGO, show_psu, NULL, 12);
|
||||||
/* SMF Version */
|
/* SMF Version */
|
||||||
static SENSOR_DEVICE_ATTR(smf_version, S_IRUGO, show_smf_version, NULL, 0);
|
static SENSOR_DEVICE_ATTR(smf_version, S_IRUGO, show_smf_version, NULL, 0);
|
||||||
static SENSOR_DEVICE_ATTR(smf_firmware_ver, S_IRUGO, show_smf_version, NULL, 1);
|
static SENSOR_DEVICE_ATTR(smf_firmware_ver, S_IRUGO, show_smf_version, NULL, 1);
|
||||||
|
@ -0,0 +1,270 @@
|
|||||||
|
#! /usr/bin/python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC
|
||||||
|
#
|
||||||
|
# Module contains implementation of IpmiSensor and IpmiFru classes that
|
||||||
|
# provide Sensor's and FRU's information respectively.
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
|
||||||
|
# IPMI Request Network Function Codes
|
||||||
|
NetFn_SensorEvent = 0x04
|
||||||
|
NetFn_Storage = 0x0A
|
||||||
|
|
||||||
|
# IPMI Sensor Device Commands
|
||||||
|
Cmd_GetSensorReadingFactors = 0x23
|
||||||
|
Cmd_GetSensorThreshold = 0x27
|
||||||
|
Cmd_GetSensorReading = 0x2D
|
||||||
|
|
||||||
|
# IPMI FRU Device Commands
|
||||||
|
Cmd_ReadFRUData = 0x11
|
||||||
|
|
||||||
|
class IpmiSensor(object):
|
||||||
|
|
||||||
|
# Sensor Threshold types and their respective bit masks
|
||||||
|
THRESHOLD_BIT_MASK = {
|
||||||
|
"LowerNonCritical" : 0,
|
||||||
|
"LowerCritical" : 1,
|
||||||
|
"LowerNonRecoverable" : 2,
|
||||||
|
"UpperNonCritical" : 3,
|
||||||
|
"UpperCritical" : 4,
|
||||||
|
"UpperNonRecoverable" : 5
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, sensor_id, is_discrete=False):
|
||||||
|
self.id = sensor_id
|
||||||
|
self.is_discrete = is_discrete
|
||||||
|
|
||||||
|
def _get_ipmitool_raw_output(self, args):
|
||||||
|
"""
|
||||||
|
Returns a list the elements of which are the individual bytes of
|
||||||
|
ipmitool raw <cmd> command output.
|
||||||
|
"""
|
||||||
|
result_bytes = list()
|
||||||
|
result = ""
|
||||||
|
command = "ipmitool raw {}".format(args)
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
stdout = proc.communicate()[0]
|
||||||
|
proc.wait()
|
||||||
|
if not proc.returncode:
|
||||||
|
result = stdout.rstrip('\n')
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for i in result.split():
|
||||||
|
result_bytes.append(int(i, 16))
|
||||||
|
|
||||||
|
return result_bytes
|
||||||
|
|
||||||
|
def _get_converted_sensor_reading(self, raw_value):
|
||||||
|
"""
|
||||||
|
Returns a 2 element tuple(bool, int) in which first element
|
||||||
|
provides the validity of the reading and the second element is
|
||||||
|
the converted sensor reading
|
||||||
|
"""
|
||||||
|
# Get Sensor Reading Factors
|
||||||
|
cmd_args = "{} {} {} {}".format(NetFn_SensorEvent,
|
||||||
|
Cmd_GetSensorReadingFactors,
|
||||||
|
self.id, raw_value)
|
||||||
|
factors = self._get_ipmitool_raw_output(cmd_args)
|
||||||
|
|
||||||
|
if len(factors) != 7:
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
# Compute Twos complement
|
||||||
|
def get_twos_complement(val, bits):
|
||||||
|
if val & (1 << (bits - 1)):
|
||||||
|
val = val - (1 << bits)
|
||||||
|
return val
|
||||||
|
|
||||||
|
# Calculate actual sensor value from the raw sensor value
|
||||||
|
# using the sensor reading factors.
|
||||||
|
M = get_twos_complement(((factors[2] & 0xC0) << 8) | factors[1], 10)
|
||||||
|
B = get_twos_complement(((factors[4] & 0xC0) << 8) | factors[3], 10)
|
||||||
|
R_exp = get_twos_complement((factors[6] & 0xF0) >> 4, 4)
|
||||||
|
B_exp = get_twos_complement(factors[6] & 0x0F, 4)
|
||||||
|
|
||||||
|
converted_reading = ((M * raw_value) + (B * 10**B_exp)) * 10**R_exp
|
||||||
|
|
||||||
|
return True, converted_reading
|
||||||
|
|
||||||
|
def get_reading(self):
|
||||||
|
"""
|
||||||
|
For Threshold sensors, returns the sensor reading.
|
||||||
|
For Discrete sensors, returns the state value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A tuple (bool, int) where the first element provides the
|
||||||
|
validity of the reading and the second element provides the
|
||||||
|
sensor reading/state value.
|
||||||
|
"""
|
||||||
|
# Get Sensor Reading
|
||||||
|
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorReading,
|
||||||
|
self.id)
|
||||||
|
output = self._get_ipmitool_raw_output(cmd_args)
|
||||||
|
if len(output) != 4:
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
# Check reading/state unavailable
|
||||||
|
if output[1] & 0x20:
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
if self.is_discrete:
|
||||||
|
state = ((output[3] & 0x7F) << 8) | output[2]
|
||||||
|
return True, state
|
||||||
|
else:
|
||||||
|
return self._get_converted_sensor_reading(output[0])
|
||||||
|
|
||||||
|
def get_threshold(self, threshold_type):
|
||||||
|
"""
|
||||||
|
Returns the sensor's threshold value for a given threshold type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
threshold_type (str) - one of the below mentioned
|
||||||
|
threshold type strings
|
||||||
|
|
||||||
|
"LowerNonCritical"
|
||||||
|
"LowerCritical"
|
||||||
|
"LowerNonRecoverable"
|
||||||
|
"UpperNonCritical"
|
||||||
|
"UpperCritical"
|
||||||
|
"UpperNonRecoverable"
|
||||||
|
Returns:
|
||||||
|
A tuple (bool, int) where the first element provides the
|
||||||
|
validity of that threshold and second element provides the
|
||||||
|
threshold value.
|
||||||
|
"""
|
||||||
|
# Thresholds are not valid for discrete sensors
|
||||||
|
if self.is_discrete:
|
||||||
|
raise TypeError("Threshold is not applicable for Discrete Sensor")
|
||||||
|
|
||||||
|
if threshold_type not in self.THRESHOLD_BIT_MASK.keys():
|
||||||
|
raise ValueError("Invalid threshold type {} provided. Valid types "
|
||||||
|
"are {}".format(threshold_type,
|
||||||
|
self.THRESHOLD_BIT_MASK.keys()))
|
||||||
|
|
||||||
|
bit_mask = self.THRESHOLD_BIT_MASK[threshold_type]
|
||||||
|
|
||||||
|
# Get Sensor Threshold
|
||||||
|
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorThreshold,
|
||||||
|
self.id)
|
||||||
|
thresholds = self._get_ipmitool_raw_output(cmd_args)
|
||||||
|
if len(thresholds) != 7:
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
valid_thresholds = thresholds.pop(0)
|
||||||
|
# Check whether particular threshold is readable
|
||||||
|
if valid_thresholds & (1 << bit_mask):
|
||||||
|
return self._get_converted_sensor_reading(thresholds[bit_mask])
|
||||||
|
else:
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
class IpmiFru(object):
|
||||||
|
|
||||||
|
def __init__(self, fru_id):
|
||||||
|
self.id = fru_id
|
||||||
|
|
||||||
|
def _get_ipmitool_fru_print(self):
|
||||||
|
result = ""
|
||||||
|
command = "ipmitool fru print {}".format(self.id)
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
stdout = proc.communicate()[0]
|
||||||
|
proc.wait()
|
||||||
|
if not proc.returncode:
|
||||||
|
result = stdout.rstrip('\n')
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_from_fru(self, info):
|
||||||
|
"""
|
||||||
|
Returns a string containing the info from FRU
|
||||||
|
"""
|
||||||
|
fru_output = self._get_ipmitool_fru_print()
|
||||||
|
if not fru_output:
|
||||||
|
return "NA"
|
||||||
|
|
||||||
|
info_req = re.search(r"%s\s*:(.*)"%info, fru_output)
|
||||||
|
if not info_req:
|
||||||
|
return "NA"
|
||||||
|
|
||||||
|
return info_req.group(1).strip()
|
||||||
|
|
||||||
|
def get_board_serial(self):
|
||||||
|
"""
|
||||||
|
Returns a string containing the Serial Number of the device.
|
||||||
|
"""
|
||||||
|
return self._get_from_fru('Board Serial')
|
||||||
|
|
||||||
|
def get_board_part_number(self):
|
||||||
|
"""
|
||||||
|
Returns a string containing the Part Number of the device.
|
||||||
|
"""
|
||||||
|
return self._get_from_fru('Board Part Number')
|
||||||
|
|
||||||
|
def get_board_mfr_id(self):
|
||||||
|
"""
|
||||||
|
Returns a string containing the manufacturer id of the FRU.
|
||||||
|
"""
|
||||||
|
return self._get_from_fru('Board Mfg')
|
||||||
|
|
||||||
|
def get_board_product(self):
|
||||||
|
"""
|
||||||
|
Returns a string containing the manufacturer id of the FRU.
|
||||||
|
"""
|
||||||
|
return self._get_from_fru('Board Product')
|
||||||
|
|
||||||
|
|
||||||
|
def get_fru_data(self, offset, count=1):
|
||||||
|
"""
|
||||||
|
Reads and returns the FRU data at the provided offset.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
offset (int) - FRU offset to read
|
||||||
|
count (int) - Number of bytes to read [optional, default = 1]
|
||||||
|
Returns:
|
||||||
|
A tuple (bool, list(int)) where the first element provides
|
||||||
|
the validity of the data read and the second element is a
|
||||||
|
list, the elements of which are the individual bytes of the
|
||||||
|
FRU data read.
|
||||||
|
"""
|
||||||
|
result_bytes = list()
|
||||||
|
is_valid = True
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
offset_LSB = offset & 0xFF
|
||||||
|
offset_MSB = offset & 0xFF00
|
||||||
|
command = "ipmitool raw {} {} {} {} {} {}".format(NetFn_Storage,
|
||||||
|
Cmd_ReadFRUData,
|
||||||
|
self.id, offset_LSB,
|
||||||
|
offset_MSB, count)
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
stdout = proc.communicate()[0]
|
||||||
|
proc.wait()
|
||||||
|
if not proc.returncode:
|
||||||
|
result = stdout.rstrip('\n')
|
||||||
|
except Exception as e:
|
||||||
|
is_valid = False
|
||||||
|
|
||||||
|
if (not result) or (not is_valid):
|
||||||
|
return False, result_bytes
|
||||||
|
|
||||||
|
for i in result.split():
|
||||||
|
result_bytes.append(int(i, 16))
|
||||||
|
|
||||||
|
read_count = result_bytes.pop(0)
|
||||||
|
if read_count != count:
|
||||||
|
return False, result_bytes
|
||||||
|
else:
|
||||||
|
return True, result_bytes
|
@ -0,0 +1,24 @@
|
|||||||
|
# Helper functions to access hardware
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import mmap
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Read PCI device
|
||||||
|
|
||||||
|
def pci_mem_read(mm, offset):
|
||||||
|
mm.seek(offset)
|
||||||
|
read_data_stream = mm.read(4)
|
||||||
|
return struct.unpack('I',read_data_stream)[0]
|
||||||
|
|
||||||
|
def pci_get_value(resource, offset):
|
||||||
|
with open(resource, 'r+b') as fd:
|
||||||
|
mm = mmap.mmap(fd.fileno(), 0)
|
||||||
|
val = pci_mem_read(mm, offset)
|
||||||
|
mm.close()
|
||||||
|
return val
|
||||||
|
|
||||||
|
# Read I2C device
|
||||||
|
|
||||||
|
def i2c_get(bus, i2caddr, ofs):
|
||||||
|
return int(subprocess.check_output(['/usr/sbin/i2cget', '-y', str(bus), str(i2caddr), str(ofs)]), 16)
|
@ -6,5 +6,6 @@ s5232f/scripts/pcisysfs.py usr/bin
|
|||||||
s5232f/scripts/qsfp_irq_enable.py usr/bin
|
s5232f/scripts/qsfp_irq_enable.py usr/bin
|
||||||
s5232f/cfg/s5232f-modules.conf etc/modules-load.d
|
s5232f/cfg/s5232f-modules.conf etc/modules-load.d
|
||||||
s5232f/systemd/platform-modules-s5232f.service etc/systemd/system
|
s5232f/systemd/platform-modules-s5232f.service etc/systemd/system
|
||||||
|
s5232f/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-dellemc_s5232f_c3538-r0
|
||||||
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_s5232f_c3538-r0
|
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_s5232f_c3538-r0
|
||||||
common/fw-updater usr/local/bin
|
common/fw-updater usr/local/bin
|
||||||
|
@ -6,5 +6,6 @@ z9264f/scripts/pcisysfs.py usr/bin
|
|||||||
z9264f/scripts/qsfp_irq_enable.py usr/bin
|
z9264f/scripts/qsfp_irq_enable.py usr/bin
|
||||||
z9264f/cfg/z9264f-modules.conf etc/modules-load.d
|
z9264f/cfg/z9264f-modules.conf etc/modules-load.d
|
||||||
z9264f/systemd/platform-modules-z9264f.service etc/systemd/system
|
z9264f/systemd/platform-modules-z9264f.service etc/systemd/system
|
||||||
|
z9264f/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-dellemc_z9264f_c3538-r0
|
||||||
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_z9264f_c3538-r0
|
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_z9264f_c3538-r0
|
||||||
common/fw-updater usr/local/bin
|
common/fw-updater usr/local/bin
|
||||||
|
@ -29,6 +29,16 @@ override_dh_auto_build:
|
|||||||
cd $(MOD_SRC_DIR)/$${mod}; \
|
cd $(MOD_SRC_DIR)/$${mod}; \
|
||||||
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
|
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
|
||||||
cd $(MOD_SRC_DIR); \
|
cd $(MOD_SRC_DIR); \
|
||||||
|
elif [ $$mod = "z9264f" ]; then \
|
||||||
|
cp $(COMMON_DIR)/ipmihelper.py $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
|
||||||
|
cd $(MOD_SRC_DIR)/$${mod}; \
|
||||||
|
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
|
||||||
|
cd $(MOD_SRC_DIR); \
|
||||||
|
elif [ $$mod = "s5232f" ]; then \
|
||||||
|
cp $(COMMON_DIR)/ipmihelper.py $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
|
||||||
|
cd $(MOD_SRC_DIR)/$${mod}; \
|
||||||
|
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
|
||||||
|
cd $(MOD_SRC_DIR); \
|
||||||
fi; \
|
fi; \
|
||||||
echo "making man page alias $$mod -> $$mod APIs";\
|
echo "making man page alias $$mod -> $$mod APIs";\
|
||||||
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
|
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
|
||||||
@ -67,6 +77,16 @@ override_dh_clean:
|
|||||||
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
|
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
|
||||||
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
|
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
|
||||||
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
|
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
|
||||||
|
elif [ $$mod = "z9264f" ]; then \
|
||||||
|
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
|
||||||
|
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
|
||||||
|
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
|
||||||
|
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
|
||||||
|
elif [ $$mod = "s5232f" ]; then \
|
||||||
|
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
|
||||||
|
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
|
||||||
|
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
|
||||||
|
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
|
||||||
fi; \
|
fi; \
|
||||||
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \
|
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \
|
||||||
done)
|
done)
|
||||||
|
@ -133,6 +133,20 @@ platform_firmware_versions() {
|
|||||||
r_maj=`/usr/sbin/i2cget -y 600 0x33 0x1 | sed ' s/.*\(0x..\)$/\1/'`
|
r_maj=`/usr/sbin/i2cget -y 600 0x33 0x1 | sed ' s/.*\(0x..\)$/\1/'`
|
||||||
echo "Slave CPLD 4: $((r_maj)).$((r_min))" >> $FIRMWARE_VERSION_FILE
|
echo "Slave CPLD 4: $((r_maj)).$((r_min))" >> $FIRMWARE_VERSION_FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_python_api_package() {
|
||||||
|
device="/usr/share/sonic/device"
|
||||||
|
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
|
||||||
|
|
||||||
|
rv=$(pip install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_python_api_package() {
|
||||||
|
rv=$(pip show sonic-platform > /dev/null 2>/dev/null)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
rv=$(pip uninstall -y sonic-platform > /dev/null 2>/dev/null)
|
||||||
|
fi
|
||||||
|
}
|
||||||
init_devnum
|
init_devnum
|
||||||
|
|
||||||
if [ "$1" == "init" ]; then
|
if [ "$1" == "init" ]; then
|
||||||
@ -147,6 +161,7 @@ if [ "$1" == "init" ]; then
|
|||||||
switch_board_qsfp "new_device"
|
switch_board_qsfp "new_device"
|
||||||
switch_board_modsel
|
switch_board_modsel
|
||||||
switch_board_led_default
|
switch_board_led_default
|
||||||
|
install_python_api_package
|
||||||
python /usr/bin/qsfp_irq_enable.py
|
python /usr/bin/qsfp_irq_enable.py
|
||||||
platform_firmware_versions
|
platform_firmware_versions
|
||||||
|
|
||||||
@ -155,9 +170,9 @@ elif [ "$1" == "deinit" ]; then
|
|||||||
switch_board_qsfp "delete_device"
|
switch_board_qsfp "delete_device"
|
||||||
switch_board_sfp "delete_device"
|
switch_board_sfp "delete_device"
|
||||||
switch_board_qsfp_mux "delete_device"
|
switch_board_qsfp_mux "delete_device"
|
||||||
|
|
||||||
modprobe -r i2c-mux-pca954x
|
modprobe -r i2c-mux-pca954x
|
||||||
modprobe -r i2c-dev
|
modprobe -r i2c-dev
|
||||||
|
remove_python_api_package
|
||||||
else
|
else
|
||||||
echo "s5232f_platform : Invalid option !"
|
echo "s5232f_platform : Invalid option !"
|
||||||
fi
|
fi
|
||||||
|
1
platform/broadcom/sonic-platform-modules-dell/s5232f/setup.py
Symbolic link
1
platform/broadcom/sonic-platform-modules-dell/s5232f/setup.py
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../s6100/setup.py
|
@ -0,0 +1,2 @@
|
|||||||
|
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "watchdog"]
|
||||||
|
from sonic_platform import *
|
@ -0,0 +1,232 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# DELLEMC S5232F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the platform information
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
|
try:
|
||||||
|
import sys
|
||||||
|
from sonic_platform_base.chassis_base import ChassisBase
|
||||||
|
from sonic_platform.sfp import Sfp
|
||||||
|
from sonic_platform.eeprom import Eeprom
|
||||||
|
from sonic_platform.component import Component
|
||||||
|
from sonic_platform.psu import Psu
|
||||||
|
from sonic_platform.thermal import Thermal
|
||||||
|
from sonic_platform.watchdog import Watchdog
|
||||||
|
from sonic_platform.fan import Fan
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
MAX_S5232F_COMPONENT = 6 # BIOS,BMC,FPGA,SYSTEM CPLD,4 SLAVE CPLDs
|
||||||
|
MAX_S5232F_FANTRAY =4
|
||||||
|
MAX_S5232F_FAN = 2
|
||||||
|
MAX_S5232F_PSU = 2
|
||||||
|
MAX_S5232F_THERMAL = 8
|
||||||
|
|
||||||
|
|
||||||
|
class Chassis(ChassisBase):
|
||||||
|
"""
|
||||||
|
DELLEMC Platform-specific Chassis class
|
||||||
|
"""
|
||||||
|
|
||||||
|
oir_fd = -1
|
||||||
|
epoll = -1
|
||||||
|
|
||||||
|
_global_port_pres_dict = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
ChassisBase.__init__(self)
|
||||||
|
# sfp.py will read eeprom contents and retrive the eeprom data.
|
||||||
|
# We pass the eeprom path from chassis.py
|
||||||
|
self.PORT_START = 1
|
||||||
|
self.PORT_END = 34
|
||||||
|
self.PORTS_IN_BLOCK = (self.PORT_END + 1)
|
||||||
|
_sfp_port = range(33, self.PORT_END + 1)
|
||||||
|
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
|
||||||
|
|
||||||
|
for index in range(self.PORT_START, self.PORTS_IN_BLOCK):
|
||||||
|
port_num = index + 1
|
||||||
|
eeprom_path = eeprom_base.format(port_num)
|
||||||
|
if index not in _sfp_port:
|
||||||
|
sfp_node = Sfp(index, 'QSFP', eeprom_path)
|
||||||
|
else:
|
||||||
|
sfp_node = Sfp(index, 'SFP', eeprom_path)
|
||||||
|
self._sfp_list.append(sfp_node)
|
||||||
|
|
||||||
|
self._eeprom = Eeprom()
|
||||||
|
self._watchdog = Watchdog()
|
||||||
|
self._num_sfps = self.PORT_END
|
||||||
|
self._num_fans = MAX_S5232F_FANTRAY * MAX_S5232F_FAN
|
||||||
|
self._fan_list = [Fan(i, j) for i in range(MAX_S5232F_FANTRAY) \
|
||||||
|
for j in range(MAX_S5232F_FAN)]
|
||||||
|
self._psu_list = [Psu(i) for i in range(MAX_S5232F_PSU)]
|
||||||
|
self._thermal_list = [Thermal(i) for i in range(MAX_S5232F_THERMAL)]
|
||||||
|
self._component_list = [Component(i) for i in range(MAX_S5232F_COMPONENT)]
|
||||||
|
|
||||||
|
for port_num in range(self.PORT_START, self.PORTS_IN_BLOCK):
|
||||||
|
# sfp get uses zero-indexing, but port numbers start from 1
|
||||||
|
presence = self.get_sfp(port_num).get_presence()
|
||||||
|
self._global_port_pres_dict[port_num] = '1' if presence else '0'
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.oir_fd != -1:
|
||||||
|
self.epoll.unregister(self.oir_fd.fileno())
|
||||||
|
self.epoll.close()
|
||||||
|
self.oir_fd.close()
|
||||||
|
|
||||||
|
# check for this event change for sfp / do we need to handle timeout/sleep
|
||||||
|
|
||||||
|
def get_change_event(self, timeout=0):
|
||||||
|
from time import sleep
|
||||||
|
"""
|
||||||
|
Returns a nested dictionary containing all devices which have
|
||||||
|
experienced a change at chassis level
|
||||||
|
"""
|
||||||
|
port_dict = {}
|
||||||
|
change_dict = {}
|
||||||
|
change_dict['sfp'] = port_dict
|
||||||
|
elapsed_time_ms = 0
|
||||||
|
sleep_time_ms = 500
|
||||||
|
sleep_time = sleep_time_ms / 1000
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
|
||||||
|
presence = self.get_sfp(port_num).get_presence()
|
||||||
|
if(presence and self._global_port_pres_dict[port_num] == '0'):
|
||||||
|
self._global_port_pres_dict[port_num] = '1'
|
||||||
|
port_dict[port_num] = '1'
|
||||||
|
elif(not presence and
|
||||||
|
self._global_port_pres_dict[port_num] == '1'):
|
||||||
|
self._global_port_pres_dict[port_num] = '0'
|
||||||
|
port_dict[port_num] = '0'
|
||||||
|
|
||||||
|
if(len(port_dict) > 0):
|
||||||
|
return True, change_dict
|
||||||
|
if timeout != 0:
|
||||||
|
elapsed_time_ms += sleep_time_ms
|
||||||
|
if elapsed_time_ms > timeout:
|
||||||
|
break
|
||||||
|
|
||||||
|
sleep(sleep_time)
|
||||||
|
return True, change_dict
|
||||||
|
|
||||||
|
def get_sfp(self, index):
|
||||||
|
"""
|
||||||
|
Retrieves sfp represented by (0-based) index <index>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index: An integer, the index (0-based) of the sfp to retrieve.
|
||||||
|
The index should be the sequence of a physical port in a chassis,
|
||||||
|
starting from 0.
|
||||||
|
For example, 0 for Ethernet0, 1 for Ethernet4 and so on.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An object dervied from SfpBase representing the specified sfp
|
||||||
|
"""
|
||||||
|
sfp = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The index will start from 0
|
||||||
|
sfp = self._sfp_list[index-1]
|
||||||
|
except IndexError:
|
||||||
|
sys.stderr.write("SFP index {} out of range (0-{})\n".format(
|
||||||
|
index, len(self._sfp_list)-1))
|
||||||
|
return sfp
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the chassis
|
||||||
|
Returns:
|
||||||
|
string: The name of the chassis
|
||||||
|
"""
|
||||||
|
return self._eeprom.modelstr()
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the chassis
|
||||||
|
Returns:
|
||||||
|
bool: True if chassis is present, False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the model number (or part number) of the chassis
|
||||||
|
Returns:
|
||||||
|
string: Model/part number of chassis
|
||||||
|
"""
|
||||||
|
return self._eeprom.part_number_str()
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the chassis (Service tag)
|
||||||
|
Returns:
|
||||||
|
string: Serial number of chassis
|
||||||
|
"""
|
||||||
|
return self._eeprom.serial_str()
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the chassis
|
||||||
|
Returns:
|
||||||
|
bool: A boolean value, True if chassis is operating properly
|
||||||
|
False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_base_mac(self):
|
||||||
|
"""
|
||||||
|
Retrieves the base MAC address for the chassis
|
||||||
|
Returns:
|
||||||
|
A string containing the MAC address in the format
|
||||||
|
'XX:XX:XX:XX:XX:XX'
|
||||||
|
"""
|
||||||
|
return self._eeprom.base_mac_addr('')
|
||||||
|
|
||||||
|
def get_serial_number(self):
|
||||||
|
"""
|
||||||
|
Retrieves the hardware serial number for the chassis
|
||||||
|
Returns:
|
||||||
|
A string containing the hardware serial number for this chassis.
|
||||||
|
"""
|
||||||
|
return self._eeprom.serial_number_str()
|
||||||
|
|
||||||
|
def get_system_eeprom_info(self):
|
||||||
|
"""
|
||||||
|
Retrieves the full content of system EEPROM information for the chassis
|
||||||
|
Returns:
|
||||||
|
A dictionary where keys are the type code defined in
|
||||||
|
OCP ONIE TlvInfo EEPROM format and values are their corresponding
|
||||||
|
values.
|
||||||
|
"""
|
||||||
|
return self._eeprom.system_eeprom_info()
|
||||||
|
|
||||||
|
def get_eeprom(self):
|
||||||
|
"""
|
||||||
|
Retrieves the Sys Eeprom instance for the chassis.
|
||||||
|
Returns :
|
||||||
|
The instance of the Sys Eeprom
|
||||||
|
"""
|
||||||
|
return self._eeprom
|
||||||
|
|
||||||
|
def get_num_fans(self):
|
||||||
|
"""
|
||||||
|
Retrives the number of Fans on the chassis.
|
||||||
|
Returns :
|
||||||
|
An integer represents the number of Fans on the chassis.
|
||||||
|
"""
|
||||||
|
return self._num_fans
|
||||||
|
|
||||||
|
def get_num_sfps(self):
|
||||||
|
"""
|
||||||
|
Retrives the numnber of Media on the chassis.
|
||||||
|
Returns:
|
||||||
|
An integer represences the number of SFPs on the chassis.
|
||||||
|
"""
|
||||||
|
return self._num_sfps
|
@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DELLEMC S5232F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the Components' (e.g., BIOS, CPLD, FPGA, BMC etc.) available in
|
||||||
|
# the platform
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
from sonic_platform_base.component_base import ComponentBase
|
||||||
|
import sonic_platform.hwaccess as hwaccess
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
def get_bios_version():
|
||||||
|
return subprocess.check_output(['dmidecode', '-s', 'system-version']).strip()
|
||||||
|
|
||||||
|
def get_fpga_version():
|
||||||
|
val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:04:00.0/resource0', 0)
|
||||||
|
return '{}.{}'.format((val >> 8) & 0xff, val & 0xff)
|
||||||
|
|
||||||
|
def get_bmc_version():
|
||||||
|
return subprocess.check_output(
|
||||||
|
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
def get_cpld_version(bus, i2caddr):
|
||||||
|
return '{}.{}'.format(hwaccess.i2c_get(bus, i2caddr, 1),
|
||||||
|
hwaccess.i2c_get(bus, i2caddr, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_cpld0_version():
|
||||||
|
return get_cpld_version(601, 0x31)
|
||||||
|
|
||||||
|
def get_cpld1_version():
|
||||||
|
return get_cpld_version(600, 0x30)
|
||||||
|
|
||||||
|
def get_cpld2_version():
|
||||||
|
return get_cpld_version(600, 0x31)
|
||||||
|
|
||||||
|
|
||||||
|
class Component(ComponentBase):
|
||||||
|
"""DellEMC Platform-specific Component class"""
|
||||||
|
|
||||||
|
CHASSIS_COMPONENTS = [
|
||||||
|
['BIOS',
|
||||||
|
'Performs initialization of hardware components during booting',
|
||||||
|
get_bios_version
|
||||||
|
],
|
||||||
|
|
||||||
|
['FPGA',
|
||||||
|
'Used for managing the system LEDs',
|
||||||
|
get_fpga_version
|
||||||
|
],
|
||||||
|
|
||||||
|
['BMC',
|
||||||
|
'Platform management controller for on-board temperature,monitoring, in-chassis power, Fan and LED control',
|
||||||
|
get_bmc_version
|
||||||
|
],
|
||||||
|
|
||||||
|
['System CPLD',
|
||||||
|
'Used for managing the CPU power sequence and CPU states',
|
||||||
|
get_cpld0_version
|
||||||
|
],
|
||||||
|
|
||||||
|
['Slave CPLD 1',
|
||||||
|
'Used for managing SFP28/QSFP28 port transceivers (SFP28 1-24, QSFP28 1-4)',
|
||||||
|
get_cpld1_version
|
||||||
|
],
|
||||||
|
|
||||||
|
['Slave CPLD 2',
|
||||||
|
'Used for managing SFP28/QSFP28 port transceivers (SFP28 25-48, QSFP28 5-8)',
|
||||||
|
get_cpld2_version
|
||||||
|
],
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, component_index = 0):
|
||||||
|
self.index = component_index
|
||||||
|
self.name = self.CHASSIS_COMPONENTS[self.index][0]
|
||||||
|
self.description = self.CHASSIS_COMPONENTS[self.index][1]
|
||||||
|
#self.version = self.CHASSIS_COMPONENTS[self.index][2]()
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the component
|
||||||
|
Returns:
|
||||||
|
A string containing the name of the component
|
||||||
|
"""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
"""
|
||||||
|
Retrieves the description of the component
|
||||||
|
Returns:
|
||||||
|
A string containing the description of the component
|
||||||
|
"""
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
def get_firmware_version(self):
|
||||||
|
"""
|
||||||
|
Retrieves the firmware version of the component
|
||||||
|
Returns:
|
||||||
|
A string containing the firmware version of the component
|
||||||
|
"""
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
def install_firmware(self, image_path):
|
||||||
|
"""
|
||||||
|
Installs firmware to the component
|
||||||
|
Args:
|
||||||
|
image_path: A string, path to firmware image
|
||||||
|
Returns:
|
||||||
|
A boolean, True if install was successful, False if not
|
||||||
|
"""
|
||||||
|
return False
|
@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# DellEmc S5248F
|
||||||
|
#
|
||||||
|
# Platform and model specific eeprom subclass, inherits from the base class,
|
||||||
|
# and provides the followings:
|
||||||
|
# - the eeprom format definition
|
||||||
|
# - specific encoder/decoder if there is special need
|
||||||
|
#############################################################################
|
||||||
|
try:
|
||||||
|
import os.path
|
||||||
|
from sonic_eeprom import eeprom_tlvinfo
|
||||||
|
import binascii
|
||||||
|
except ImportError, e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.eeprom_path = None
|
||||||
|
for b in (0, 1):
|
||||||
|
f = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'.format(b)
|
||||||
|
if os.path.exists(f):
|
||||||
|
self.eeprom_path = f
|
||||||
|
break
|
||||||
|
if self.eeprom_path is None:
|
||||||
|
return
|
||||||
|
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")
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
Returns the serial number
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_SERIAL_NUMBER)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def base_mac_addr(self, e):
|
||||||
|
"""
|
||||||
|
Returns the base mac address found in the system EEPROM
|
||||||
|
"""
|
||||||
|
(is_valid, t) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_MAC_BASE)
|
||||||
|
if not is_valid or t[1] != 6:
|
||||||
|
return super(eeprom_tlvinfo.TlvInfoDecoder, self).switchaddrstr(t)
|
||||||
|
|
||||||
|
return ":".join([binascii.b2a_hex(T) for T in t[2]])
|
||||||
|
|
||||||
|
def modelstr(self):
|
||||||
|
"""
|
||||||
|
Returns the Model name
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_PRODUCT_NAME)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def part_number_str(self):
|
||||||
|
"""
|
||||||
|
Returns the part number
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_PART_NUMBER)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def serial_str(self):
|
||||||
|
"""
|
||||||
|
Returns the servicetag number
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_SERVICE_TAG)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def revision_str(self):
|
||||||
|
"""
|
||||||
|
Returns the device revision
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_DEVICE_VERSION)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC S5232F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the Fans' information which are available in the platform.
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.fan_base import FanBase
|
||||||
|
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
FAN1_MAX_SPEED_OFFSET = 71
|
||||||
|
FAN2_MAX_SPEED_OFFSET = 73
|
||||||
|
PSU_FAN_MAX_SPEED_OFFSET = 50
|
||||||
|
FAN_DIRECTION_OFFSET = 69
|
||||||
|
PSU_FAN_DIRECTION_OFFSET = 47
|
||||||
|
|
||||||
|
|
||||||
|
class Fan(FanBase):
|
||||||
|
"""DellEMC Platform-specific Fan class"""
|
||||||
|
# { FAN-ID: { Sensor-Name: Sensor-ID } }
|
||||||
|
FAN_SENSOR_MAPPING = { 1: {"Prsnt": 0x51, "State": 0x64, "Speed": 0x24},
|
||||||
|
2: {"Prsnt": 0x51, "State": 0x60, "Speed": 0x20},
|
||||||
|
3: {"Prsnt": 0x52, "State": 0x65, "Speed": 0x25},
|
||||||
|
4: {"Prsnt": 0x52, "State": 0x61, "Speed": 0x21},
|
||||||
|
5: {"Prsnt": 0x53, "State": 0x66, "Speed": 0x26},
|
||||||
|
6: {"Prsnt": 0x53, "State": 0x62, "Speed": 0x22},
|
||||||
|
7: {"Prsnt": 0x54, "State": 0x67, "Speed": 0x27},
|
||||||
|
8: {"Prsnt": 0x54, "State": 0x63, "Speed": 0x23} }
|
||||||
|
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x46, "Speed": 0x2e},
|
||||||
|
2: {"State": 0x47, "Speed": 0x2f} }
|
||||||
|
|
||||||
|
# { FANTRAY-ID: FRU-ID }
|
||||||
|
FAN_FRU_MAPPING = { 1: 3, 2: 4, 3: 5, 4: 6 }
|
||||||
|
PSU_FRU_MAPPING = { 1: 1, 2: 2 }
|
||||||
|
|
||||||
|
def __init__(self, fantray_index=1, fan_index=1, psu_fan=False,
|
||||||
|
dependency=None):
|
||||||
|
self.is_psu_fan = psu_fan
|
||||||
|
if not self.is_psu_fan:
|
||||||
|
# API index is starting from 0, DellEMC platform index is
|
||||||
|
# starting from 1
|
||||||
|
self.fantrayindex = fantray_index + 1
|
||||||
|
self.fanindex = fan_index + 1
|
||||||
|
if (self.fanindex == 1):
|
||||||
|
self.max_speed_offset = FAN1_MAX_SPEED_OFFSET
|
||||||
|
else:
|
||||||
|
self.max_speed_offset = FAN2_MAX_SPEED_OFFSET
|
||||||
|
self.fan_direction_offset = FAN_DIRECTION_OFFSET
|
||||||
|
self.index = (self.fantrayindex - 1) * 2 + self.fanindex
|
||||||
|
self.prsnt_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Prsnt"],
|
||||||
|
is_discrete=True)
|
||||||
|
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])
|
||||||
|
else:
|
||||||
|
self.dependency = dependency
|
||||||
|
self.fanindex = fan_index
|
||||||
|
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.max_speed_offset = PSU_FAN_MAX_SPEED_OFFSET
|
||||||
|
self.fan_direction_offset = PSU_FAN_DIRECTION_OFFSET
|
||||||
|
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
|
||||||
|
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the device
|
||||||
|
Returns:
|
||||||
|
String: The name of the device
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return "PSU{} Fan".format(self.fanindex)
|
||||||
|
else:
|
||||||
|
return "FanTray{}-Fan{}".format(self.fantrayindex, self.fanindex)
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the part number of the FAN
|
||||||
|
Returns:
|
||||||
|
String: Part number of FAN
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return 'NA'
|
||||||
|
else:
|
||||||
|
return self.fru.get_board_part_number()
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the FAN
|
||||||
|
Returns:
|
||||||
|
String: Serial number of FAN
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return 'NA'
|
||||||
|
else:
|
||||||
|
return self.fru.get_board_serial()
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the FAN
|
||||||
|
Returns:
|
||||||
|
bool: True if fan is present, False if not
|
||||||
|
"""
|
||||||
|
presence = False
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return self.dependency.get_presence()
|
||||||
|
else:
|
||||||
|
is_valid, state = self.prsnt_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state & 0b1):
|
||||||
|
presence = True
|
||||||
|
return presence
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the FAN
|
||||||
|
Returns:
|
||||||
|
bool: True if FAN is operating properly, False if not
|
||||||
|
"""
|
||||||
|
status = False
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state == 0x00):
|
||||||
|
status = True
|
||||||
|
return status
|
||||||
|
|
||||||
|
def get_direction(self):
|
||||||
|
"""
|
||||||
|
Retrieves the fan airfow direction
|
||||||
|
Returns:
|
||||||
|
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
|
||||||
|
depending on fan direction
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
In DellEMC platforms,
|
||||||
|
- Forward/Exhaust : Air flows from Port side to Fan side.
|
||||||
|
- Reverse/Intake : Air flows from Fan side to Port side.
|
||||||
|
"""
|
||||||
|
direction = [self.FAN_DIRECTION_EXHAUST, self.FAN_DIRECTION_INTAKE]
|
||||||
|
fan_status = self.get_status()
|
||||||
|
if not fan_status:
|
||||||
|
return 'NA'
|
||||||
|
is_valid, fan_direction = self.fru.get_fru_data(self.fan_direction_offset)
|
||||||
|
if is_valid:
|
||||||
|
return direction[fan_direction[0]]
|
||||||
|
else:
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_speed(self):
|
||||||
|
"""
|
||||||
|
Retrieves the speed of the fan
|
||||||
|
Returns:
|
||||||
|
int: percentage of the max fan speed
|
||||||
|
"""
|
||||||
|
if self.max_speed == 0:
|
||||||
|
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
|
||||||
|
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
|
||||||
|
is_valid, fan_speed = self.speed_sensor.get_reading()
|
||||||
|
if not is_valid or self.max_speed == 0:
|
||||||
|
speed = 0
|
||||||
|
else:
|
||||||
|
speed = (100 * fan_speed)/self.max_speed
|
||||||
|
return speed
|
||||||
|
|
||||||
|
def get_speed_rpm(self):
|
||||||
|
"""
|
||||||
|
Retrieves the speed of the fan
|
||||||
|
Returns:
|
||||||
|
int: percentage of the max fan speed
|
||||||
|
"""
|
||||||
|
fan_speed = 0
|
||||||
|
is_valid, fan_speed = self.speed_sensor.get_reading()
|
||||||
|
return fan_speed
|
@ -0,0 +1 @@
|
|||||||
|
../../common/sonic_platform/hwaccess.py
|
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the platform information
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.platform_base import PlatformBase
|
||||||
|
from sonic_platform.chassis import Chassis
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Platform(PlatformBase):
|
||||||
|
"""
|
||||||
|
DELLEMC Platform-specific class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
PlatformBase.__init__(self)
|
||||||
|
self._chassis = Chassis()
|
@ -0,0 +1,179 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC S5232F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the PSUs' information which are available in the platform
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.psu_base import PsuBase
|
||||||
|
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
|
||||||
|
from sonic_platform.fan import Fan
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Psu(PsuBase):
|
||||||
|
"""DellEMC Platform-specific PSU class"""
|
||||||
|
|
||||||
|
# { PSU-ID: { Sensor-Name: Sensor-ID } }
|
||||||
|
SENSOR_MAPPING = { 1: { "State": 0x31, "Current": 0x39,
|
||||||
|
"Power": 0x37, "Voltage": 0x38 },
|
||||||
|
2: { "State": 0x32, "Current": 0x3F,
|
||||||
|
"Power": 0x3D, "Voltage": 0x3E } }
|
||||||
|
# ( PSU-ID: FRU-ID }
|
||||||
|
FRU_MAPPING = { 1: 1, 2: 2 }
|
||||||
|
|
||||||
|
def __init__(self, psu_index):
|
||||||
|
PsuBase.__init__(self)
|
||||||
|
# PSU is 1-based in DellEMC platforms
|
||||||
|
self.index = psu_index + 1
|
||||||
|
self.state_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["State"],
|
||||||
|
is_discrete=True)
|
||||||
|
self.voltage_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Voltage"])
|
||||||
|
self.current_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Current"])
|
||||||
|
self.power_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Power"])
|
||||||
|
self.fru = IpmiFru(self.FRU_MAPPING[self.index])
|
||||||
|
|
||||||
|
self._fan_list.append(Fan(fantray_index=1,fan_index=self.index, psu_fan=True,
|
||||||
|
dependency=self))
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the device
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: The name of the device
|
||||||
|
"""
|
||||||
|
return "PSU{}".format(self.index)
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the Power Supply Unit (PSU)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if PSU is present, False if not
|
||||||
|
"""
|
||||||
|
presence = False
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state & 0b1):
|
||||||
|
presence = True
|
||||||
|
|
||||||
|
return presence
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the part number of the PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Part number of PSU
|
||||||
|
"""
|
||||||
|
return self.fru.get_board_part_number()
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Serial number of PSU
|
||||||
|
"""
|
||||||
|
return self.fru.get_board_serial()
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if PSU is operating properly, False if not
|
||||||
|
"""
|
||||||
|
status = False
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state == 0x01):
|
||||||
|
status = True
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
def get_voltage(self):
|
||||||
|
"""
|
||||||
|
Retrieves current PSU voltage output
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the output voltage in volts,
|
||||||
|
e.g. 12.1
|
||||||
|
"""
|
||||||
|
is_valid, voltage = self.voltage_sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
voltage = 0
|
||||||
|
|
||||||
|
return float(voltage)
|
||||||
|
|
||||||
|
def get_current(self):
|
||||||
|
"""
|
||||||
|
Retrieves present electric current supplied by PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, electric current in amperes,
|
||||||
|
e.g. 15.4
|
||||||
|
"""
|
||||||
|
is_valid, current = self.current_sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
current = 0
|
||||||
|
|
||||||
|
return float(current)
|
||||||
|
|
||||||
|
def get_power(self):
|
||||||
|
"""
|
||||||
|
Retrieves current energy supplied by PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the power in watts,
|
||||||
|
e.g. 302.6
|
||||||
|
"""
|
||||||
|
is_valid, power = self.power_sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
power = 0
|
||||||
|
|
||||||
|
return float(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
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state == 0x01):
|
||||||
|
status = True
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
def get_mfr_id(self):
|
||||||
|
"""
|
||||||
|
Retrives the Manufacturer Id of PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string, the manunfacturer id.
|
||||||
|
"""
|
||||||
|
return self.fru.get_board_mfr_id()
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
"""
|
||||||
|
Retrives the Power Type of PSU
|
||||||
|
|
||||||
|
Returns :
|
||||||
|
A string, PSU power type
|
||||||
|
"""
|
||||||
|
info = self.fru.get_board_product().split(',')
|
||||||
|
if 'AC' in info : return 'AC'
|
||||||
|
if 'DC' in info : return 'DC'
|
||||||
|
return 'Unknown'
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC S5232F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the Thermals' information which are available in the platform
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.thermal_base import ThermalBase
|
||||||
|
from sonic_platform.ipmihelper import IpmiSensor
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Thermal(ThermalBase):
|
||||||
|
"""DellEMC Platform-specific Thermal class"""
|
||||||
|
|
||||||
|
# [ Sensor-Name, Sensor-ID ]
|
||||||
|
SENSOR_MAPPING = [
|
||||||
|
['CPU On-board', 0xe],
|
||||||
|
['ASIC On-board', 0x2],
|
||||||
|
['System Front Left', 0x3],
|
||||||
|
['System Front Middle', 0x1],
|
||||||
|
['System Front Right', 0x4],
|
||||||
|
['Inlet Airflow Sensor', 0x5],
|
||||||
|
['PSU1 Airflow Sensor', 0x7],
|
||||||
|
['PSU2 Airflow Sensor', 0x8]
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, thermal_index=0):
|
||||||
|
ThermalBase.__init__(self)
|
||||||
|
self.index = thermal_index + 1
|
||||||
|
self.sensor = IpmiSensor(self.SENSOR_MAPPING[self.index - 1][1])
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: The name of the thermal
|
||||||
|
"""
|
||||||
|
return self.SENSOR_MAPPING[self.index - 1][0]
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if thermal is present, False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the model number (or part number) of the Thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Model/part number of Thermal
|
||||||
|
"""
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the Thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Serial number of Thermal
|
||||||
|
"""
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean value, True if thermal is operating properly,
|
||||||
|
False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
is_valid, temperature = self.sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
temperature = 0
|
||||||
|
|
||||||
|
return float(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
|
||||||
|
"""
|
||||||
|
is_valid, high_threshold = self.sensor.get_threshold("UpperNonRecoverable")
|
||||||
|
if not is_valid:
|
||||||
|
high_threshold = 0
|
||||||
|
|
||||||
|
return float(high_threshold)
|
||||||
|
|
||||||
|
def get_low_threshold(self):
|
||||||
|
"""
|
||||||
|
Retrieves the low threshold temperature of thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the low threshold temperature of thermal in
|
||||||
|
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)
|
||||||
|
|
||||||
|
def set_high_threshold(self, temperature):
|
||||||
|
"""
|
||||||
|
Sets the high threshold temperature of thermal
|
||||||
|
|
||||||
|
Args :
|
||||||
|
temperature: A float number up to nearest thousandth of one
|
||||||
|
degree Celsius, e.g. 30.125
|
||||||
|
Returns:
|
||||||
|
A boolean, True if threshold is set successfully, False if
|
||||||
|
not
|
||||||
|
"""
|
||||||
|
# Thermal threshold values are pre-defined based on HW.
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_low_threshold(self, temperature):
|
||||||
|
"""
|
||||||
|
Sets the low threshold temperature of thermal
|
||||||
|
|
||||||
|
Args :
|
||||||
|
temperature: A float number up to nearest thousandth of one
|
||||||
|
degree Celsius, e.g. 30.125
|
||||||
|
Returns:
|
||||||
|
A boolean, True if threshold is set successfully, False if
|
||||||
|
not
|
||||||
|
"""
|
||||||
|
# Thermal threshold values are pre-defined based on HW.
|
||||||
|
return False
|
@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
#
|
||||||
|
# DELLEMC S5232f
|
||||||
|
#
|
||||||
|
# Abstract base class for implementing a platform-specific class with
|
||||||
|
# which to interact with a hardware watchdog module in SONiC
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
import subprocess
|
||||||
|
from sonic_platform_base.watchdog_base import WatchdogBase
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class _timespec(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('tv_sec', ctypes.c_long),
|
||||||
|
('tv_nsec', ctypes.c_long)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Watchdog(WatchdogBase):
|
||||||
|
"""
|
||||||
|
Abstract base class for interfacing with a hardware watchdog module
|
||||||
|
"""
|
||||||
|
|
||||||
|
TIMERS = [15,20,30,40,50,60,65,70]
|
||||||
|
|
||||||
|
armed_time = 0
|
||||||
|
timeout = 0
|
||||||
|
CLOCK_MONOTONIC = 1
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._librt = ctypes.CDLL('librt.so.1', use_errno=True)
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
ts = _timespec()
|
||||||
|
if self._clock_gettime(self.CLOCK_MONOTONIC, ctypes.pointer(ts)) != 0:
|
||||||
|
self._errno = ctypes.get_errno()
|
||||||
|
return 0
|
||||||
|
return ts.tv_sec + ts.tv_nsec * 1e-9
|
||||||
|
|
||||||
|
def arm(self, seconds):
|
||||||
|
"""
|
||||||
|
Arm the hardware watchdog with a timeout of <seconds> seconds.
|
||||||
|
If the watchdog is currently armed, calling this function will
|
||||||
|
simply reset the timer to the provided value. If the underlying
|
||||||
|
hardware does not support the value provided in <seconds>, this
|
||||||
|
method should arm the watchdog with the *next greater*
|
||||||
|
available value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An integer specifying the *actual* number of seconds the
|
||||||
|
watchdog was armed with. On failure returns -1.
|
||||||
|
"""
|
||||||
|
timer_offset = -1
|
||||||
|
for key,timer_seconds in enumerate(self.TIMERS):
|
||||||
|
if seconds <= timer_seconds:
|
||||||
|
timer_offset = key
|
||||||
|
seconds = timer_seconds
|
||||||
|
break
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if wd_timer_offset != timer_offset:
|
||||||
|
# Setting 5th to 7th bits
|
||||||
|
# value from timer_offset
|
||||||
|
self.disarm()
|
||||||
|
self._set_reg_val(reg_val | (timer_offset << 4))
|
||||||
|
|
||||||
|
if self.is_armed():
|
||||||
|
# Setting last bit to WD Timer punch
|
||||||
|
# Last bit = WD Timer punch
|
||||||
|
self._set_reg_val(reg_val & 0xFE)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.armed_time = self._get_time()
|
||||||
|
self.timeout = seconds
|
||||||
|
return seconds
|
||||||
|
|
||||||
|
def disarm(self):
|
||||||
|
"""
|
||||||
|
Disarm the hardware watchdog
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if watchdog is disarmed successfully, False
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.armed_time = 0
|
||||||
|
self.timeout = 0
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_armed(self):
|
||||||
|
"""
|
||||||
|
Retrieves the armed state of the hardware watchdog.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if watchdog is armed, False if not
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Extracting 4th bit to get 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)
|
||||||
|
|
||||||
|
def get_remaining_time(self):
|
||||||
|
"""
|
||||||
|
If the watchdog is armed, retrieve the number of seconds
|
||||||
|
remaining on the watchdog timer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An integer specifying the number of seconds remaining on
|
||||||
|
their watchdog timer. If the watchdog is not armed, returns
|
||||||
|
-1.
|
||||||
|
|
||||||
|
S5232 doesnot 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.
|
||||||
|
If this API called from any other process, it would return
|
||||||
|
0. If the watchdog is not armed, this API would return -1.
|
||||||
|
"""
|
||||||
|
if not self.is_armed():
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if self.armed_time > 0 and self.timeout != 0:
|
||||||
|
cur_time = self._get_time()
|
||||||
|
|
||||||
|
if cur_time <= 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
diff_time = int(cur_time - self.armed_time)
|
||||||
|
|
||||||
|
if diff_time > self.timeout:
|
||||||
|
return self.timeout
|
||||||
|
else:
|
||||||
|
return self.timeout - diff_time
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
@ -125,6 +125,33 @@ init_switch_port_led() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_python_api_package() {
|
||||||
|
device="/usr/share/sonic/device"
|
||||||
|
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
|
||||||
|
|
||||||
|
rv=$(pip install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_python_api_package() {
|
||||||
|
rv=$(pip show sonic-platform > /dev/null 2>/dev/null)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
rv=$(pip uninstall -y sonic-platform > /dev/null 2>/dev/null)
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_reboot_cause() {
|
||||||
|
REBOOT_REASON_FILE="/host/reboot-cause/platform/reboot_reason"
|
||||||
|
resource="/sys/bus/pci/devices/0000:04:00.0/resource0"
|
||||||
|
|
||||||
|
# Handle First Boot into software version with reboot cause determination support
|
||||||
|
if [[ ! -e $REBOOT_REASON_FILE ]]; then
|
||||||
|
echo "0" > $REBOOT_REASON_FILE
|
||||||
|
else
|
||||||
|
/usr/bin/pcisysfs.py --get --offset 0x18 --res $resource | sed '1d; s/.*:\(.*\)$/\1/;' > $REBOOT_REASON_FILE
|
||||||
|
fi
|
||||||
|
/usr/bin/pcisysfs.py --set --val 0x0 --offset 0x18 --res $resource
|
||||||
|
}
|
||||||
|
|
||||||
init_devnum
|
init_devnum
|
||||||
|
|
||||||
if [ "$1" == "init" ]; then
|
if [ "$1" == "init" ]; then
|
||||||
@ -135,11 +162,13 @@ if [ "$1" == "init" ]; then
|
|||||||
modprobe i2c_ocores
|
modprobe i2c_ocores
|
||||||
modprobe dell_z9264f_fpga_ocores
|
modprobe dell_z9264f_fpga_ocores
|
||||||
sys_eeprom "new_device"
|
sys_eeprom "new_device"
|
||||||
|
get_reboot_cause
|
||||||
switch_board_qsfp_mux "new_device"
|
switch_board_qsfp_mux "new_device"
|
||||||
switch_board_qsfp "new_device"
|
switch_board_qsfp "new_device"
|
||||||
switch_board_sfp "new_device"
|
switch_board_sfp "new_device"
|
||||||
switch_board_modsel
|
switch_board_modsel
|
||||||
init_switch_port_led
|
init_switch_port_led
|
||||||
|
install_python_api_package
|
||||||
python /usr/bin/qsfp_irq_enable.py
|
python /usr/bin/qsfp_irq_enable.py
|
||||||
|
|
||||||
elif [ "$1" == "deinit" ]; then
|
elif [ "$1" == "deinit" ]; then
|
||||||
@ -147,6 +176,7 @@ elif [ "$1" == "deinit" ]; then
|
|||||||
switch_board_qsfp "delete_device"
|
switch_board_qsfp "delete_device"
|
||||||
switch_board_qsfp_mux "delete_device"
|
switch_board_qsfp_mux "delete_device"
|
||||||
switch_board_sfp "delete_device"
|
switch_board_sfp "delete_device"
|
||||||
|
remove_python_api_package
|
||||||
modprobe -r i2c-mux-pca954x
|
modprobe -r i2c-mux-pca954x
|
||||||
modprobe -r i2c-dev
|
modprobe -r i2c-dev
|
||||||
else
|
else
|
||||||
|
1
platform/broadcom/sonic-platform-modules-dell/z9264f/setup.py
Symbolic link
1
platform/broadcom/sonic-platform-modules-dell/z9264f/setup.py
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../s6100/setup.py
|
@ -0,0 +1,2 @@
|
|||||||
|
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "watchdog"]
|
||||||
|
from sonic_platform import *
|
@ -0,0 +1,315 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# DELLEMC Z9264F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the platform information
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import select
|
||||||
|
from sonic_platform_base.chassis_base import ChassisBase
|
||||||
|
from sonic_platform.sfp import Sfp
|
||||||
|
from sonic_platform.eeprom import Eeprom
|
||||||
|
from sonic_platform.component import Component
|
||||||
|
from sonic_platform.psu import Psu
|
||||||
|
from sonic_platform.watchdog import Watchdog
|
||||||
|
from sonic_platform.fan import Fan
|
||||||
|
from sonic_platform.thermal import Thermal
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
MAX_Z9264F_FANTRAY =4
|
||||||
|
MAX_Z9264F_FAN = 2
|
||||||
|
MAX_Z9264F_COMPONENT = 8 # BIOS,BMC,FPGA,SYSTEM CPLD,4 SLAVE CPLDs
|
||||||
|
MAX_Z9264F_PSU = 2
|
||||||
|
MAX_Z9264F_THERMAL = 8
|
||||||
|
|
||||||
|
|
||||||
|
class Chassis(ChassisBase):
|
||||||
|
"""
|
||||||
|
DELLEMC Platform-specific Chassis class
|
||||||
|
"""
|
||||||
|
|
||||||
|
REBOOT_CAUSE_PATH = "/host/reboot-cause/platform/reboot_reason"
|
||||||
|
OIR_FD_PATH = "/sys/bus/pci/devices/0000:04:00.0/port_msi"
|
||||||
|
|
||||||
|
oir_fd = -1
|
||||||
|
epoll = -1
|
||||||
|
|
||||||
|
_global_port_pres_dict = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
ChassisBase.__init__(self)
|
||||||
|
# sfp.py will read eeprom contents and retrive the eeprom data.
|
||||||
|
# We pass the eeprom path from chassis.py
|
||||||
|
self.PORT_START = 1
|
||||||
|
self.PORT_END = 66
|
||||||
|
PORTS_IN_BLOCK = (self.PORT_END + 1)
|
||||||
|
_sfp_port = range(65, self.PORT_END + 1)
|
||||||
|
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
|
||||||
|
|
||||||
|
for index in range(self.PORT_START, PORTS_IN_BLOCK):
|
||||||
|
port_num = index + 1
|
||||||
|
eeprom_path = eeprom_base.format(port_num)
|
||||||
|
if index not in _sfp_port:
|
||||||
|
sfp_node = Sfp(index, 'QSFP', eeprom_path)
|
||||||
|
else:
|
||||||
|
sfp_node = Sfp(index, 'SFP', eeprom_path)
|
||||||
|
self._sfp_list.append(sfp_node)
|
||||||
|
|
||||||
|
self._eeprom = Eeprom()
|
||||||
|
|
||||||
|
self._watchdog = Watchdog()
|
||||||
|
|
||||||
|
for i in range(MAX_Z9264F_COMPONENT):
|
||||||
|
component = Component(i)
|
||||||
|
self._component_list.append(component)
|
||||||
|
|
||||||
|
for i in range(MAX_Z9264F_PSU):
|
||||||
|
psu = Psu(i)
|
||||||
|
self._psu_list.append(psu)
|
||||||
|
|
||||||
|
for i in range(MAX_Z9264F_FANTRAY):
|
||||||
|
for j in range(MAX_Z9264F_FAN):
|
||||||
|
fan = Fan(i,j)
|
||||||
|
self._fan_list.append(fan)
|
||||||
|
|
||||||
|
for i in range(MAX_Z9264F_THERMAL):
|
||||||
|
thermal = Thermal(i)
|
||||||
|
self._thermal_list.append(thermal)
|
||||||
|
|
||||||
|
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
|
||||||
|
presence = self.get_sfp(port_num).get_presence()
|
||||||
|
if presence:
|
||||||
|
self._global_port_pres_dict[port_num] = '1'
|
||||||
|
else:
|
||||||
|
self._global_port_pres_dict[port_num] = '0'
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.oir_fd != -1:
|
||||||
|
self.epoll.unregister(self.oir_fd.fileno())
|
||||||
|
self.epoll.close()
|
||||||
|
self.oir_fd.close()
|
||||||
|
|
||||||
|
def _get_register(self, reg_file):
|
||||||
|
retval = 'ERR'
|
||||||
|
if (not os.path.isfile(reg_file)):
|
||||||
|
print reg_file, 'not found !'
|
||||||
|
return retval
|
||||||
|
|
||||||
|
try:
|
||||||
|
with os.fdopen(os.open(reg_file, os.O_RDONLY)) as fd:
|
||||||
|
retval = fd.read()
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
retval = retval.rstrip('\r\n')
|
||||||
|
retval = retval.lstrip(" ")
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def _check_interrupts(self, port_dict):
|
||||||
|
retval = 0
|
||||||
|
is_port_dict_updated = False
|
||||||
|
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
|
||||||
|
sfp = self.get_sfp(port_num)
|
||||||
|
presence = sfp.get_presence()
|
||||||
|
if(presence and (self._global_port_pres_dict[port_num] == '0')):
|
||||||
|
is_port_dict_updated = True
|
||||||
|
self._global_port_pres_dict[port_num] = '1'
|
||||||
|
port_dict[port_num] = '1'
|
||||||
|
elif(not presence and (self._global_port_pres_dict[port_num] == '1')):
|
||||||
|
is_port_dict_updated = True
|
||||||
|
self._global_port_pres_dict[port_num] = '0'
|
||||||
|
port_dict[port_num] = '0'
|
||||||
|
return retval, is_port_dict_updated
|
||||||
|
|
||||||
|
def get_change_event(self, timeout=0):
|
||||||
|
"""
|
||||||
|
Returns a nested dictionary containing all devices which have
|
||||||
|
experienced a change at chassis level
|
||||||
|
"""
|
||||||
|
port_dict = {}
|
||||||
|
change_dict = {}
|
||||||
|
change_dict['sfp'] = port_dict
|
||||||
|
try:
|
||||||
|
# We get notified when there is a MSI interrupt (vector 4/5)CVR
|
||||||
|
# Open the sysfs file and register the epoll object
|
||||||
|
self.oir_fd = os.fdopen(os.open(self.OIR_FD_PATH, os.O_RDONLY))
|
||||||
|
if self.oir_fd != -1:
|
||||||
|
# Do a dummy read before epoll register
|
||||||
|
self.oir_fd.read()
|
||||||
|
self.epoll = select.epoll()
|
||||||
|
self.epoll.register(
|
||||||
|
self.oir_fd.fileno(), select.EPOLLIN & select.EPOLLET)
|
||||||
|
else:
|
||||||
|
print("get_transceiver_change_event : unable to create fd")
|
||||||
|
return False, change_dict
|
||||||
|
# Check for missed interrupts by invoking self._check_interrupts
|
||||||
|
# which will update the port_dict.
|
||||||
|
while True:
|
||||||
|
interrupt_count_start = self._get_register(self.OIR_FD_PATH)
|
||||||
|
retval, is_port_dict_updated = self._check_interrupts(port_dict)
|
||||||
|
if ((retval == 0) and (is_port_dict_updated is True)):
|
||||||
|
return True, change_dict
|
||||||
|
interrupt_count_end = self._get_register(self.OIR_FD_PATH)
|
||||||
|
if (interrupt_count_start == 'ERR' or
|
||||||
|
interrupt_count_end == 'ERR'):
|
||||||
|
print("get_transceiver_change_event : \
|
||||||
|
unable to retrive interrupt count")
|
||||||
|
break
|
||||||
|
# check_interrupts() itself may take upto 100s of msecs.
|
||||||
|
# We detect a missed interrupt based on the count
|
||||||
|
if interrupt_count_start == interrupt_count_end:
|
||||||
|
break
|
||||||
|
# Block until an xcvr is inserted or removed with timeout = -1
|
||||||
|
events = self.epoll.poll(
|
||||||
|
timeout=timeout if timeout != 0 else -1)
|
||||||
|
if events:
|
||||||
|
# check interrupts and return the change_dict
|
||||||
|
retval, is_port_dict_updated = \
|
||||||
|
self._check_interrupts(port_dict)
|
||||||
|
if (retval != 0):
|
||||||
|
return False, change_dict
|
||||||
|
return True, change_dict
|
||||||
|
except Exception as e:
|
||||||
|
return False, change_dict
|
||||||
|
finally:
|
||||||
|
if self.oir_fd != -1:
|
||||||
|
self.epoll.unregister(self.oir_fd.fileno())
|
||||||
|
self.epoll.close()
|
||||||
|
self.oir_fd.close()
|
||||||
|
self.oir_fd = -1
|
||||||
|
self.epoll = -1
|
||||||
|
return False, change_dict
|
||||||
|
|
||||||
|
def get_sfp(self, index):
|
||||||
|
"""
|
||||||
|
Retrieves sfp represented by (1-based) index <index>
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index: An integer, the index (1-based) of the sfp to retrieve.
|
||||||
|
The index should be the sequence of a physical port in a chassis,
|
||||||
|
starting from 1.
|
||||||
|
For example, 1 for Ethernet0, 2 for Ethernet4 and so on.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An object dervied from SfpBase representing the specified sfp
|
||||||
|
"""
|
||||||
|
sfp = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The index will start from 1
|
||||||
|
sfp = self._sfp_list[index-1]
|
||||||
|
except IndexError:
|
||||||
|
sys.stderr.write("SFP index {} out of range (1-{})\n".format(
|
||||||
|
index, len(self._sfp_list)))
|
||||||
|
return sfp
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the chassis
|
||||||
|
Returns:
|
||||||
|
string: The name of the chassis
|
||||||
|
"""
|
||||||
|
return self._eeprom.modelstr()
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the chassis
|
||||||
|
Returns:
|
||||||
|
bool: True if chassis is present, False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the model number (or part number) of the chassis
|
||||||
|
Returns:
|
||||||
|
string: Model/part number of chassis
|
||||||
|
"""
|
||||||
|
return self._eeprom.part_number_str()
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the chassis (Service tag)
|
||||||
|
Returns:
|
||||||
|
string: Serial number of chassis
|
||||||
|
"""
|
||||||
|
return self._eeprom.serial_str()
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the chassis
|
||||||
|
Returns:
|
||||||
|
bool: A boolean value, True if chassis is operating properly
|
||||||
|
False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_base_mac(self):
|
||||||
|
"""
|
||||||
|
Retrieves the base MAC address for the chassis
|
||||||
|
Returns:
|
||||||
|
A string containing the MAC address in the format
|
||||||
|
'XX:XX:XX:XX:XX:XX'
|
||||||
|
"""
|
||||||
|
return self._eeprom.base_mac_addr()
|
||||||
|
|
||||||
|
def get_serial_number(self):
|
||||||
|
"""
|
||||||
|
Retrieves the hardware serial number for the chassis
|
||||||
|
Returns:
|
||||||
|
A string containing the hardware serial number for this chassis.
|
||||||
|
"""
|
||||||
|
return self._eeprom.serial_number_str()
|
||||||
|
|
||||||
|
def get_system_eeprom_info(self):
|
||||||
|
"""
|
||||||
|
Retrieves the full content of system EEPROM information for the chassis
|
||||||
|
Returns:
|
||||||
|
A dictionary where keys are the type code defined in
|
||||||
|
OCP ONIE TlvInfo EEPROM format and values are their corresponding
|
||||||
|
values.
|
||||||
|
"""
|
||||||
|
return self._eeprom.system_eeprom_info()
|
||||||
|
|
||||||
|
def get_reboot_cause(self):
|
||||||
|
"""
|
||||||
|
Retrieves the cause of the previous reboot
|
||||||
|
Returns:
|
||||||
|
A tuple (string, string) where the first element is a string
|
||||||
|
containing the cause of the previous reboot. This string must be
|
||||||
|
one of the predefined strings in this class. If the first string
|
||||||
|
is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used
|
||||||
|
to pass a description of the reboot cause.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(self.REBOOT_CAUSE_PATH) as fd:
|
||||||
|
reboot_cause = int(fd.read(), 16)
|
||||||
|
except Exception as e:
|
||||||
|
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||||
|
|
||||||
|
if reboot_cause & 0x1:
|
||||||
|
return (self.REBOOT_CAUSE_POWER_LOSS, None)
|
||||||
|
elif reboot_cause & 0x2:
|
||||||
|
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||||
|
elif reboot_cause & 0x4:
|
||||||
|
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "PSU Shutdown")
|
||||||
|
elif reboot_cause & 0x8:
|
||||||
|
return (self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU, None)
|
||||||
|
elif reboot_cause & 0x10:
|
||||||
|
return (self.REBOOT_CAUSE_WATCHDOG, None)
|
||||||
|
elif reboot_cause & 0x20:
|
||||||
|
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "BMC Shutdown")
|
||||||
|
elif reboot_cause & 0x40:
|
||||||
|
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Hot-Swap Shutdown")
|
||||||
|
elif reboot_cause & 0x80:
|
||||||
|
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Reset Button Shutdown")
|
||||||
|
elif reboot_cause & 0x100:
|
||||||
|
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Reset Button Cold Reboot")
|
||||||
|
else:
|
||||||
|
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
|
@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DELLEMC Z9264F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the Components' (e.g., BIOS, CPLD, FPGA, BMC etc.) available in
|
||||||
|
# the platform
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import re
|
||||||
|
from sonic_platform_base.component_base import ComponentBase
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
FIRMWARE_VERSION_FILE="/var/log/firmware_versions"
|
||||||
|
|
||||||
|
class Component(ComponentBase):
|
||||||
|
"""DellEMC Platform-specific Component class"""
|
||||||
|
|
||||||
|
CHASSIS_COMPONENTS = [
|
||||||
|
["BIOS", ("Performs initialization of hardware components during "
|
||||||
|
"booting")],
|
||||||
|
["FPGA", ("Used for managing the system LEDs")],
|
||||||
|
["BMC", ("Platform management controller for on-board temperature "
|
||||||
|
"monitoring, in-chassis power, Fan and LED control")],
|
||||||
|
["System CPLD", ("Used for managing the CPU power sequence and CPU states")],
|
||||||
|
["Slave CPLD 1", ("Used for managing QSFP/QSFP28 port transceivers (1-16)")],
|
||||||
|
["Slave CPLD 2", ("Used for managing QSFP/QSFP28 port transceivers (17-32)")],
|
||||||
|
["Slave CPLD 3", ("Used for managing QSFP/QSFP28 port transceivers (33-48)")],
|
||||||
|
["Slave CPLD 4", ("Used for managing QSFP/QSFP28 port transceivers (49-64) and SFP/SFP28 "
|
||||||
|
"port transceivers (65 and 66)")],
|
||||||
|
]
|
||||||
|
def __init__(self, component_index=0):
|
||||||
|
self.index = component_index
|
||||||
|
self.name = self.CHASSIS_COMPONENTS[self.index][0]
|
||||||
|
self.description = self.CHASSIS_COMPONENTS[self.index][1]
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the component
|
||||||
|
Returns:
|
||||||
|
A string containing the name of the component
|
||||||
|
"""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
"""
|
||||||
|
Retrieves the description of the component
|
||||||
|
Returns:
|
||||||
|
A string containing the description of the component
|
||||||
|
"""
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
def get_firmware_version(self):
|
||||||
|
"""
|
||||||
|
Retrieves the firmware version of the component
|
||||||
|
Returns:
|
||||||
|
A string containing the firmware version of the component
|
||||||
|
"""
|
||||||
|
rv = ""
|
||||||
|
try:
|
||||||
|
fd = open(FIRMWARE_VERSION_FILE,"r")
|
||||||
|
except IOError:
|
||||||
|
return rv
|
||||||
|
version_contents = fd.read()
|
||||||
|
fd.close()
|
||||||
|
if not version_contents:
|
||||||
|
return rv
|
||||||
|
if self.index < 8:
|
||||||
|
version = re.search(r''+self.CHASSIS_COMPONENTS[self.index][0]+':(.*)',version_contents)
|
||||||
|
if version:
|
||||||
|
rv = version.group(1).strip()
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def install_firmware(self, image_path):
|
||||||
|
"""
|
||||||
|
Installs firmware to the component
|
||||||
|
Args:
|
||||||
|
image_path: A string, path to firmware image
|
||||||
|
Returns:
|
||||||
|
A boolean, True if install was successful, False if not
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# DellEmc Z9264F
|
||||||
|
#
|
||||||
|
# Platform and model specific eeprom subclass, inherits from the base class,
|
||||||
|
# and provides the followings:
|
||||||
|
# - the eeprom format definition
|
||||||
|
# - specific encoder/decoder if there is special need
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import os.path
|
||||||
|
from sonic_eeprom import eeprom_tlvinfo
|
||||||
|
import binascii
|
||||||
|
except ImportError, e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.eeprom_path = None
|
||||||
|
for b in (0, 1):
|
||||||
|
f = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'.format(b)
|
||||||
|
if os.path.exists(f):
|
||||||
|
self.eeprom_path = f
|
||||||
|
break
|
||||||
|
if self.eeprom_path is None:
|
||||||
|
return
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
Returns the serial number
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_SERIAL_NUMBER)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def base_mac_addr(self):
|
||||||
|
"""
|
||||||
|
Returns the base mac address found in the system EEPROM
|
||||||
|
"""
|
||||||
|
(is_valid, t) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_MAC_BASE)
|
||||||
|
if not is_valid or t[1] != 6:
|
||||||
|
return super(TlvInfoDecoder, self).switchaddrstr(e)
|
||||||
|
|
||||||
|
return ":".join([binascii.b2a_hex(T) for T in t[2]])
|
||||||
|
|
||||||
|
def modelstr(self):
|
||||||
|
"""
|
||||||
|
Returns the Model name
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_PRODUCT_NAME)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def part_number_str(self):
|
||||||
|
"""
|
||||||
|
Returns the part number
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_PART_NUMBER)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def serial_str(self):
|
||||||
|
"""
|
||||||
|
Returns the servicetag number
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_SERVICE_TAG)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
return results[2]
|
||||||
|
|
||||||
|
def revision_str(self):
|
||||||
|
"""
|
||||||
|
Returns the device revision
|
||||||
|
"""
|
||||||
|
(is_valid, results) = self.get_tlv_field(
|
||||||
|
self.eeprom_data, self._TLV_CODE_DEVICE_VERSION)
|
||||||
|
if not is_valid:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
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
|
@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC Z9264F
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the Fans' information which are available in the platform.
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.fan_base import FanBase
|
||||||
|
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
#Derived the offset from BMC management information
|
||||||
|
FAN1_MAX_SPEED_OFFSET = 71
|
||||||
|
FAN2_MAX_SPEED_OFFSET = 73
|
||||||
|
PSU_FAN_MAX_SPEED_OFFSET = 50
|
||||||
|
FAN_DIRECTION_OFFSET = 69
|
||||||
|
PSU_FAN_DIRECTION_OFFSET = 47
|
||||||
|
|
||||||
|
|
||||||
|
class Fan(FanBase):
|
||||||
|
"""DellEMC Platform-specific Fan class"""
|
||||||
|
#Derived the sensor IDs from BMC
|
||||||
|
# { FAN-ID: { Sensor-Name: Sensor-ID } }
|
||||||
|
FAN_SENSOR_MAPPING = { 1: {"Prsnt": 0x51, "State": 0x64, "Speed": 0x24},
|
||||||
|
2: {"Prsnt": 0x51, "State": 0x60, "Speed": 0x20},
|
||||||
|
3: {"Prsnt": 0x52, "State": 0x65, "Speed": 0x25},
|
||||||
|
4: {"Prsnt": 0x52, "State": 0x61, "Speed": 0x21},
|
||||||
|
5: {"Prsnt": 0x53, "State": 0x66, "Speed": 0x26},
|
||||||
|
6: {"Prsnt": 0x53, "State": 0x62, "Speed": 0x22},
|
||||||
|
7: {"Prsnt": 0x54, "State": 0x67, "Speed": 0x27},
|
||||||
|
8: {"Prsnt": 0x54, "State": 0x63, "Speed": 0x23} }
|
||||||
|
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x46, "Speed": 0x2e},
|
||||||
|
2: {"State": 0x47, "Speed": 0x2f} }
|
||||||
|
|
||||||
|
# { FANTRAY-ID: FRU-ID }
|
||||||
|
FAN_FRU_MAPPING = { 1: 1, 2: 2, 3: 3, 4: 4 }
|
||||||
|
PSU_FRU_MAPPING = { 1: 6, 2: 7 }
|
||||||
|
|
||||||
|
def __init__(self, fantray_index=1, fan_index=1, psu_fan=False,
|
||||||
|
dependency=None):
|
||||||
|
self.is_psu_fan = psu_fan
|
||||||
|
if not self.is_psu_fan:
|
||||||
|
# API index is starting from 0, DellEMC platform index is
|
||||||
|
# starting from 1
|
||||||
|
self.fantrayindex = fantray_index + 1
|
||||||
|
self.fanindex = fan_index + 1
|
||||||
|
if (self.fanindex == 1):
|
||||||
|
self.max_speed_offset = FAN1_MAX_SPEED_OFFSET
|
||||||
|
else:
|
||||||
|
self.max_speed_offset = FAN2_MAX_SPEED_OFFSET
|
||||||
|
self.fan_direction_offset = FAN_DIRECTION_OFFSET
|
||||||
|
self.index = (self.fantrayindex - 1) * 2 + self.fanindex
|
||||||
|
self.prsnt_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Prsnt"],
|
||||||
|
is_discrete=True)
|
||||||
|
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])
|
||||||
|
else:
|
||||||
|
self.dependency = dependency
|
||||||
|
self.fanindex = fan_index
|
||||||
|
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.max_speed_offset = PSU_FAN_MAX_SPEED_OFFSET
|
||||||
|
self.fan_direction_offset = PSU_FAN_DIRECTION_OFFSET
|
||||||
|
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
|
||||||
|
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the device
|
||||||
|
Returns:
|
||||||
|
String: The name of the device
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return "PSU{} Fan".format(self.fanindex)
|
||||||
|
else:
|
||||||
|
return "FanTray{}-Fan{}".format(self.fantrayindex, self.fanindex)
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the part number of the FAN
|
||||||
|
Returns:
|
||||||
|
String: Part number of FAN
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return 'NA'
|
||||||
|
else:
|
||||||
|
return self.fru.get_board_part_number()
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the FAN
|
||||||
|
Returns:
|
||||||
|
String: Serial number of FAN
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return 'NA'
|
||||||
|
else:
|
||||||
|
return self.fru.get_board_serial()
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the FAN
|
||||||
|
Returns:
|
||||||
|
bool: True if fan is present, False if not
|
||||||
|
"""
|
||||||
|
presence = False
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return self.dependency.get_presence()
|
||||||
|
else:
|
||||||
|
is_valid, state = self.prsnt_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state & 0b1):
|
||||||
|
presence = True
|
||||||
|
return presence
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the FAN
|
||||||
|
Returns:
|
||||||
|
bool: True if FAN is operating properly, False if not
|
||||||
|
"""
|
||||||
|
status = False
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state == 0x00):
|
||||||
|
status = True
|
||||||
|
return status
|
||||||
|
|
||||||
|
def get_direction(self):
|
||||||
|
"""
|
||||||
|
Retrieves the fan airfow direction
|
||||||
|
Returns:
|
||||||
|
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
|
||||||
|
depending on fan direction
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
In DellEMC platforms,
|
||||||
|
- Forward/Exhaust : Air flows from Port side to Fan side.
|
||||||
|
- Reverse/Intake : Air flows from Fan side to Port side.
|
||||||
|
"""
|
||||||
|
direction = [self.FAN_DIRECTION_EXHAUST, self.FAN_DIRECTION_INTAKE]
|
||||||
|
fan_status = self.get_status()
|
||||||
|
if not fan_status:
|
||||||
|
return 'NA'
|
||||||
|
is_valid, fan_direction = self.fru.get_fru_data(self.fan_direction_offset)
|
||||||
|
if is_valid:
|
||||||
|
return direction[fan_direction[0]]
|
||||||
|
else:
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_speed(self):
|
||||||
|
"""
|
||||||
|
Retrieves the speed of the fan
|
||||||
|
Returns:
|
||||||
|
int: percentage of the max fan speed
|
||||||
|
"""
|
||||||
|
if self.max_speed == 0:
|
||||||
|
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
|
||||||
|
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
|
||||||
|
is_valid, fan_speed = self.speed_sensor.get_reading()
|
||||||
|
if not is_valid or self.max_speed == 0:
|
||||||
|
speed = 0
|
||||||
|
else:
|
||||||
|
speed = (100 * fan_speed)/self.max_speed
|
||||||
|
return speed
|
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the platform information
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.platform_base import PlatformBase
|
||||||
|
from sonic_platform.chassis import Chassis
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Platform(PlatformBase):
|
||||||
|
"""
|
||||||
|
DELLEMC Platform-specific class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
PlatformBase.__init__(self)
|
||||||
|
self._chassis = Chassis()
|
@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC Z9264
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the PSUs' information which are available in the platform
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.psu_base import PsuBase
|
||||||
|
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
|
||||||
|
from sonic_platform.fan import Fan
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Psu(PsuBase):
|
||||||
|
"""DellEMC Platform-specific PSU class"""
|
||||||
|
|
||||||
|
# { PSU-ID: { Sensor-Name: Sensor-ID } }
|
||||||
|
SENSOR_MAPPING = { 1: { "State": 0x31, "Current": 0x39,
|
||||||
|
"Power": 0x37, "Voltage": 0x38 },
|
||||||
|
2: { "State": 0x32, "Current": 0x3F,
|
||||||
|
"Power": 0x3D, "Voltage": 0x3E } }
|
||||||
|
# ( PSU-ID: FRU-ID }
|
||||||
|
FRU_MAPPING = { 1: 6, 2: 7 }
|
||||||
|
|
||||||
|
def __init__(self, psu_index):
|
||||||
|
PsuBase.__init__(self)
|
||||||
|
# PSU is 1-based in DellEMC platforms
|
||||||
|
self.index = psu_index + 1
|
||||||
|
self.state_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["State"],
|
||||||
|
is_discrete=True)
|
||||||
|
self.voltage_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Voltage"])
|
||||||
|
self.current_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Current"])
|
||||||
|
self.power_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Power"])
|
||||||
|
self.fru = IpmiFru(self.FRU_MAPPING[self.index])
|
||||||
|
|
||||||
|
self._fan_list.append(Fan(fantray_index=1,fan_index=self.index, psu_fan=True,
|
||||||
|
dependency=self))
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the device
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: The name of the device
|
||||||
|
"""
|
||||||
|
return "PSU{}".format(self.index)
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the Power Supply Unit (PSU)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if PSU is present, False if not
|
||||||
|
"""
|
||||||
|
presence = False
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state & 0b1):
|
||||||
|
presence = True
|
||||||
|
|
||||||
|
return presence
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the part number of the PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Part number of PSU
|
||||||
|
"""
|
||||||
|
return self.fru.get_board_part_number()
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Serial number of PSU
|
||||||
|
"""
|
||||||
|
return self.fru.get_board_serial()
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if PSU is operating properly, False if not
|
||||||
|
"""
|
||||||
|
status = False
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state == 0x01):
|
||||||
|
status = True
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
def get_voltage(self):
|
||||||
|
"""
|
||||||
|
Retrieves current PSU voltage output
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the output voltage in volts,
|
||||||
|
e.g. 12.1
|
||||||
|
"""
|
||||||
|
is_valid, voltage = self.voltage_sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
voltage = 0
|
||||||
|
|
||||||
|
return float(voltage)
|
||||||
|
|
||||||
|
def get_current(self):
|
||||||
|
"""
|
||||||
|
Retrieves present electric current supplied by PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, electric current in amperes,
|
||||||
|
e.g. 15.4
|
||||||
|
"""
|
||||||
|
is_valid, current = self.current_sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
current = 0
|
||||||
|
|
||||||
|
return float(current)
|
||||||
|
|
||||||
|
def get_power(self):
|
||||||
|
"""
|
||||||
|
Retrieves current energy supplied by PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the power in watts,
|
||||||
|
e.g. 302.6
|
||||||
|
"""
|
||||||
|
is_valid, power = self.power_sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
power = 0
|
||||||
|
|
||||||
|
return float(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
|
||||||
|
is_valid, state = self.state_sensor.get_reading()
|
||||||
|
if is_valid:
|
||||||
|
if (state == 0x01):
|
||||||
|
status = True
|
||||||
|
|
||||||
|
return status
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# DellEMC Z9264
|
||||||
|
#
|
||||||
|
# Module contains an implementation of SONiC Platform Base API and
|
||||||
|
# provides the Thermals' information which are available in the platform
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sonic_platform_base.thermal_base import ThermalBase
|
||||||
|
from sonic_platform.ipmihelper import IpmiSensor
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class Thermal(ThermalBase):
|
||||||
|
"""DellEMC Platform-specific Thermal class"""
|
||||||
|
|
||||||
|
# [ Sensor-Name, Sensor-ID ]
|
||||||
|
SENSOR_MAPPING = [
|
||||||
|
['CPU On-board', 0x6],
|
||||||
|
['ASIC On-board', 0x8],
|
||||||
|
['System Front Left', 0x3],
|
||||||
|
['System Front Middle', 0x7],
|
||||||
|
['System Front Right', 0x4],
|
||||||
|
['Inlet Airflow Sensor', 0x5],
|
||||||
|
['PSU1 Airflow Sensor', 0x2],
|
||||||
|
['PSU2 Airflow Sensor', 0x1]
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, thermal_index):
|
||||||
|
ThermalBase.__init__(self)
|
||||||
|
self.index = thermal_index + 1
|
||||||
|
self.sensor = IpmiSensor(self.SENSOR_MAPPING[self.index - 1][1])
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
Retrieves the name of the thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: The name of the thermal
|
||||||
|
"""
|
||||||
|
return self.SENSOR_MAPPING[self.index - 1][0]
|
||||||
|
|
||||||
|
def get_presence(self):
|
||||||
|
"""
|
||||||
|
Retrieves the presence of the thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if thermal is present, False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the model number (or part number) of the Thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Model/part number of Thermal
|
||||||
|
"""
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the Thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Serial number of Thermal
|
||||||
|
"""
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean value, True if thermal is operating properly,
|
||||||
|
False if not
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
is_valid, temperature = self.sensor.get_reading()
|
||||||
|
if not is_valid:
|
||||||
|
temperature = 0
|
||||||
|
|
||||||
|
return float(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
|
||||||
|
"""
|
||||||
|
is_valid, high_threshold = self.sensor.get_threshold("UpperNonRecoverable")
|
||||||
|
if not is_valid:
|
||||||
|
high_threshold = 0
|
||||||
|
|
||||||
|
return float(high_threshold)
|
||||||
|
|
||||||
|
def get_low_threshold(self):
|
||||||
|
"""
|
||||||
|
Retrieves the low threshold temperature of thermal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the low threshold temperature of thermal in
|
||||||
|
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)
|
||||||
|
|
||||||
|
def set_high_threshold(self, temperature):
|
||||||
|
"""
|
||||||
|
Sets the high threshold temperature of thermal
|
||||||
|
|
||||||
|
Args :
|
||||||
|
temperature: A float number up to nearest thousandth of one
|
||||||
|
degree Celsius, e.g. 30.125
|
||||||
|
Returns:
|
||||||
|
A boolean, True if threshold is set successfully, False if
|
||||||
|
not
|
||||||
|
"""
|
||||||
|
# Thermal threshold values are pre-defined based on HW.
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_low_threshold(self, temperature):
|
||||||
|
"""
|
||||||
|
Sets the low threshold temperature of thermal
|
||||||
|
|
||||||
|
Args :
|
||||||
|
temperature: A float number up to nearest thousandth of one
|
||||||
|
degree Celsius, e.g. 30.125
|
||||||
|
Returns:
|
||||||
|
A boolean, True if threshold is set successfully, False if
|
||||||
|
not
|
||||||
|
"""
|
||||||
|
# Thermal threshold values are pre-defined based on HW.
|
||||||
|
return False
|
@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
#
|
||||||
|
# DELLEMC Z9264f
|
||||||
|
#
|
||||||
|
# Abstract base class for implementing a platform-specific class with
|
||||||
|
# which to interact with a hardware watchdog module in SONiC
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
import subprocess
|
||||||
|
from sonic_platform_base.watchdog_base import WatchdogBase
|
||||||
|
except ImportError as e:
|
||||||
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
class _timespec(ctypes.Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('tv_sec', ctypes.c_long),
|
||||||
|
('tv_nsec', ctypes.c_long)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Watchdog(WatchdogBase):
|
||||||
|
"""
|
||||||
|
Abstract base class for interfacing with a hardware watchdog module
|
||||||
|
"""
|
||||||
|
|
||||||
|
TIMERS = [15,20,30,40,50,60,65,70]
|
||||||
|
|
||||||
|
armed_time = 0
|
||||||
|
timeout = 0
|
||||||
|
CLOCK_MONOTONIC = 1
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._librt = ctypes.CDLL('librt.so.1', use_errno=True)
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
ts = _timespec()
|
||||||
|
if self._clock_gettime(self.CLOCK_MONOTONIC, ctypes.pointer(ts)) != 0:
|
||||||
|
self._errno = ctypes.get_errno()
|
||||||
|
return 0
|
||||||
|
return ts.tv_sec + ts.tv_nsec * 1e-9
|
||||||
|
|
||||||
|
def arm(self, seconds):
|
||||||
|
"""
|
||||||
|
Arm the hardware watchdog with a timeout of <seconds> seconds.
|
||||||
|
If the watchdog is currently armed, calling this function will
|
||||||
|
simply reset the timer to the provided value. If the underlying
|
||||||
|
hardware does not support the value provided in <seconds>, this
|
||||||
|
method should arm the watchdog with the *next greater*
|
||||||
|
available value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An integer specifying the *actual* number of seconds the
|
||||||
|
watchdog was armed with. On failure returns -1.
|
||||||
|
"""
|
||||||
|
timer_offset = -1
|
||||||
|
for key,timer_seconds in enumerate(self.TIMERS):
|
||||||
|
if seconds <= timer_seconds:
|
||||||
|
timer_offset = key
|
||||||
|
seconds = timer_seconds
|
||||||
|
break
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if wd_timer_offset != timer_offset:
|
||||||
|
# Setting 5th to 7th bits
|
||||||
|
# value from timer_offset
|
||||||
|
self.disarm()
|
||||||
|
self._set_reg_val((reg_val & 0x87) | (timer_offset << 4))
|
||||||
|
|
||||||
|
if self.is_armed():
|
||||||
|
# Setting last bit to WD Timer punch
|
||||||
|
# Last bit = WD Timer punch
|
||||||
|
self._set_reg_val(reg_val & 0xFE)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.armed_time = self._get_time()
|
||||||
|
self.timeout = seconds
|
||||||
|
return seconds
|
||||||
|
|
||||||
|
def disarm(self):
|
||||||
|
"""
|
||||||
|
Disarm the hardware watchdog
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if watchdog is disarmed successfully, False
|
||||||
|
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)
|
||||||
|
|
||||||
|
self.armed_time = 0
|
||||||
|
self.timeout = 0
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_armed(self):
|
||||||
|
"""
|
||||||
|
Retrieves the armed state of the hardware watchdog.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if watchdog is armed, False if not
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Extracting 4th bit to get 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)
|
||||||
|
|
||||||
|
def get_remaining_time(self):
|
||||||
|
"""
|
||||||
|
If the watchdog is armed, retrieve the number of seconds
|
||||||
|
remaining on the watchdog timer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An integer specifying the number of seconds remaining on
|
||||||
|
their watchdog timer. If the watchdog is not armed, returns
|
||||||
|
-1.
|
||||||
|
|
||||||
|
Z9264 doesnot 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.
|
||||||
|
If this API called from any other process, it would return
|
||||||
|
0. If the watchdog is not armed, this API would return -1.
|
||||||
|
"""
|
||||||
|
if not self.is_armed():
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if self.armed_time > 0 and self.timeout != 0:
|
||||||
|
cur_time = self._get_time()
|
||||||
|
|
||||||
|
if cur_time <= 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
diff_time = int(cur_time - self.armed_time)
|
||||||
|
|
||||||
|
if diff_time > self.timeout:
|
||||||
|
return self.timeout
|
||||||
|
else:
|
||||||
|
return self.timeout - diff_time
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user