[DellEMC Z9332f] Platform API 2.0 Support and bug fixing (#5958)

- Add platform infra to support 2.0 API
- Bug fixing for 9332 known issues
This commit is contained in:
Srideep 2020-12-10 11:30:44 -07:00 committed by GitHub
parent 6d9ecbcfd8
commit 3c9a7ec623
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2630 additions and 33 deletions

View File

@ -23,6 +23,29 @@ Cmd_GetSensorReading = 0x2D
# IPMI FRU Device Commands
Cmd_ReadFRUData = 0x11
def get_ipmitool_raw_output(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:
pass
for i in result.split():
result_bytes.append(int(i, 16))
return result_bytes
class IpmiSensor(object):
# Sensor Threshold types and their respective bit masks
@ -39,29 +62,6 @@ class IpmiSensor(object):
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.decode('utf-8').rstrip('\n')
except:
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
@ -72,7 +72,7 @@ class IpmiSensor(object):
cmd_args = "{} {} {} {}".format(NetFn_SensorEvent,
Cmd_GetSensorReadingFactors,
self.id, raw_value)
factors = self._get_ipmitool_raw_output(cmd_args)
factors = get_ipmitool_raw_output(cmd_args)
if len(factors) != 7:
return False, 0
@ -107,7 +107,7 @@ class IpmiSensor(object):
# Get Sensor Reading
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorReading,
self.id)
output = self._get_ipmitool_raw_output(cmd_args)
output = get_ipmitool_raw_output(cmd_args)
if len(output) != 4:
return False, 0
@ -154,7 +154,7 @@ class IpmiSensor(object):
# Get Sensor Threshold
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorThreshold,
self.id)
thresholds = self._get_ipmitool_raw_output(cmd_args)
thresholds = get_ipmitool_raw_output(cmd_args)
if len(thresholds) != 7:
return False, 0

View File

@ -3,5 +3,9 @@ z9332f/scripts/platform_sensors.py usr/local/bin
z9332f/scripts/sensors usr/bin
z9332f/cfg/z9332f-modules.conf etc/modules-load.d
z9332f/systemd/platform-modules-z9332f.service etc/systemd/system
z9332f/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-dellemc_z9332f_d1508-r0
z9332f/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-dellemc_z9332f_d1508-r0
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_z9332f_d1508-r0
common/pcisysfs.py usr/bin
common/fw-updater usr/local/bin
common/onie_mode_set usr/local/bin

View File

@ -45,6 +45,12 @@ override_dh_auto_build:
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
elif [ $$mod = "z9332f" ]; 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; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
fi; \
echo "making man page alias $$mod -> $$mod APIs";\
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
@ -97,6 +103,11 @@ override_dh_clean:
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 = "z9332f" ]; 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; \
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \
done); \

View File

@ -75,7 +75,7 @@ class Chassis(ChassisBase):
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-1).get_presence()
presence = self.get_sfp(port_num).get_presence()
self._global_port_pres_dict[port_num] = '1' if presence else '0'
def __del__(self):
@ -99,7 +99,7 @@ class Chassis(ChassisBase):
while True:
time.sleep(0.5)
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
presence = self.get_sfp(port_num-1).get_presence()
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'
@ -134,10 +134,10 @@ class Chassis(ChassisBase):
try:
# The index will start from 0
sfp = self._sfp_list[index]
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))
index, len(self._sfp_list)))
return sfp
def get_name(self):

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5232F
# DellEMC Z9332F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fan-Drawers' information available in the platform.
@ -14,7 +14,7 @@ try:
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
S5232F_FANS_PER_FANTRAY = 2
Z9332F_FANS_PER_FANTRAY = 2
class FanDrawer(FanDrawerBase):
@ -25,7 +25,7 @@ class FanDrawer(FanDrawerBase):
FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms
self.fantrayindex = fantray_index + 1
for i in range(S5232F_FANS_PER_FANTRAY):
for i in range(Z9332F_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i))
def get_name(self):

View File

@ -145,6 +145,29 @@ platform_firmware_versions() {
ver=`/usr/sbin/i2cget -y 4 0x31 0x0`
echo "Switch CPLD 2: $((ver))" >> $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)
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-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
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -eq 0 ]; then
rv=$(pip3 uninstall -y sonic-platform > /dev/null 2>/dev/null)
fi
}
init_devnum
if [ "$1" == "init" ]; then
@ -160,6 +183,7 @@ if [ "$1" == "init" ]; then
switch_board_qsfp "new_device"
switch_board_sfp "new_device"
switch_board_led_default
install_python_api_package
# /usr/bin/qsfp_irq_enable.py
platform_firmware_versions
@ -174,6 +198,7 @@ elif [ "$1" == "deinit" ]; then
modprobe -r cls-i2c-ocore
modprobe -r cls-switchboard
modprobe -r mc24lc64t
remove_python_api_package
else
echo "z9332f_platform : Invalid option !"
fi

View File

@ -0,0 +1 @@
../s6100/setup.py

View File

@ -0,0 +1 @@
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "fan_drawer"]

View File

