[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:
parent
6d9ecbcfd8
commit
3c9a7ec623
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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); \
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
1
platform/broadcom/sonic-platform-modules-dell/z9332f/setup.py
Symbolic link
1
platform/broadcom/sonic-platform-modules-dell/z9332f/setup.py
Symbolic link
@ -0,0 +1 @@
|
||||
../s6100/setup.py
|
@ -0,0 +1 @@
|
||||
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "fan_drawer"]
|
319
platform/broadcom/sonic-platform-modules-dell/z9332f/sonic_platform/chassis.py
Executable file
319
platform/broadcom/sonic-platform-modules-dell/z9332f/sonic_platform/chassis.py
Executable 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
|
@ -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
|
@ -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
|
171
platform/broadcom/sonic-platform-modules-dell/z9332f/sonic_platform/fan.py
Executable file
171
platform/broadcom/sonic-platform-modules-dell/z9332f/sonic_platform/fan.py
Executable 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
|
@ -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)
|
@ -0,0 +1 @@
|
||||
../../common/sonic_platform/hwaccess.py
|
@ -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
|
@ -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 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
|
@ -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)
|
||||
|
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user