[202012]Dell s6100 Fix sonic-mgmt platform test failures (#16207)
ADO: 24709703 #### Why I did it sonic-mgmt platform testcases failed. #### How I did it Implement platform API 2.0. #### How to verify it Run sonic-mgmt tests.
This commit is contained in:
parent
5d5727f6b9
commit
7fc5436d73
@ -15,6 +15,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "FPGA"
|
"name": "FPGA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SSD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ONIE"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fans": [
|
"fans": [
|
||||||
@ -463,6 +469,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "QSFP+ or later"
|
"name": "QSFP+ or later"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SFP/SFP+/SFP28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SFP/SFP+/SFP28"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -10,16 +10,17 @@
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
from sonic_platform_base.chassis_base import ChassisBase
|
from sonic_platform_base.chassis_base import ChassisBase
|
||||||
from sonic_platform.sfp import Sfp
|
from sonic_platform.component import Component
|
||||||
from sonic_platform.psu import Psu
|
from sonic_platform.eeprom import Eeprom
|
||||||
from sonic_platform.fan_drawer import FanDrawer
|
from sonic_platform.fan_drawer import FanDrawer
|
||||||
from sonic_platform.module import Module
|
from sonic_platform.module import Module
|
||||||
|
from sonic_platform.psu import Psu
|
||||||
from sonic_platform.thermal import Thermal
|
from sonic_platform.thermal import Thermal
|
||||||
from sonic_platform.component import Component
|
from sonic_platform.watchdog import Watchdog, WatchdogTCO
|
||||||
from sonic_platform.watchdog import Watchdog
|
from sonic_platform.sfp import Sfp
|
||||||
from sonic_platform.eeprom import Eeprom
|
|
||||||
import time
|
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ MAX_S6100_MODULE = 4
|
|||||||
MAX_S6100_FANTRAY = 4
|
MAX_S6100_FANTRAY = 4
|
||||||
MAX_S6100_PSU = 2
|
MAX_S6100_PSU = 2
|
||||||
MAX_S6100_THERMAL = 10
|
MAX_S6100_THERMAL = 10
|
||||||
MAX_S6100_COMPONENT = 3
|
MAX_S6100_COMPONENT = 5
|
||||||
|
|
||||||
|
|
||||||
class Chassis(ChassisBase):
|
class Chassis(ChassisBase):
|
||||||
@ -64,6 +65,8 @@ class Chassis(ChassisBase):
|
|||||||
'amber': 0x02, 'blinking amber': 0x08
|
'amber': 0x02, 'blinking amber': 0x08
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_global_port_pres_dict = {}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
ChassisBase.__init__(self)
|
ChassisBase.__init__(self)
|
||||||
@ -75,6 +78,7 @@ class Chassis(ChassisBase):
|
|||||||
module = Module(i)
|
module = Module(i)
|
||||||
self._module_list.append(module)
|
self._module_list.append(module)
|
||||||
self._sfp_list.extend(module._sfp_list)
|
self._sfp_list.extend(module._sfp_list)
|
||||||
|
|
||||||
#SFP ports
|
#SFP ports
|
||||||
sfp_port = 11
|
sfp_port = 11
|
||||||
for index in range(64,66):
|
for index in range(64,66):
|
||||||
@ -101,12 +105,26 @@ class Chassis(ChassisBase):
|
|||||||
component = Component(i)
|
component = Component(i)
|
||||||
self._component_list.append(component)
|
self._component_list.append(component)
|
||||||
|
|
||||||
|
for i in self._sfp_list:
|
||||||
|
presence = i.get_presence()
|
||||||
|
if presence:
|
||||||
|
self._global_port_pres_dict[i.index] = '1'
|
||||||
|
else:
|
||||||
|
self._global_port_pres_dict[i.index] = '0'
|
||||||
|
|
||||||
|
bios_ver = self.get_component(0).get_firmware_version()
|
||||||
|
bios_minor_ver = bios_ver.split("-")[-1]
|
||||||
|
if bios_minor_ver.isdigit() and (int(bios_minor_ver) >= 9):
|
||||||
|
self._watchdog = WatchdogTCO()
|
||||||
|
else:
|
||||||
self._watchdog = Watchdog()
|
self._watchdog = Watchdog()
|
||||||
|
|
||||||
self._transceiver_presence = self._get_transceiver_presence()
|
self._transceiver_presence = self._get_transceiver_presence()
|
||||||
|
|
||||||
def _get_reboot_reason_smf_register(self):
|
def _get_reboot_reason_smf_register(self):
|
||||||
# In S6100, mb_poweron_reason register will
|
# In S6100, mb_poweron_reason register will
|
||||||
# Returns 0xaa or 0xcc on software reload
|
# Returns 0xaa or 0xcc on software reload
|
||||||
|
# Returns 0x88 on cold-reboot happened during software reload
|
||||||
# Returns 0xff or 0xbb on power-cycle
|
# Returns 0xff or 0xbb on power-cycle
|
||||||
# Returns 0xdd on Watchdog
|
# Returns 0xdd on Watchdog
|
||||||
# Returns 0xee on Thermal Shutdown
|
# Returns 0xee on Thermal Shutdown
|
||||||
@ -236,6 +254,15 @@ class Chassis(ChassisBase):
|
|||||||
"""
|
"""
|
||||||
return self._eeprom.base_mac_addr()
|
return self._eeprom.base_mac_addr()
|
||||||
|
|
||||||
|
def get_revision(self):
|
||||||
|
"""
|
||||||
|
Retrieves the hardware revision of the device
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Revision value of device
|
||||||
|
"""
|
||||||
|
return self._eeprom.revision_str()
|
||||||
|
|
||||||
def get_system_eeprom_info(self):
|
def get_system_eeprom_info(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the full content of system EEPROM information for the chassis
|
Retrieves the full content of system EEPROM information for the chassis
|
||||||
@ -246,6 +273,19 @@ class Chassis(ChassisBase):
|
|||||||
"""
|
"""
|
||||||
return self._eeprom.system_eeprom_info()
|
return self._eeprom.system_eeprom_info()
|
||||||
|
|
||||||
|
def get_module_index(self, module_name):
|
||||||
|
"""
|
||||||
|
Retrieves module index from the module name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module_name: A string, prefixed by SUPERVISOR, LINE-CARD or FABRIC-CARD
|
||||||
|
Ex. SUPERVISOR0, LINE-CARD1, FABRIC-CARD5
|
||||||
|
Returns:
|
||||||
|
An integer, the index of the ModuleBase object in the module_list
|
||||||
|
"""
|
||||||
|
module_index = re.match(r'IOM([1-4])', module_name).group(1)
|
||||||
|
return int(module_index) - 1
|
||||||
|
|
||||||
def get_reboot_cause(self):
|
def get_reboot_cause(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the cause of the previous reboot
|
Retrieves the cause of the previous reboot
|
||||||
@ -265,6 +305,8 @@ class Chassis(ChassisBase):
|
|||||||
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
return (ChassisBase.REBOOT_CAUSE_POWER_LOSS, None)
|
||||||
elif ((smf_mb_reg_reason == 0xaa) or (smf_mb_reg_reason == 0xcc)):
|
elif ((smf_mb_reg_reason == 0xaa) or (smf_mb_reg_reason == 0xcc)):
|
||||||
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
return (ChassisBase.REBOOT_CAUSE_NON_HARDWARE, None)
|
||||||
|
elif (smf_mb_reg_reason == 0x88):
|
||||||
|
return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "CPU Reset")
|
||||||
elif (smf_mb_reg_reason == 0xdd):
|
elif (smf_mb_reg_reason == 0xdd):
|
||||||
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
return (ChassisBase.REBOOT_CAUSE_WATCHDOG, None)
|
||||||
elif (smf_mb_reg_reason == 0xee):
|
elif (smf_mb_reg_reason == 0xee):
|
||||||
|
@ -10,13 +10,20 @@
|
|||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tarfile
|
||||||
from sonic_platform_base.component_base import ComponentBase
|
from sonic_platform_base.component_base import ComponentBase
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
BIOS_QUERY_VERSION_COMMAND = "dmidecode -s system-version"
|
BIOS_QUERY_VERSION_COMMAND = "dmidecode -s system-version"
|
||||||
|
SSD_VERSION_COMMAND = "ssdutil -v"
|
||||||
|
SSD_UPGRADE_SCHEDULE = "/usr/local/bin/ssd_upgrade_schedule"
|
||||||
|
PCI_VERSION_COMMAND = "lspci -s 0:0.0"
|
||||||
|
ONIE_VERSION_COMMAND = "/usr/local/bin/onie_version"
|
||||||
|
|
||||||
|
|
||||||
class Component(ComponentBase):
|
class Component(ComponentBase):
|
||||||
@ -29,9 +36,11 @@ class Component(ComponentBase):
|
|||||||
CHASSIS_COMPONENTS = [
|
CHASSIS_COMPONENTS = [
|
||||||
["BIOS", ("Performs initialization of hardware components during "
|
["BIOS", ("Performs initialization of hardware components during "
|
||||||
"booting")],
|
"booting")],
|
||||||
["CPLD", "Used for managing IO modules, SFP+ modules and system LEDs"],
|
|
||||||
["FPGA", ("Platform management controller for on-board temperature "
|
["FPGA", ("Platform management controller for on-board temperature "
|
||||||
"monitoring, in-chassis power, Fan and LED control")]
|
"monitoring, in-chassis power, Fan and LED control")],
|
||||||
|
["CPLD", "Used for managing IO modules, SFP+ modules and system LEDs"],
|
||||||
|
["SSD", "Solid State Drive that stores data persistently"],
|
||||||
|
["ONIE", "Open Network Install Environment"]
|
||||||
]
|
]
|
||||||
MODULE_COMPONENT = [
|
MODULE_COMPONENT = [
|
||||||
"IOM{}-CPLD",
|
"IOM{}-CPLD",
|
||||||
@ -125,6 +134,38 @@ class Component(ComponentBase):
|
|||||||
else:
|
else:
|
||||||
return 'NA'
|
return 'NA'
|
||||||
|
|
||||||
|
def _get_ssd_version(self):
|
||||||
|
rv = 'NA'
|
||||||
|
ssd_ver = self._get_command_result(SSD_VERSION_COMMAND)
|
||||||
|
if not ssd_ver:
|
||||||
|
return rv
|
||||||
|
else:
|
||||||
|
version = re.search(r'Firmware\s*:(.*)',ssd_ver)
|
||||||
|
if version:
|
||||||
|
rv = version.group(1).strip()
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def _get_available_firmware_version(self, image_path):
|
||||||
|
if not os.path.isfile(image_path):
|
||||||
|
return False, "ERROR: File not found"
|
||||||
|
|
||||||
|
try:
|
||||||
|
updater = tarfile.open(image_path, "r")
|
||||||
|
except tarfile.ReadError:
|
||||||
|
return False, "ERROR: Unable to extract firmware updater"
|
||||||
|
|
||||||
|
try:
|
||||||
|
ver_info_fd = updater.extractfile("fw-component-version")
|
||||||
|
except KeyError:
|
||||||
|
updater.close()
|
||||||
|
return False, "ERROR: Version info not available"
|
||||||
|
|
||||||
|
ver_info = json.load(ver_info_fd)
|
||||||
|
ver_info_fd.close()
|
||||||
|
updater.close()
|
||||||
|
|
||||||
|
return True, ver_info
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the name of the component
|
Retrieves the name of the component
|
||||||
@ -216,10 +257,58 @@ class Component(ComponentBase):
|
|||||||
else:
|
else:
|
||||||
return bios_ver
|
return bios_ver
|
||||||
|
|
||||||
elif self.index == 1: # SwitchCard CPLD
|
elif self.index == 1: # FPGA
|
||||||
return self._get_cpld_version()
|
|
||||||
elif self.index == 2: # FPGA
|
|
||||||
return self._get_fpga_version()
|
return self._get_fpga_version()
|
||||||
|
elif self.index == 2: # SwitchCard CPLD
|
||||||
|
return self._get_cpld_version()
|
||||||
|
elif self.index == 3: #SSD
|
||||||
|
return self._get_ssd_version()
|
||||||
|
elif self.index == 4: # ONIE
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(ONIE_VERSION_COMMAND, text=True).strip()
|
||||||
|
except (FileNotFoundError, subprocess.CalledProcessError):
|
||||||
|
return 'NA'
|
||||||
|
|
||||||
|
def get_available_firmware_version(self, image_path):
|
||||||
|
"""
|
||||||
|
Retrieves the available firmware version of the component
|
||||||
|
|
||||||
|
Note: the firmware version will be read from image
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path: A string, path to firmware image
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the available firmware version of the component
|
||||||
|
"""
|
||||||
|
avail_ver = None
|
||||||
|
if self.index == 2: # SwitchCard CPLD
|
||||||
|
valid, version = self._get_available_firmware_version(image_path)
|
||||||
|
pci_ver = self._get_command_result(PCI_VERSION_COMMAND)
|
||||||
|
if valid:
|
||||||
|
if pci_ver:
|
||||||
|
board_ver = re.search(r"\(rev ([0-9]{2})\)$", pci_ver)
|
||||||
|
if board_ver:
|
||||||
|
board_ver = board_ver.group(1).strip()
|
||||||
|
board_type = 'B0' if board_ver == '02' else 'C0'
|
||||||
|
cpld_ver = self._get_cpld_version()
|
||||||
|
avail_ver = version.get(board_type) if board_type == 'B0' else cpld_ver
|
||||||
|
else:
|
||||||
|
print(version)
|
||||||
|
|
||||||
|
elif self.index == 3: # SSD
|
||||||
|
valid, version = self._get_available_firmware_version(image_path)
|
||||||
|
ssd_ver = self._get_command_result(SSD_VERSION_COMMAND)
|
||||||
|
if valid:
|
||||||
|
if ssd_ver:
|
||||||
|
ssd_model = re.search(r'Device Model\s*:.*(3IE[3]{0,1})', ssd_ver)
|
||||||
|
if ssd_model:
|
||||||
|
ssd_model = ssd_model.group(1).strip()
|
||||||
|
avail_ver = version.get(ssd_model)
|
||||||
|
else:
|
||||||
|
print(version)
|
||||||
|
|
||||||
|
return avail_ver if avail_ver else "NA"
|
||||||
|
|
||||||
def install_firmware(self, image_path):
|
def install_firmware(self, image_path):
|
||||||
"""
|
"""
|
||||||
@ -232,3 +321,16 @@ class Component(ComponentBase):
|
|||||||
A boolean, True if install was successful, False if not
|
A boolean, True if install was successful, False if not
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def update_firmware(self,image_path):
|
||||||
|
"""
|
||||||
|
Updates firmware to the componenent
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path: A string, path to firmware image
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if install was successful, False if not
|
||||||
|
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
@ -159,3 +159,13 @@ class FanDrawer(FanDrawerBase):
|
|||||||
return self.STATUS_LED_COLOR_AMBER
|
return self.STATUS_LED_COLOR_AMBER
|
||||||
else:
|
else:
|
||||||
return self.STATUS_LED_COLOR_OFF
|
return self.STATUS_LED_COLOR_OFF
|
||||||
|
|
||||||
|
def get_maximum_consumed_power(self):
|
||||||
|
"""
|
||||||
|
Retrives the maximum power drawn by Fan Drawer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float, with value of the maximum consumable power of the
|
||||||
|
component.
|
||||||
|
"""
|
||||||
|
return 54.0
|
||||||
|
@ -198,3 +198,49 @@ class Module(ModuleBase):
|
|||||||
‘0x26’:’01’, ‘0x27’:’REV01’, ‘0x28’:’AG9064-C2358-16G’}
|
‘0x26’:’01’, ‘0x27’:’REV01’, ‘0x28’:’AG9064-C2358-16G’}
|
||||||
"""
|
"""
|
||||||
return self._eeprom.system_eeprom_info()
|
return self._eeprom.system_eeprom_info()
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
"""
|
||||||
|
Retrieves the platform vendor's product description of the module
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string, providing the vendor's product description of the module.
|
||||||
|
"""
|
||||||
|
return self._eeprom.modelstr()
|
||||||
|
|
||||||
|
def get_slot(self):
|
||||||
|
"""
|
||||||
|
Retrieves the platform vendor's slot number of the module
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An integer, indicating the slot number in the chassis
|
||||||
|
"""
|
||||||
|
return self.index
|
||||||
|
|
||||||
|
def get_oper_status(self):
|
||||||
|
"""
|
||||||
|
Retrieves the operational status of the module
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string, the operational status of the module from one of the
|
||||||
|
predefined status values: MODULE_STATUS_EMPTY, MODULE_STATUS_OFFLINE,
|
||||||
|
MODULE_STATUS_FAULT, MODULE_STATUS_PRESENT or MODULE_STATUS_ONLINE
|
||||||
|
"""
|
||||||
|
if self.get_presence():
|
||||||
|
if self.get_status():
|
||||||
|
return self.MODULE_STATUS_ONLINE
|
||||||
|
else:
|
||||||
|
return self.MODULE_STATUS_PRESENT
|
||||||
|
else:
|
||||||
|
return self.MODULE_STATUS_EMPTY
|
||||||
|
|
||||||
|
def get_maximum_consumed_power(self):
|
||||||
|
"""
|
||||||
|
Retrives the maximum power drawn by this module
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float, with value of the maximum consumable power of the
|
||||||
|
module.
|
||||||
|
"""
|
||||||
|
return 97.23
|
||||||
|
|
||||||
|
@ -34,11 +34,13 @@ class Psu(PsuBase):
|
|||||||
self.psu_voltage_reg = "in30_input"
|
self.psu_voltage_reg = "in30_input"
|
||||||
self.psu_current_reg = "curr602_input"
|
self.psu_current_reg = "curr602_input"
|
||||||
self.psu_power_reg = "power2_input"
|
self.psu_power_reg = "power2_input"
|
||||||
|
self.psu_maxpower_reg = "power2_max"
|
||||||
self.psu_temperature_reg = "temp14_input"
|
self.psu_temperature_reg = "temp14_input"
|
||||||
elif self.index == 2:
|
elif self.index == 2:
|
||||||
self.psu_voltage_reg = "in32_input"
|
self.psu_voltage_reg = "in32_input"
|
||||||
self.psu_current_reg = "curr702_input"
|
self.psu_current_reg = "curr702_input"
|
||||||
self.psu_power_reg = "power4_input"
|
self.psu_power_reg = "power4_input"
|
||||||
|
self.psu_maxpower_reg = "power4_max"
|
||||||
self.psu_temperature_reg = "temp15_input"
|
self.psu_temperature_reg = "temp15_input"
|
||||||
|
|
||||||
# Passing True to specify it is a PSU fan
|
# Passing True to specify it is a PSU fan
|
||||||
@ -124,6 +126,19 @@ class Psu(PsuBase):
|
|||||||
|
|
||||||
return psu_serialno
|
return psu_serialno
|
||||||
|
|
||||||
|
def get_revision(self):
|
||||||
|
"""
|
||||||
|
Retrieves the hardware revision of the device
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
string: Revision value of device
|
||||||
|
"""
|
||||||
|
serial = self.get_serial()
|
||||||
|
if serial != "NA" and len(serial) == 28:
|
||||||
|
return serial[-3:]
|
||||||
|
else:
|
||||||
|
return "NA"
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the operational status of the PSU
|
Retrieves the operational status of the PSU
|
||||||
@ -195,6 +210,24 @@ class Psu(PsuBase):
|
|||||||
|
|
||||||
return psu_power
|
return psu_power
|
||||||
|
|
||||||
|
def get_maximum_supplied_power(self):
|
||||||
|
"""
|
||||||
|
Retrieves the maximum supplied power by PSU
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A float number, the maximum power output in Watts.
|
||||||
|
e.g. 1200.1
|
||||||
|
"""
|
||||||
|
psu_maxpower = self._get_pmc_register(self.psu_maxpower_reg)
|
||||||
|
if (psu_maxpower != 'ERR') and self.get_presence():
|
||||||
|
# Converting the value returned by driver which is in
|
||||||
|
# microwatts to watts
|
||||||
|
psu_maxpower = float(psu_maxpower) / 1000000
|
||||||
|
else:
|
||||||
|
psu_maxpower = 0.0
|
||||||
|
|
||||||
|
return psu_maxpower
|
||||||
|
|
||||||
def get_powergood_status(self):
|
def get_powergood_status(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the powergood status of PSU
|
Retrieves the powergood status of PSU
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
#
|
#
|
||||||
# DELLEMC S6100
|
# DELLEMC S6100
|
||||||
@ -10,13 +8,30 @@
|
|||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import array
|
||||||
|
import ctypes
|
||||||
|
import fcntl
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import ctypes
|
|
||||||
from sonic_platform_base.watchdog_base import WatchdogBase
|
from sonic_platform_base.watchdog_base import WatchdogBase
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
# ioctl constants
|
||||||
|
IOC_WRITE = 0x40000000
|
||||||
|
IOC_READ = 0x80000000
|
||||||
|
IOC_SIZE_INT = 0x00040000
|
||||||
|
|
||||||
|
WATCHDOG_IOCTL_BASE = ord('W')
|
||||||
|
|
||||||
|
WDIOC_SETOPTIONS = IOC_READ | IOC_SIZE_INT | (WATCHDOG_IOCTL_BASE << 8) | 4
|
||||||
|
WDIOC_KEEPALIVE = IOC_READ | IOC_SIZE_INT | (WATCHDOG_IOCTL_BASE << 8) | 5
|
||||||
|
WDIOC_SETTIMEOUT = IOC_READ | IOC_WRITE | IOC_SIZE_INT | (WATCHDOG_IOCTL_BASE << 8) | 6
|
||||||
|
|
||||||
|
WDIOS_DISABLECARD = 0x0001
|
||||||
|
WDIOS_ENABLECARD = 0x0002
|
||||||
|
|
||||||
|
|
||||||
class _timespec(ctypes.Structure):
|
class _timespec(ctypes.Structure):
|
||||||
_fields_ = [
|
_fields_ = [
|
||||||
@ -24,6 +39,7 @@ class _timespec(ctypes.Structure):
|
|||||||
('tv_nsec', ctypes.c_long)
|
('tv_nsec', ctypes.c_long)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Watchdog(WatchdogBase):
|
class Watchdog(WatchdogBase):
|
||||||
"""
|
"""
|
||||||
Abstract base class for interfacing with a hardware watchdog module
|
Abstract base class for interfacing with a hardware watchdog module
|
||||||
@ -226,3 +242,142 @@ class Watchdog(WatchdogBase):
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
class WatchdogTCO(WatchdogBase):
|
||||||
|
"""
|
||||||
|
Watchdog class for interfacing with iTCO watchdog
|
||||||
|
"""
|
||||||
|
|
||||||
|
IDENTITY = "iTCO_wdt"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.dev = None
|
||||||
|
self.dev_name = None
|
||||||
|
wd_sysfs_path = "/sys/class/watchdog"
|
||||||
|
|
||||||
|
for dev_file in glob.glob("/dev/watchdog*"):
|
||||||
|
dev = os.path.basename(dev_file)
|
||||||
|
dev_identity = self._read_file("{}/{}/identity".format(wd_sysfs_path, dev))
|
||||||
|
if dev_identity == self.IDENTITY:
|
||||||
|
self.dev_name = dev
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.dev_name is None:
|
||||||
|
raise RuntimeError("{} is not initialized".format(self.IDENTITY))
|
||||||
|
|
||||||
|
self.state_file = "{}/{}/state".format(wd_sysfs_path, self.dev_name)
|
||||||
|
self.timeout_file = "{}/{}/timeout".format(wd_sysfs_path, self.dev_name)
|
||||||
|
self.timeleft_file = "{}/{}/timeleft".format(wd_sysfs_path, self.dev_name)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.dev is not None:
|
||||||
|
os.close(self.dev)
|
||||||
|
|
||||||
|
def _ioctl(self, request, arg=0, mutate_flag=True):
|
||||||
|
"""
|
||||||
|
Perform ioctl on watchdog device
|
||||||
|
"""
|
||||||
|
self._open_wd_dev()
|
||||||
|
fcntl.ioctl(self.dev, request, arg, mutate_flag)
|
||||||
|
|
||||||
|
def _open_wd_dev(self):
|
||||||
|
"""
|
||||||
|
Open watchdog device file
|
||||||
|
"""
|
||||||
|
if self.dev is None:
|
||||||
|
wd_dev = "/dev/{}".format(self.dev_name)
|
||||||
|
self.dev = os.open(wd_dev, os.O_RDWR)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _read_file(file_path):
|
||||||
|
"""
|
||||||
|
Read a file
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(file_path, "r") as fd:
|
||||||
|
read_str = fd.read()
|
||||||
|
except OSError:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
return read_str.strip()
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
if seconds < 0 or seconds > 0x3ff:
|
||||||
|
return -1
|
||||||
|
if seconds < 4:
|
||||||
|
seconds = 4
|
||||||
|
|
||||||
|
try:
|
||||||
|
timeout = int(self._read_file(self.timeout_file))
|
||||||
|
if timeout != seconds:
|
||||||
|
buf = array.array('I', [seconds])
|
||||||
|
self._ioctl(WDIOC_SETTIMEOUT, buf)
|
||||||
|
timeout = int(buf[0])
|
||||||
|
|
||||||
|
if self.is_armed():
|
||||||
|
self._ioctl(WDIOC_KEEPALIVE)
|
||||||
|
else:
|
||||||
|
buf = array.array('h', [WDIOS_ENABLECARD])
|
||||||
|
self._ioctl(WDIOC_SETOPTIONS, buf, False)
|
||||||
|
except OSError:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
return timeout
|
||||||
|
|
||||||
|
def disarm(self):
|
||||||
|
"""
|
||||||
|
Disarm the hardware watchdog
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if watchdog is disarmed successfully, False
|
||||||
|
if not
|
||||||
|
"""
|
||||||
|
disarmed = True
|
||||||
|
if self.is_armed():
|
||||||
|
try:
|
||||||
|
buf = array.array('h', [WDIOS_DISABLECARD])
|
||||||
|
self._ioctl(WDIOC_SETOPTIONS, buf, False)
|
||||||
|
except OSError:
|
||||||
|
disarmed = False
|
||||||
|
|
||||||
|
return disarmed
|
||||||
|
|
||||||
|
def is_armed(self):
|
||||||
|
"""
|
||||||
|
Retrieves the armed state of the hardware watchdog.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean, True if watchdog is armed, False if not
|
||||||
|
"""
|
||||||
|
state = self._read_file(self.state_file)
|
||||||
|
return state == "active"
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
timeleft = -1
|
||||||
|
if self.is_armed():
|
||||||
|
timeleft = int(self._read_file(self.timeleft_file))
|
||||||
|
|
||||||
|
return timeleft
|
||||||
|
Reference in New Issue
Block a user