@ -0,0 +1,319 @@
#!/usr/bin/env python
#############################################################################
# DELLEMC Z9332F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
import time
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.fan_drawer import FanDrawer
from sonic_platform.watchdog import Watchdog
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
MAX_Z9332F_FANTRAY = 7
MAX_Z9332F_FAN = 2
MAX_Z9332F_PSU = 2
MAX_Z9332F_THERMAL = 14
MAX_Z9332F_COMPONENT = 6 # BIOS,FPGA,BMC,BB CPLD and 2 Switch CPLDs
media_part_num_list = set([ \
"8T47V","XTY28","MHVPK","GF76J","J6FGD","F1KMV","9DN5J","H4DHD","6MCNV","0WRX0","X7F70","5R2PT","WTRD1","WTRD1","WTRD1","WTRD1","5250G","WTRD1","C5RNH","C5RNH","FTLX8571D3BCL-FC",
"C5RNH","5250G","N8TDR","7D64H","7D64H","RN84N","RN84N","HMTNW","6K3Y6","6K3Y6","TY5FM","50M0R","PGYJT","WP2PP","85Y13","1HCGH","FP9R1","FYD0M","C6Y7M","C6Y7M","V250M","V250M",
"5CWK6","5CWK6","53HVN","53HVN","358VV","358VV","MV799","MV799","YJF03","P9GND","T1KCN","1DXKP","MT7R2","K0T7R","W5G04","7TCDN","7TCDN","7TCDN","7TCDN","7TCDN","V3XJK","0MV31",
"5FVP7","N6KM9","C41MF","77KC3","XW7J0","V4NJV","2XJHY","H93DH","H93DH","F8CG0","F8CG0","F8CG0","119N6","WFMF5","794RX","288F6","1M31V","1M31V","5NP8R","5NP8R","4TC09","4TC09",
"FC6KV","FC6KV","J90VN","J90VN","05RH0","05RH0","YDN52","0C2YV","YDN52","0C2YV","9JT65","D7M6H","6GW14","FYVFW","0VF5H","P4YPY","P4YPY","TCPM2","TCPM2","JNPF8","JNPF8","27GG5",
"27GG5","P8T4W","P8T4W","JR54Y","M6N0J","XJYD0","K44H9","035KG","P7C7N","76V43","3CC35","FN4FC","26FN3","YFNDD","YFNDD","7R9N9","035KG","P7C7N","76V43","3CC35","PLRXPLSCS43811",
"FN4FC","26FN3","YFNDD","YFNDD","7R9N9","G86YJ","V407F","V407F","9KH6T","G86YJ","V407F","9KH6T","2JVDD","D0R73","VXFJY","9X8JP","2JVDD","D0R73","VXFJY","9X8JP","2JVDD","D0R73","VXFJY",
"9X8JP","GMFC5","GMFC5","GMFC5","D7P80","3MFXG","3MFXG","0GWXJ","THPF3","THPF3","THPF3","THPF3","THPF3","PJ62G","3XCX1","JJYKG","RRRTK","16K56","86JM2","K5R6C","7MG2C","WTPPN","9HTT2",
"NKM4F","VXGGG","JC9W6","6MR8M","RP3GV","M5PPJ","XKY55","TKCXT","05J8P","5WGKD","XFDRT","NW8DM","YPKH3","5WGKD","XFDRT","NW8DM","YPKH3","71XXK","MVCX6","0XYP6","HPPVW","3GHRT","71XXK",
"MVCX6","0XYP6","HPPVW","3GHRT","2X5T6","135V2","KD5MV","2X5T6","KD5MV","HHFK0","3YWG7","5CMT2","RCVP5","X5DH4","HHFK0","3YWG7","5CMT2","RCVP5","X5DH4","3YWG7","5CMT2","RCVP5","X5DH4",
"4WJ41","4WJ41","14NV5","14NV5","14NV5","4WGYD","YKMH7","X7CCC","X7CCC","0X9CT","0CY8V","P7D7R","W4GPP","W4GPP","W4GPP","HHHCHC","07RN7","07RN7","0YR96","0YR96","JCYM9","FTLX8571D3BCL",
"DDW0X","VPFDJ","229KM","9FC7D","DDW0X","VPFDJ","6FMR5","J7K20","N3K9W","6FMR5","8R4VM","7VN5T","D9YM8","8R4VM","VYXPW","87TPX","WY6FK","VYXPW","87TPX","WY6FK","WG8C4","N8K82","2DV6Y",
"77C3C","RC0HM","77C3C","RC0HM","JHXTN","3P3PG","92YVM","4VX5M","4VX5M","6RRGD","W4JWV","22V6R","XR11M","9GMDY","JMCWK","TP2F0","6MGDY","78RHK", "C0TP5","0WDNV","FCLF8522P2BTL"\
])
class Chassis(ChassisBase):
"""
DELLEMC Platform-specific Chassis class
"""
REBOOT_CAUSE_PATH = "/host/reboot-cause/platform/reboot_reason"
oir_fd = -1
epoll = -1
_global_port_pres_dict = {}
_port_to_i2c_mapping = {
1: 10,
2: 11,
3: 12,
4: 13,
5: 14,
6: 15,
7: 16,
8: 17,
9: 18,
10: 19,
11: 20,
12: 21,
13: 22,
14: 23,
15: 24,
16: 25,
17: 26,
18: 27,
19: 28,
20: 29,
21: 30,
22: 31,
23: 32,
24: 33,
25: 34,
26: 35,
27: 36,
28: 37,
29: 38,
30: 39,
31: 40,
32: 41,
33: 1,
34: 2,
}
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.PORTS_IN_BLOCK)
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
for index in range(self.PORT_START, self.PORTS_IN_BLOCK):
eeprom_path = eeprom_base.format(self._port_to_i2c_mapping[index])
port_type = 'SFP' if index in _sfp_port else 'QSFP'
sfp_node = Sfp(index, port_type, eeprom_path)
self._sfp_list.append(sfp_node)
self._eeprom = Eeprom()
self._watchdog = Watchdog()
for i in range(MAX_Z9332F_FANTRAY):
fandrawer = FanDrawer(i)
self._fan_drawer_list.append(fandrawer)
self._fan_list.extend(fandrawer._fan_list)
self._num_sfps = self.PORT_END
self._num_fans = MAX_Z9332F_FANTRAY * MAX_Z9332F_FAN
self._psu_list = [Psu(i) for i in range(MAX_Z9332F_PSU)]
self._thermal_list = [Thermal(i) for i in range(MAX_Z9332F_THERMAL)]
self._component_list = [Component(i) for i in range(MAX_Z9332F_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'
self._watchdog = Watchdog()
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):
"""
Returns a nested dictionary containing all devices which have
experienced a change at chassis level
"""
start_ms = time.time() * 1000
port_dict = {}
change_dict = {}
change_dict['sfp'] = port_dict
while True:
time.sleep(0.5)
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:
now_ms = time.time() * 1000
if (now_ms - start_ms >= timeout):
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)))
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
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 EnvironmentError:
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 & 0x44:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "CPU warm reset")
elif reboot_cause & 0x8:
return (self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU, None)
elif reboot_cause & 0x66:
return (self.REBOOT_CAUSE_WATCHDOG, None)
elif reboot_cause & 0x55:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "CPU cold reset")
elif reboot_cause & 0x11:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Power on reset")
elif reboot_cause & 0x77:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Power Cycle reset")
else:
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
def get_qualified_media_list(self):
return media_part_num_list

View File

@ -0,0 +1,121 @@
#!/usr/bin/env python
########################################################################
# DELLEMC Z9332F
#
# 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', 'bios-version']).strip()
def get_fpga_version():
val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:09: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, 0))
def get_cpld0_version():
return get_cpld_version(5, 0x0d)
def get_cpld1_version():
return get_cpld_version(4, 0x30)
def get_cpld2_version():
return get_cpld_version(4, 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
],
['Baseboard CPLD',
'Used for managing the CPU power sequence and CPU states',
get_cpld0_version
],
['Switch CPLD 1',
'Used for managing QSFP28/SFP port transceivers ',
get_cpld1_version
],
['Switch CPLD 2',
'Used for managing QSFP28/SFP port transceivers',
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

View File

@ -0,0 +1,139 @@
#!/usr/bin/env python
#############################################################################
# DellEmc Z9332F
#
# 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
except ImportError as 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}-0056/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 = (eeprom[9] << 8) | 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
+ eeprom[tlv_index + 1]]
code = "0x%02X" % tlv[0]
if tlv[0] == self._TLV_CODE_VENDOR_EXT:
value = str((tlv[2] << 24) | (tlv[3] << 16) |
(tlv[4] << 8) | tlv[5])
value += tlv[6:6 + tlv[1]].decode('ascii')
else:
name, value = self.decoder(None, tlv)
self.eeprom_tlv_dict[code] = value
if eeprom[tlv_index] == self._TLV_CODE_CRC_32:
break
tlv_index += 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].decode('ascii')
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(["{:02x}".format(T) for T in t[2]]).upper()
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].decode('ascii')
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].decode('ascii')
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].decode('ascii')
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].decode('ascii')
def system_eeprom_info(self):
"""
Returns a dictionary, where keys are the type code defined in
ONIE EEPROM format and values are their corresponding values
found in the system EEPROM.
"""
return self.eeprom_tlv_dict

View File

@ -0,0 +1,171 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9332F
#
# 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, get_ipmitool_raw_output
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Fan(FanBase):
"""DellEMC Platform-specific Fan class"""
# { FAN-ID: { Sensor-Name: Sensor-ID } }
FAN_SENSOR_MAPPING = { 1: {"Prsnt": 0x6, "State": 0x6, "Speed": 0xd},
2: {"Prsnt": 0x6, "State": 0x6, "Speed": 0x45},
3: {"Prsnt": 0x7, "State": 0x7, "Speed": 0xe},
4: {"Prsnt": 0x7, "State": 0x7, "Speed": 0x46},
5: {"Prsnt": 0x8, "State": 0x8, "Speed": 0xf},
6: {"Prsnt": 0x8, "State": 0x8, "Speed": 0x47},
7: {"Prsnt": 0x9, "State": 0x9, "Speed": 0x10},
8: {"Prsnt": 0x9, "State": 0x9, "Speed": 0x48},
9: {"Prsnt": 0xa, "State": 0xa, "Speed": 0x11},
10: {"Prsnt": 0xa, "State": 0xa, "Speed": 0x49},
11: {"Prsnt": 0xb, "State": 0xb, "Speed": 0x12},
12: {"Prsnt": 0xb, "State": 0xb, "Speed": 0x4a},
13: {"Prsnt": 0xc, "State": 0xc, "Speed": 0x13},
14: {"Prsnt": 0xc, "State": 0xc, "Speed": 0x4b} }
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x2f, "Speed": 0x33},
2: {"State": 0x39, "Speed": 0x3d} }
# { FANTRAY-ID: FRU-ID }
FAN_FRU_MAPPING = { 1: 6, 2: 7, 3: 8, 4: 9, 5: 10, 6: 11, 7: 12 }
PSU_FRU_MAPPING = { 1: 3, 2: 4 }
def __init__(self, fantray_index=1, fan_index=1, psu_fan=False, dependency=None):
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
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])
self.fan_dir_raw_cmd = "0x3a 0x0a {}".format(fantray_index)
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.fan_dir_raw_cmd = "0x3a 0x0a {}".format(7+(fan_index-1))
self.max_speed = 23500
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 None
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 None
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 & 0b1:
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_presence()
if not fan_status:
return None
dir_res = get_ipmitool_raw_output(self.fan_dir_raw_cmd)
if dir_res is not None and len(dir_res) == 1 :
return direction[dir_res[0]]
else:
return None
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 = 23500
is_valid, fan_speed = self.speed_sensor.get_reading()
if not is_valid or self.max_speed == 0:
return None
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
"""
is_valid, fan_speed = self.speed_sensor.get_reading()
return fan_speed if is_valid else None

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9332F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fan-Drawers' information available in the platform.
#
########################################################################
try:
from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.fan import Fan
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
Z9332F_FANS_PER_FANTRAY = 2
class FanDrawer(FanDrawerBase):
"""DellEMC Platform-specific Fan class"""
def __init__(self, fantray_index):
FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms
self.fantrayindex = fantray_index + 1
for i in range(Z9332F_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i))
def get_name(self):
"""
Retrieves the fan drawer name
Returns:
string: The name of the device
"""
return "FanTray{}".format(self.fantrayindex)

View File

@ -0,0 +1 @@
../../common/sonic_platform/hwaccess.py

View File

@ -0,0 +1,269 @@
#!/usr/bin/python3
########################################################################
# 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
def get_ipmitool_raw_output(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 EnvironmentError:
pass
for i in result.split():
result_bytes.append(int(i, 16))
return result_bytes
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_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 = 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 = 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 list(self.THRESHOLD_BIT_MASK.keys()):
raise ValueError("Invalid threshold type {} provided. Valid types "
"are {}".format(threshold_type,
list(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 = 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.decode('utf-8').rstrip('\n')
except EnvironmentError:
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.decode('utf-8').rstrip('\n')
except EnvironmentError:
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

View File

@ -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()

View File

@ -0,0 +1,179 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9332F
#
# 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, get_ipmitool_raw_output
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": 0x2f, "Current": 0x37,
"Power": 0x38, "Voltage": 0x36 },
2: { "State": 0x39, "Current": 0x41,
"Power": 0x42, "Voltage": 0x40 } }
# ( PSU-ID: FRU-ID }
FRU_MAPPING = { 1: 3, 2: 4 }
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.psu_type_raw_cmd = "0x3A 0x0B {}".format(psu_index+1)
self._fan_list.append(Fan(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 & 0b1010) == 0:
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:
return None
return "{:.1f}".format(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:
return None
return "{:.1f}".format(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:
return None
return "{:.1f}".format(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 & 0b1010) == 0:
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
"""
psu_type = [None, 'AC', 'AC', 'DC']
type_res = get_ipmitool_raw_output(self.psu_type_raw_cmd)
if type_res is not None and len(type_res) == 1 :
return psu_type[type_res[0]]
return None

View File

@ -0,0 +1,908 @@
#!/usr/bin/env python
#############################################################################
# DELLEMC S5248F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
import os
import time
import struct
import mmap
from sonic_platform_base.sfp_base import SfpBase
from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId
from sonic_platform_base.sonic_sfp.sff8436 import sff8436Dom
from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId
from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom
from sonic_platform_base.sonic_sfp.sff8472 import sffbase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
# Enabled when ext_media is available
#ext_media_module = None
#try:
# import ext_media_api as ext_media_module
#except :
# ext_media_module = None
# pass
PAGE_OFFSET = 0
KEY_OFFSET = 1
KEY_WIDTH = 2
FUNC_NAME = 3
QSFP_INFO_OFFSET = 128
QSFP_DOM_OFFSET = 0
QSFP_DOM_OFFSET1 = 384
SFP_INFO_OFFSET = 0
SFP_DOM_OFFSET = 256
SFP_STATUS_CONTROL_OFFSET = 110
SFP_STATUS_CONTROL_WIDTH = 7
SFP_TX_DISABLE_HARD_BIT = 7
SFP_TX_DISABLE_SOFT_BIT = 6
qsfp_cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)',
'Length OM1(m)', 'Length Cable Assembly(m)')
qsfp_compliance_code_tup = (
'10/40G Ethernet Compliance Code',
'SONET Compliance codes',
'SAS/SATA compliance codes',
'Gigabit Ethernet Compliant codes',
'Fibre Channel link length/Transmitter Technology',
'Fibre Channel transmission media',
'Fibre Channel Speed')
sfp_cable_length_tup = ('LengthSMFkm-UnitsOfKm', 'LengthSMF(UnitsOf100m)',
'Length50um(UnitsOf10m)', 'Length62.5um(UnitsOfm)',
'LengthOM3(UnitsOf10m)', 'LengthCable(UnitsOfm)')
sfp_compliance_code_tup = ('10GEthernetComplianceCode', 'InfinibandComplianceCode',
'ESCONComplianceCodes', 'SONETComplianceCodes',
'EthernetComplianceCodes', 'FibreChannelLinkLength',
'FibreChannelTechnology', 'SFP+CableTechnology',
'FibreChannelTransmissionMedia', 'FibreChannelSpeed')
info_dict_keys = ['type', 'hardware_rev', 'serial',
'manufacturer', 'model', 'connector',
'encoding', 'ext_identifier', 'ext_rateselect_compliance',
'cable_type', 'cable_length', 'nominal_bit_rate',
'specification_compliance', 'type_abbrv_name','vendor_date', 'vendor_oui']
dom_dict_keys = ['rx_los', 'tx_fault', 'reset_status',
'power_lpmode', 'tx_disable', 'tx_disable_channel',
'temperature', 'voltage', 'rx1power',
'rx2power', 'rx3power', 'rx4power',
'tx1bias', 'tx2bias', 'tx3bias',
'tx4bias', 'tx1power', 'tx2power',
'tx3power', 'tx4power']
threshold_dict_keys = ['temphighalarm', 'temphighwarning',
'templowalarm', 'templowwarning',
'vcchighalarm', 'vcchighwarning',
'vcclowalarm', 'vcclowwarning',
'rxpowerhighalarm', 'rxpowerhighwarning',
'rxpowerlowalarm', 'rxpowerlowwarning',
'txpowerhighalarm', 'txpowerhighwarning',
'txpowerlowalarm', 'txpowerlowwarning',
'txbiashighalarm', 'txbiashighwarning',
'txbiaslowalarm', 'txbiaslowwarning']
sff8436_parser = {
'reset_status': [QSFP_DOM_OFFSET, 2, 1, 'parse_dom_status_indicator'],
'rx_los': [QSFP_DOM_OFFSET, 3, 1, 'parse_dom_tx_rx_los'],
'tx_fault': [QSFP_DOM_OFFSET, 4, 1, 'parse_dom_tx_fault'],
'tx_disable': [QSFP_DOM_OFFSET, 86, 1, 'parse_dom_tx_disable'],
'power_lpmode': [QSFP_DOM_OFFSET, 93, 1, 'parse_dom_power_control'],
'power_override': [QSFP_DOM_OFFSET, 93, 1, 'parse_dom_power_control'],
'Temperature': [QSFP_DOM_OFFSET, 22, 2, 'parse_temperature'],
'Voltage': [QSFP_DOM_OFFSET, 26, 2, 'parse_voltage'],
'ChannelMonitor': [QSFP_DOM_OFFSET, 34, 16, 'parse_channel_monitor_params'],
'ChannelMonitor_TxPower':
[QSFP_DOM_OFFSET, 34, 24, 'parse_channel_monitor_params_with_tx_power'],
'cable_type': [QSFP_INFO_OFFSET, -1, -1, 'parse_sfp_info_bulk'],
'cable_length': [QSFP_INFO_OFFSET, -1, -1, 'parse_sfp_info_bulk'],
'connector': [QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'type': [QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'encoding': [QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'ext_identifier': [QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'ext_rateselect_compliance':
[QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'nominal_bit_rate': [QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'specification_compliance':
[QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'type_abbrv_name': [QSFP_INFO_OFFSET, 0, 20, 'parse_sfp_info_bulk'],
'manufacturer': [QSFP_INFO_OFFSET, 20, 16, 'parse_vendor_name'],
'vendor_oui': [QSFP_INFO_OFFSET, 37, 3, 'parse_vendor_oui'],
'model': [QSFP_INFO_OFFSET, 40, 16, 'parse_vendor_pn'],
'hardware_rev': [QSFP_INFO_OFFSET, 56, 2, 'parse_vendor_rev'],
'serial': [QSFP_INFO_OFFSET, 68, 16, 'parse_vendor_sn'],
'vendor_date': [QSFP_INFO_OFFSET, 84, 8, 'parse_vendor_date'],
'dom_capability': [QSFP_INFO_OFFSET, 92, 1, 'parse_qsfp_dom_capability'],
'dom_rev': [QSFP_DOM_OFFSET, 1, 1, 'parse_sfp_dom_rev'],
'ModuleThreshold': [QSFP_DOM_OFFSET1, 128, 24, 'parse_module_threshold_values'],
'ChannelThreshold': [QSFP_DOM_OFFSET1, 176, 16, 'parse_channel_threshold_values'],
}
sff8472_parser = {
'Temperature': [SFP_DOM_OFFSET, 96, 2, 'parse_temperature'],
'Voltage': [SFP_DOM_OFFSET, 98, 2, 'parse_voltage'],
'ChannelMonitor': [SFP_DOM_OFFSET, 100, 6, 'parse_channel_monitor_params'],
'cable_type': [SFP_INFO_OFFSET, -1, -1, 'parse_sfp_info_bulk'],
'cable_length': [SFP_INFO_OFFSET, -1, -1, 'parse_sfp_info_bulk'],
'connector': [SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'type': [SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'encoding': [SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'ext_identifier': [SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'ext_rateselect_compliance':
[SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'nominal_bit_rate': [SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'specification_compliance':
[SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'type_abbrv_name': [SFP_INFO_OFFSET, 0, 21, 'parse_sfp_info_bulk'],
'manufacturer': [SFP_INFO_OFFSET, 20, 16, 'parse_vendor_name'],
'vendor_oui': [SFP_INFO_OFFSET, 37, 3, 'parse_vendor_oui'],
'model': [SFP_INFO_OFFSET, 40, 16, 'parse_vendor_pn'],
'hardware_rev': [SFP_INFO_OFFSET, 56, 4, 'parse_vendor_rev'],
'serial': [SFP_INFO_OFFSET, 68, 16, 'parse_vendor_sn'],
'vendor_date': [SFP_INFO_OFFSET, 84, 8, 'parse_vendor_date'],
'ModuleThreshold': [SFP_DOM_OFFSET, 0, 56, 'parse_alarm_warning_threshold'],
}
class Sfp(SfpBase):
"""
DELLEMC Platform-specific Sfp class
"""
BASE_RES_PATH = "/sys/bus/pci/devices/0000:09:00.0/resource0"
def __init__(self, index, sfp_type, eeprom_path):
SfpBase.__init__(self)
self.sfp_type = sfp_type
self.index = index
self.eeprom_path = eeprom_path
self.qsfpInfo = sff8436InterfaceId()
self.qsfpDomInfo = sff8436Dom()
self.sfpInfo = sff8472InterfaceId()
self.sfpDomInfo = sff8472Dom(None,1)
def get_eeprom_sysfs_path(self):
return self.eeprom_path
def pci_mem_read(self, mm, offset):
mm.seek(offset)
read_data_stream = mm.read(4)
reg_val = struct.unpack('I', read_data_stream)
mem_val = str(reg_val)[1:-2]
# print "reg_val read:%x"%reg_val
return mem_val
def pci_mem_write(self, mm, offset, data):
mm.seek(offset)
# print "data to write:%x"%data
mm.write(struct.pack('I', data))
def pci_set_value(self, resource, val, offset):
fd = os.open(resource, os.O_RDWR)
mm = mmap.mmap(fd, 0)
val = self.pci_mem_write(mm, offset, val)
mm.close()
os.close(fd)
return val
def pci_get_value(self, resource, offset):
fd = os.open(resource, os.O_RDWR)
mm = mmap.mmap(fd, 0)
val = self.pci_mem_read(mm, offset)
mm.close()
os.close(fd)
return val
def _read_eeprom_bytes(self, eeprom_path, offset, num_bytes):
eeprom_raw = []
try:
eeprom = open(eeprom_path, mode="rb", buffering=0)
except IOError:
return None
for i in range(0, num_bytes):
eeprom_raw.append("0x00")
try:
eeprom.seek(offset)
raw = eeprom.read(num_bytes)
except IOError:
eeprom.close()
return None
try:
for n in range(0, num_bytes):
eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2)
except BaseException:
eeprom.close()
return None
eeprom.close()
return eeprom_raw
def _get_eeprom_data(self, eeprom_key):
eeprom_data = None
page_offset = None
if(self.sfp_type == 'QSFP'):
page_offset = sff8436_parser[eeprom_key][PAGE_OFFSET]
eeprom_data_raw = self._read_eeprom_bytes(
self.eeprom_path,
(sff8436_parser[eeprom_key][PAGE_OFFSET] +
sff8436_parser[eeprom_key][KEY_OFFSET]),
sff8436_parser[eeprom_key][KEY_WIDTH])
if (eeprom_data_raw is not None):
# Offset 128 is used to retrieve sff8436InterfaceId Info
# Offset 0 is used to retrieve sff8436Dom Info
if (page_offset == 128):
if ( self.qsfpInfo is None):
return None
eeprom_data = getattr(
self.qsfpInfo, sff8436_parser[eeprom_key][FUNC_NAME])(
eeprom_data_raw, 0)
else:
if ( self.qsfpDomInfo is None):
return None
eeprom_data = getattr(
self.qsfpDomInfo, sff8436_parser[eeprom_key][FUNC_NAME])(
eeprom_data_raw, 0)
else:
page_offset = sff8472_parser[eeprom_key][PAGE_OFFSET]
eeprom_data_raw = self._read_eeprom_bytes(
self.eeprom_path,
(sff8472_parser[eeprom_key][PAGE_OFFSET] +
sff8472_parser[eeprom_key][KEY_OFFSET]),
sff8472_parser[eeprom_key][KEY_WIDTH])
if (eeprom_data_raw is not None):
# Offset 0 is used to retrieve sff8472InterfaceId Info
# Offset 256 is used to retrieve sff8472Dom Info
if (page_offset == 0):
if ( self.sfpInfo is None):
return None
eeprom_data = getattr(
self.sfpInfo, sff8472_parser[eeprom_key][FUNC_NAME])(
eeprom_data_raw, 0)
else:
if ( self.sfpDomInfo is None):
return None
eeprom_data = getattr(
self.sfpDomInfo, sff8472_parser[eeprom_key][FUNC_NAME])(
eeprom_data_raw, 0)
return eeprom_data
def get_transceiver_info(self):
"""
Retrieves transceiver info of this SFP
"""
transceiver_info_dict = {}
compliance_code_dict = {}
transceiver_info_dict = dict.fromkeys(info_dict_keys, 'N/A')
# BaseInformation
try:
iface_data = self._get_eeprom_data('type')
connector = iface_data['data']['Connector']['value']
encoding = iface_data['data']['EncodingCodes']['value']
ext_id = iface_data['data']['Extended Identifier']['value']
rate_identifier = iface_data['data']['RateIdentifier']['value']
identifier = iface_data['data']['type']['value']
type_abbrv_name=iface_data['data']['type_abbrv_name']['value']
if(self.sfp_type == 'QSFP'):
bit_rate = str(
iface_data['data']['Nominal Bit Rate(100Mbs)']['value'])
for key in qsfp_compliance_code_tup:
if key in iface_data['data']['Specification compliance']['value']:
compliance_code_dict[key] = iface_data['data']['Specification compliance']['value'][key]['value']
for key in qsfp_cable_length_tup:
if key in iface_data['data']:
cable_type = key
cable_length = str(iface_data['data'][key]['value'])
else:
bit_rate = str(
iface_data['data']['NominalSignallingRate(UnitsOf100Mbd)']['value'])
for key in sfp_compliance_code_tup:
if key in iface_data['data']['Specification compliance']['value']:
compliance_code_dict[key] = iface_data['data']['Specification compliance']['value'][key]['value']
for key in sfp_cable_length_tup:
if key in iface_data['data']:
cable_type = key
cable_length = str(iface_data['data'][key]['value'])
transceiver_info_dict['type_abbrv_name']=type_abbrv_name
transceiver_info_dict['type'] = identifier
transceiver_info_dict['connector'] = connector
transceiver_info_dict['encoding'] = encoding
transceiver_info_dict['ext_identifier'] = ext_id
transceiver_info_dict['ext_rateselect_compliance'] = rate_identifier
transceiver_info_dict['cable_type'] = cable_type
transceiver_info_dict['cable_length'] = cable_length
transceiver_info_dict['nominal_bit_rate'] = bit_rate
transceiver_info_dict['specification_compliance'] = str(compliance_code_dict)
except (ValueError, TypeError) : pass
# Vendor Date
try:
vendor_date_data = self._get_eeprom_data('vendor_date')
vendor_date = vendor_date_data['data']['VendorDataCode(YYYY-MM-DD Lot)']['value']
transceiver_info_dict['vendor_date'] = vendor_date
except (ValueError, TypeError) : pass
# Vendor Name
try:
vendor_name_data = self._get_eeprom_data('manufacturer')
vendor_name = vendor_name_data['data']['Vendor Name']['value']
transceiver_info_dict['manufacturer'] = vendor_name
except (ValueError, TypeError) : pass
# Vendor OUI
try:
vendor_oui_data = self._get_eeprom_data('vendor_oui')
vendor_oui = vendor_oui_data['data']['Vendor OUI']['value']
transceiver_info_dict['vendor_oui'] = vendor_oui
except (ValueError, TypeError) : pass
# Vendor PN
try:
vendor_pn_data = self._get_eeprom_data('model')
vendor_pn = vendor_pn_data['data']['Vendor PN']['value']
transceiver_info_dict['model'] = vendor_pn
except (ValueError, TypeError) : pass
# Vendor Revision
try:
vendor_rev_data = self._get_eeprom_data('hardware_rev')
vendor_rev = vendor_rev_data['data']['Vendor Rev']['value']
transceiver_info_dict['hardware_rev'] = vendor_rev
except (ValueError, TypeError) : pass
# Vendor Serial Number
try:
vendor_sn_data = self._get_eeprom_data('serial')
vendor_sn = vendor_sn_data['data']['Vendor SN']['value']
transceiver_info_dict['serial'] = vendor_sn
except (ValueError, TypeError) : pass
# Attempt ext_media read
# if ext_media_module is not None:
# ext_media_dict = ext_media_module.get_ext_media_info(self)
# for key in ext_media_dict:
# value = ext_media_dict[key]
# if value in [None, 'None', 'none','n/a', '']:
# value = 'N/A'
# transceiver_info_dict[key] = str(value)
return transceiver_info_dict
def get_transceiver_threshold_info(self):
"""
Retrieves transceiver threshold info of this SFP
"""
transceiver_dom_threshold_dict = {}
transceiver_dom_threshold_dict = dict.fromkeys(
threshold_dict_keys, 'N/A')
try:
# Module Threshold
module_threshold_data = self._get_eeprom_data('ModuleThreshold')
if (self.sfp_type == 'QSFP'):
transceiver_dom_threshold_dict['temphighalarm'] = module_threshold_data['data']['TempHighAlarm']['value']
transceiver_dom_threshold_dict['temphighwarning'] = module_threshold_data['data']['TempHighWarning']['value']
transceiver_dom_threshold_dict['templowalarm'] = module_threshold_data['data']['TempLowAlarm']['value']
transceiver_dom_threshold_dict['templowwarning'] = module_threshold_data['data']['TempLowWarning']['value']
transceiver_dom_threshold_dict['vcchighalarm'] = module_threshold_data['data']['VccHighAlarm']['value']
transceiver_dom_threshold_dict['vcchighwarning'] = module_threshold_data['data']['VccHighWarning']['value']
transceiver_dom_threshold_dict['vcclowalarm'] = module_threshold_data['data']['VccLowAlarm']['value']
transceiver_dom_threshold_dict['vcclowwarning'] = module_threshold_data['data']['VccLowWarning']['value']
else: #SFP
transceiver_dom_threshold_dict['temphighalarm'] = module_threshold_data['data']['TempHighAlarm']['value']
transceiver_dom_threshold_dict['templowalarm'] = module_threshold_data['data']['TempLowAlarm']['value']
transceiver_dom_threshold_dict['temphighwarning'] = module_threshold_data['data']['TempHighWarning']['value']
transceiver_dom_threshold_dict['templowwarning'] = module_threshold_data['data']['TempLowWarning']['value']
transceiver_dom_threshold_dict['vcchighalarm'] = module_threshold_data['data']['VoltageHighAlarm']['value']
transceiver_dom_threshold_dict['vcclowalarm'] = module_threshold_data['data']['VoltageLowAlarm']['value']
transceiver_dom_threshold_dict['vcchighwarning'] = module_threshold_data['data']['VoltageHighWarning']['value']
transceiver_dom_threshold_dict['vcclowwarning'] = module_threshold_data['data']['VoltageLowWarning']['value']
transceiver_dom_threshold_dict['txbiashighalarm'] = module_threshold_data['data']['BiasHighAlarm']['value']
transceiver_dom_threshold_dict['txbiaslowalarm'] = module_threshold_data['data']['BiasLowAlarm']['value']
transceiver_dom_threshold_dict['txbiashighwarning'] = module_threshold_data['data']['BiasHighWarning']['value']
transceiver_dom_threshold_dict['txbiaslowwarning'] = module_threshold_data['data']['BiasLowWarning']['value']
transceiver_dom_threshold_dict['txpowerhighalarm'] = module_threshold_data['data']['TXPowerHighAlarm']['value']
transceiver_dom_threshold_dict['txpowerlowalarm'] = module_threshold_data['data']['TXPowerLowAlarm']['value']
transceiver_dom_threshold_dict['txpowerhighwarning'] = module_threshold_data['data']['TXPowerHighWarning']['value']
transceiver_dom_threshold_dict['txpowerlowwarning'] = module_threshold_data['data']['TXPowerLowWarning']['value']
transceiver_dom_threshold_dict['rxpowerhighalarm'] = module_threshold_data['data']['RXPowerHighAlarm']['value']
transceiver_dom_threshold_dict['rxpowerlowalarm'] = module_threshold_data['data']['RXPowerLowAlarm']['value']
transceiver_dom_threshold_dict['rxpowerhighwarning'] = module_threshold_data['data']['RXPowerHighWarning']['value']
transceiver_dom_threshold_dict['rxpowerlowwarning'] = module_threshold_data['data']['RXPowerLowWarning']['value']
except (ValueError, TypeError) : pass
try:
if (self.sfp_type == 'QSFP'):
channel_threshold_data = self._get_eeprom_data('ChannelThreshold')
transceiver_dom_threshold_dict['rxpowerhighalarm'] = channel_threshold_data['data']['RxPowerHighAlarm']['value']
transceiver_dom_threshold_dict['rxpowerhighwarning'] = channel_threshold_data['data']['RxPowerHighWarning']['value']
transceiver_dom_threshold_dict['rxpowerlowalarm'] = channel_threshold_data['data']['RxPowerLowAlarm']['value']
transceiver_dom_threshold_dict['rxpowerlowwarning'] = channel_threshold_data['data']['RxPowerLowWarning']['value']
transceiver_dom_threshold_dict['txbiashighalarm'] = channel_threshold_data['data']['TxBiasHighAlarm']['value']
transceiver_dom_threshold_dict['txbiashighwarning'] = channel_threshold_data['data']['TxBiasHighWarning']['value']
transceiver_dom_threshold_dict['txbiaslowalarm'] = channel_threshold_data['data']['TxBiasLowAlarm']['value']
transceiver_dom_threshold_dict['txbiaslowwarning'] = channel_threshold_data['data']['TxBiasLowWarning']['value']
except (ValueError, TypeError) : pass
return transceiver_dom_threshold_dict
def get_transceiver_bulk_status(self):
"""
Retrieves transceiver bulk status of this SFP
"""
tx_bias_list = []
rx_power_list = []
transceiver_dom_dict = {}
transceiver_dom_dict = dict.fromkeys(dom_dict_keys, 'N/A')
# RxLos
rx_los = self.get_rx_los()
# TxFault
tx_fault = self.get_tx_fault()
# ResetStatus
reset_state = self.get_reset_status()
# LowPower Mode
lp_mode = self.get_lpmode()
# TxDisable
tx_disable = self.get_tx_disable()
# TxDisable Channel
tx_disable_channel = self.get_tx_disable_channel()
# Temperature
temperature = self.get_temperature()
# Voltage
voltage = self.get_voltage()
# Channel Monitor
tx_power_list = self.get_tx_power()
# tx bias
tx_bias_list = self.get_tx_bias()
# rx power
rx_power_list = self.get_rx_power()
if tx_bias_list is not None:
transceiver_dom_dict['tx1bias'] = tx_bias_list[0]
transceiver_dom_dict['tx2bias'] = tx_bias_list[1]
transceiver_dom_dict['tx3bias'] = tx_bias_list[2]
transceiver_dom_dict['tx4bias'] = tx_bias_list[3]
if rx_power_list is not None:
transceiver_dom_dict['rx1power'] = rx_power_list[0]
transceiver_dom_dict['rx2power'] = rx_power_list[1]
transceiver_dom_dict['rx3power'] = rx_power_list[2]
transceiver_dom_dict['rx4power'] = rx_power_list[3]
if tx_power_list is not None:
transceiver_dom_dict['tx1power'] = tx_power_list[0]
transceiver_dom_dict['tx2power'] = tx_power_list[1]
transceiver_dom_dict['tx3power'] = tx_power_list[2]
transceiver_dom_dict['tx4power'] = tx_power_list[3]
transceiver_dom_dict['rx_los'] = rx_los
transceiver_dom_dict['tx_fault'] = tx_fault
transceiver_dom_dict['reset_status'] = reset_state
transceiver_dom_dict['power_lpmode'] = lp_mode
transceiver_dom_dict['tx_disable'] = tx_disable
transceiver_dom_dict['tx_disable_channel'] = tx_disable_channel
transceiver_dom_dict['temperature'] = temperature
transceiver_dom_dict['voltage'] = voltage
return transceiver_dom_dict
def get_name(self):
"""
Retrieves the name of the sfp
Returns : QSFP or QSFP+ or QSFP28
"""
try:
iface_data = self._get_eeprom_data('type')
identifier = iface_data['data']['type']['value']
except (TypeError, ValueError):
return 'N/A'
return identifier
def get_presence(self):
"""
Retrieves the presence of the sfp
Returns : True if sfp is present and false if it is absent
"""
# Check for invalid port_num
mask = {'QSFP' : (1 << 4), 'SFP' : (1 << 0)}
# Port offset starts with 0x4004
port_offset = 16388 + ((self.index-1) * 16)
try:
status = self.pci_get_value(self.BASE_RES_PATH, port_offset)
reg_value = int(status)
# ModPrsL is active low
if reg_value & mask[self.sfp_type] == 0:
return True
except ValueError: pass
return False
def get_model(self):
"""
Retrieves the model number (or part number) of the sfp
"""
try:
vendor_pn_data = self._get_eeprom_data('model')
vendor_pn = vendor_pn_data['data']['Vendor PN']['value']
except (TypeError, ValueError):
return 'N/A'
return vendor_pn
def get_serial(self):
"""
Retrieves the serial number of the sfp
"""
try:
vendor_sn_data = self._get_eeprom_data('serial')
vendor_sn = vendor_sn_data['data']['Vendor SN']['value']
except (TypeError, ValueError):
return 'N/A'
return vendor_sn
def get_reset_status(self):
"""
Retrives the reset status of SFP
"""
reset_status = False
try:
if (self.sfp_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
status = self.pci_get_value(self.BASE_RES_PATH, port_offset)
reg_value = int(status)
# Mask off 4th bit for reset status
mask = (1 << 4)
reset_status = not (reg_value & mask)
except ValueError: pass
return reset_status
def get_rx_los(self):
"""
Retrieves the RX LOS (lost-of-signal) status of SFP
"""
rx_los = False
try:
if (self.sfp_type == 'QSFP'):
rx_los_data = self._get_eeprom_data('rx_los')
# As the function expects a single boolean, if any one channel experience LOS,
# is considered LOS for QSFP
for rx_los_id in ('Rx1LOS', 'Rx2LOS', 'Rx3LOS', 'Rx4LOS') :
rx_los |= (rx_los_data['data'][rx_los_id]['value'] is 'On')
else:
rx_los_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
data = int(rx_los_data[0], 16)
rx_los = sffbase().test_bit(data, 1) != 0
except (TypeError, ValueError):
return 'N/A'
return rx_los
def get_tx_fault(self):
"""
Retrieves the TX fault status of SFP
"""
tx_fault = False
try:
if (self.sfp_type == 'QSFP'):
tx_fault_data = self._get_eeprom_data('tx_fault')
for tx_fault_id in ('Tx1Fault', 'Tx2Fault', 'Tx3Fault', 'Tx4Fault') :
tx_fault |= (tx_fault_data['data'][tx_fault_id]['value'] is 'On')
else:
tx_fault_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
data = int(tx_fault_data[0], 16)
tx_fault = (sffbase().test_bit(data, 2) != 0)
except (TypeError, ValueError):
return 'N/A'
return tx_fault
def get_tx_disable(self):
"""
Retrieves the tx_disable status of this SFP
"""
tx_disable = False
try:
if (self.sfp_type == 'QSFP'):
tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
tx_disable |= (tx_disable_data['data'][tx_disable_id]['value'] is 'On')
else:
tx_disable_data = self._read_eeprom_bytes(self.eeprom_path, SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
data = int(tx_disable_data[0], 16)
tx_disable_hard = (sffbase().test_bit(data, SFP_TX_DISABLE_HARD_BIT) != 0)
tx_disable_soft = (sffbase().test_bit(data, SFP_TX_DISABLE_SOFT_BIT) != 0)
tx_disable = tx_disable_hard | tx_disable_soft
except (TypeError, ValueError):
return 'N/A'
return tx_disable
def get_tx_disable_channel(self):
"""
Retrieves the TX disabled channels in this SFP
"""
tx_disable_channel = 0
try:
if (self.sfp_type == 'QSFP'):
tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
tx_disable_channel <<= 1
tx_disable_channel |= (tx_disable_data['data']['Tx1Disable']['value'] is 'On')
except (TypeError, ValueError):
return 'N/A'
return tx_disable_channel
def get_lpmode(self):
"""
Retrieves the lpmode(low power mode) of this SFP
"""
lpmode_state = False
try:
if (self.sfp_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
status = self.pci_get_value(self.BASE_RES_PATH, port_offset)
reg_value = int(status)
# Mask off 6th bit for lpmode
mask = (1 << 6)
lpmode_state = (reg_value & mask)
except ValueError: pass
return lpmode_state
def get_power_override(self):
"""
Retrieves the power-override status of this SFP
"""
power_override_state = False
try:
if (self.sfp_type == 'QSFP'):
power_override_data = self._get_eeprom_data('power_override')
power_override = power_override_data['data']['PowerOverRide']['value']
power_override_state = (power_override is 'On')
except (TypeError, ValueError): pass
return power_override_state
def get_temperature(self):
"""
Retrieves the temperature of this SFP
"""
temperature = None
try :
temperature_data = self._get_eeprom_data('Temperature')
temperature = temperature_data['data']['Temperature']['value']
except (TypeError, ValueError):
return None
return temperature
def get_voltage(self):
"""
Retrieves the supply voltage of this SFP
"""
voltage = None
try:
voltage_data = self._get_eeprom_data('Voltage')
voltage = voltage_data['data']['Vcc']['value']
except (TypeError, ValueError):
return None
return voltage
def get_tx_bias(self):
"""
Retrieves the TX bias current of this SFP
"""
tx_bias_list = []
try:
tx_bias_data = self._get_eeprom_data('ChannelMonitor')
if (self.sfp_type == 'QSFP'):
for tx_bias_id in ('TX1Bias', 'TX2Bias', 'TX3Bias', 'TX4Bias') :
tx_bias = tx_bias_data['data'][tx_bias_id]['value']
tx_bias_list.append(tx_bias)
else:
tx1_bias = tx_bias_data['data']['TXBias']['value']
tx_bias_list = [tx1_bias, "N/A", "N/A", "N/A"]
except (TypeError, ValueError):
return None
return tx_bias_list
def get_rx_power(self):
"""
Retrieves the received optical power for this SFP
"""
rx_power_list = []
try:
rx_power_data = self._get_eeprom_data('ChannelMonitor')
if (self.sfp_type == 'QSFP'):
for rx_power_id in ('RX1Power', 'RX2Power', 'RX3Power', 'RX4Power'):
rx_power = rx_power_data['data'][rx_power_id]['value']
rx_power_list.append(rx_power)
else:
rx1_pw = rx_power_data['data']['RXPower']['value']
rx_power_list = [rx1_pw, "N/A", "N/A", "N/A"]
except (TypeError, ValueError):
return None
return rx_power_list
def get_tx_power(self):
"""
Retrieves the TX power of this SFP
"""
tx_power_list = []
try:
if(self.sfp_type == 'QSFP'):
# QSFP capability byte parse, through this byte can know whether it support tx_power or not.
# TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436,
# need to add more code for determining the capability and version compliance
# in SFF-8636 dom capability definitions evolving with the versions.
qspf_dom_capability_data = self._get_eeprom_data('dom_capability')
qsfp_dom_rev_data = self._get_eeprom_data('dom_rev')
qsfp_dom_rev = qsfp_dom_rev_data['data']['dom_rev']['value']
qsfp_tx_power_support = qspf_dom_capability_data['data']['Tx_power_support']['value']
# The tx_power monitoring is only available on QSFP which compliant with SFF-8636
# and claimed that it support tx_power with one indicator bit.
if (qsfp_dom_rev[0:8] != 'SFF-8636' or (qsfp_dom_rev[0:8] == 'SFF-8636' and qsfp_tx_power_support != 'on')):
return None
channel_monitor_data = self._get_eeprom_data('ChannelMonitor_TxPower')
for tx_power_id in ('TX1Power', 'TX2Power', 'TX3Power', 'TX4Power'):
tx_pw = channel_monitor_data['data'][tx_power_id]['value']
tx_power_list.append(tx_pw)
else:
channel_monitor_data = self._get_eeprom_data('ChannelMonitor')
tx1_pw = channel_monitor_data['data']['TXPower']['value']
tx_power_list = [tx1_pw, 'N/A', 'N/A', 'N/A']
except (TypeError, ValueError):
return None
return tx_power_list
def reset(self):
"""
Reset the SFP and returns all user settings to their default state
"""
try:
if (self.sfp_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
status = self.pci_get_value(self.BASE_RES_PATH, port_offset)
reg_value = int(status)
# Mask off 4th bit for reset
mask = (1 << 4)
# ResetL is active low
reg_value = reg_value & ~mask
# Convert our register value back to a hex string and write back
self.pci_set_value(self.BASE_RES_PATH, reg_value, port_offset)
# Sleep 1 second to allow it to settle
time.sleep(1)
reg_value = reg_value | mask
# Convert our register value back to a hex string and write back
self.pci_set_value(self.BASE_RES_PATH, reg_value, port_offset)
except ValueError:
return False
return True
def set_lpmode(self, lpmode):
"""
Sets the lpmode(low power mode) of this SFP
"""
try:
if (self.sfp_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
status = self.pci_get_value(self.BASE_RES_PATH, port_offset)
reg_value = int(status)
# Mask off 6th bit for lowpower mode
mask = (1 << 6)
# LPMode is active high; set or clear the bit accordingly
if lpmode is True:
reg_value = reg_value | mask
else:
reg_value = reg_value & ~mask
# Convert our register value back to a hex string and write back
self.pci_set_value(self.BASE_RES_PATH, reg_value, port_offset)
except ValueError:
return False
return True
def get_intl_state(self):
"""
Sets the intL (interrupt; active low) pin of this SFP
"""
intl_state = True
try:
if (self.sfp_type == 'QSFP'):
# Port offset starts with 0x4004
port_offset = 16388 + ((self.index-1) * 16)
status = self.pci_get_value(self.BASE_RES_PATH, port_offset)
reg_value = int(status)
# Mask off 4th bit for intL
mask = (1 << 4)
intl_state = (reg_value & mask)
except ValueError: pass
return intl_state
def tx_disable(self, tx_disable):
"""
Disable SFP TX for all channels
"""
return False
def tx_disable_channel(self, channel, disable):
"""
Sets the tx_disable for specified SFP channels
"""
return False
def set_power_override(self, power_override, power_set):
"""
Sets SFP power level using power_override and power_set
"""
return False
def get_status(self):
"""
Retrieves the operational status of the device
"""
reset = self.get_reset_status()
return (not reset)
def get_max_port_power(self):
"""
Retrieves the maximum power allowed on the port in watts
***
This method of fetching power values is not ideal.
TODO: enhance by placing power limits in config file
***
"""
return (12.0 if self.sfp_type=='QSFP' else 2.5)

View File

@ -0,0 +1,176 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9332F
#
# 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', 0x5],
['Baseboard U3', 0x4],
['SW Internal', 0x61],
['Fan U52', 0x0],
['Fan U17', 0x1],
['SW U52', 0x2],
['SW U16', 0x3],
['PSU1 Inlet', 0x34],
['PSU1 Hotspot', 0x35],
['PSU2 Inlet', 0x3E],
['PSU2 Hotspot', 0x3F],
['SW U04', 0x4F],
['SW U14', 0x56],
['SW U4403', 0x5D]
]
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("UpperCritical")
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 get_high_critical_threshold(self):
"""
Retrieves the high critical threshold temperature of thermal
Returns:
A float number, the high critical threshold temperature of
thermal in Celsius up to nearest thousandth of one degree
Celsius, e.g. 30.125
"""
is_valid, high_crit_threshold = self.sensor.get_threshold("UpperNonRecoverable")
if not is_valid:
high_crit_threshold = 0
return float(high_crit_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

View File

@ -0,0 +1,211 @@
#!/usr/bin/env python
########################################################################
#
# DELLEMC Z9332F
#
# 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