diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py index 25668e2113..28e29e67dc 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py @@ -20,12 +20,15 @@ try: from sonic_platform.psu import Psu from sonic_platform.device import Device from sonic_platform.component import Component + from sonic_platform.watchdog import Watchdog except ImportError as e: raise ImportError(str(e) + "- required module not found") CONFIG_DB_PATH = "/etc/sonic/config_db.json" NUM_FAN = 3 NUM_PSU = 2 +RESET_REGISTER = "0x112" +REBOOT_CAUSE_PATH = "/host/reboot-cause/previous-reboot-cause.txt" class Chassis(ChassisBase): @@ -42,6 +45,7 @@ class Chassis(ChassisBase): ChassisBase.__init__(self) self._component_device = Device("component") self._component_name_list = self._component_device.get_name_list() + self._watchdog = Watchdog() def __read_config_db(self): try: @@ -51,6 +55,14 @@ class Chassis(ChassisBase): except IOError: raise IOError("Unable to open config_db file !") + def __read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + return data.strip() + except IOError: + raise IOError("Unable to open %s file !" % file_path) + def get_base_mac(self): """ Retrieves the base MAC address for the chassis @@ -63,7 +75,7 @@ class Chassis(ChassisBase): base_mac = self.config_data["DEVICE_METADATA"]["localhost"]["mac"] return str(base_mac) except KeyError: - raise KeyError("Base MAC not found") + return str(None) def get_firmware_version(self, component_name): """ @@ -94,3 +106,32 @@ class Chassis(ChassisBase): if component_name not in self._component_name_list: return False return self.component.upgrade_firmware(image_path) + + 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. + """ + self.component = Component("SMC_CPLD") + description = 'None' + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + hw_reboot_cause = self.component.get_register_value(RESET_REGISTER) + sw_reboot_cause = self.__read_txt_file(REBOOT_CAUSE_PATH) + + if sw_reboot_cause != "Unexpected reboot": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + elif hw_reboot_cause == "0x11": + reboot_cause = self.REBOOT_CAUSE_POWER_LOSS + elif hw_reboot_cause == "0x33" or hw_reboot_cause == "0x55": + reboot_cause = self.REBOOT_CAUSE_WATCHDOG + else: + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + description = 'Unknown reason' + + return (reboot_cause, description) diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py index 7a3e05b482..b80deabb17 100644 --- a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py @@ -23,7 +23,7 @@ MMC_CPLD_ADDR = '0x100' BIOS_VERSION_PATH = "/sys/class/dmi/id/bios_version" CONFIG_DB_PATH = "/etc/sonic/config_db.json" SMC_CPLD_PATH = "/sys/devices/platform/e1031.smc/version" -MMC_CPLD_PATH = "/sys/devices/platform/e1031.smc/getreg" +GETREG_PATH = "/sys/devices/platform/e1031.smc/getreg" class Component(DeviceBase): @@ -51,16 +51,6 @@ class Component(DeviceBase): return False return True - def __get_register_value(self, path, register): - # Retrieves the cpld register value - cmd = "echo {1} > {0}; cat {0}".format(path, register) - p = subprocess.Popen( - cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - raw_data, err = p.communicate() - if err is not '': - return None - return raw_data.strip() - def __get_bios_version(self): # Retrieves the BIOS firmware version try: @@ -70,6 +60,16 @@ class Component(DeviceBase): except Exception as e: return None + def get_register_value(self, register): + # Retrieves the cpld register value + cmd = "echo {1} > {0}; cat {0}".format(GETREG_PATH, register) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err is not '': + return None + return raw_data.strip() + def __get_cpld_version(self): # Retrieves the CPLD firmware version cpld_version = dict() @@ -78,8 +78,7 @@ class Component(DeviceBase): smc_cpld_version = 'None' if smc_cpld_version is 'None' else "{}.{}".format( int(smc_cpld_version[2], 16), int(smc_cpld_version[3], 16)) - mmc_cpld_version = self.__get_register_value( - MMC_CPLD_PATH, MMC_CPLD_ADDR) + mmc_cpld_version = self.get_register_value(MMC_CPLD_ADDR) mmc_cpld_version = 'None' if mmc_cpld_version is 'None' else "{}.{}".format( int(mmc_cpld_version[2], 16), int(mmc_cpld_version[3], 16)) diff --git a/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py new file mode 100644 index 0000000000..b539bc06f6 --- /dev/null +++ b/device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Watchdog contains an implementation of SONiC Platform Base API +# +############################################################################# +import ctypes +import fcntl +import os +import subprocess +import time +import array + +try: + from sonic_platform_base.watchdog_base import WatchdogBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +""" ioctl constants """ +IO_WRITE = 0x40000000 +IO_READ = 0x80000000 +IO_READ_WRITE = 0xC0000000 +IO_SIZE_INT = 0x00040000 +IO_SIZE_40 = 0x00280000 +IO_TYPE_WATCHDOG = ord('W') << 8 + +WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG +WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG +WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG + +""" Watchdog ioctl commands """ +WDIOC_GETSUPPORT = 0 | WDR_40 +WDIOC_GETSTATUS = 1 | WDR_INT +WDIOC_GETBOOTSTATUS = 2 | WDR_INT +WDIOC_GETTEMP = 3 | WDR_INT +WDIOC_SETOPTIONS = 4 | WDR_INT +WDIOC_KEEPALIVE = 5 | WDR_INT +WDIOC_SETTIMEOUT = 6 | WDWR_INT +WDIOC_GETTIMEOUT = 7 | WDR_INT +WDIOC_SETPRETIMEOUT = 8 | WDWR_INT +WDIOC_GETPRETIMEOUT = 9 | WDR_INT +WDIOC_GETTIMELEFT = 10 | WDR_INT + +""" Watchdog status constants """ +WDIOS_DISABLECARD = 0x0001 +WDIOS_ENABLECARD = 0x0002 + +WDT_COMMON_ERROR = -1 +WD_MAIN_IDENTITY = "iTCO_wdt" +WDT_SYSFS_PATH = "/sys/class/watchdog/" + + +class Watchdog(WatchdogBase): + + def __init__(self): + + self.watchdog, self.wdt_main_dev_name = self._get_wdt() + self.status_path = "/sys/class/watchdog/%s/status" % self.wdt_main_dev_name + self.state_path = "/sys/class/watchdog/%s/state" % self.wdt_main_dev_name + self.timeout_path = "/sys/class/watchdog/%s/timeout" % self.wdt_main_dev_name + # Set default value + self._disable() + self.armed = False + self.timeout = self._gettimeout(self.timeout_path) + + def _is_wd_main(self, dev): + """ + Checks watchdog identity + """ + identity = self._read_file( + "{}/{}/identity".format(WDT_SYSFS_PATH, dev)) + return identity == WD_MAIN_IDENTITY + + def _get_wdt(self): + """ + Retrieves watchdog device + """ + wdt_main_dev_list = [dev for dev in os.listdir( + "/dev/") if dev.startswith("watchdog") and self._is_wd_main(dev)] + if not wdt_main_dev_list: + return None + wdt_main_dev_name = wdt_main_dev_list[0] + watchdog_device_path = "/dev/{}".format(wdt_main_dev_name) + watchdog = os.open(watchdog_device_path, os.O_RDWR) + return watchdog, wdt_main_dev_name + + def _read_file(self, file_path): + """ + Read text file + """ + try: + with open(file_path, "r") as fd: + txt = fd.read() + except IOError: + return WDT_COMMON_ERROR + return txt.strip() + + def _enable(self): + """ + Turn on the watchdog timer + """ + req = array.array('h', [WDIOS_ENABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _disable(self): + """ + Turn off the watchdog timer + """ + req = array.array('h', [WDIOS_DISABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _keepalive(self): + """ + Keep alive watchdog timer + """ + fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE) + + def _settimeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + req = array.array('I', [seconds]) + fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True) + return int(req[0]) + + def _gettimeout(self, timeout_path): + """ + Get watchdog timeout + @return watchdog timeout + """ + req = array.array('I', [0]) + fcntl.ioctl(self.watchdog, WDIOC_GETTIMEOUT, req, True) + + return int(req[0]) + + def _gettimeleft(self): + """ + Get time left before watchdog timer expires + @return time left in seconds + """ + req = array.array('I', [0]) + fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True) + + return int(req[0]) + + ################################################################# + + def arm(self, seconds): + """ + Arm the hardware watchdog with a timeout of 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 , 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. + """ + + ret = WDT_COMMON_ERROR + if seconds < 0: + return ret + + try: + if self.timeout != seconds: + self.timeout = self._settimeout(seconds) + if self.armed: + self._keepalive() + else: + self._enable() + self.armed = True + ret = self.timeout + except IOError as e: + pass + + return ret + + def disarm(self): + """ + Disarm the hardware watchdog + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + disarmed = False + if self.is_armed(): + try: + self._disable() + self.armed = False + disarmed = True + except IOError: + pass + + 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 + """ + + return self.armed + + 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 thei + watchdog timer. If the watchdog is not armed, returns -1. + """ + + timeleft = WDT_COMMON_ERROR + + if self.armed: + try: + timeleft = self._gettimeleft() + except IOError: + pass + + return timeleft + + def __del__(self): + """ + Close watchdog + """ + + os.close(self.watchdog) diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py index 91e456e094..8f5926df33 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/chassis.py @@ -20,12 +20,15 @@ try: from sonic_platform.psu import Psu from sonic_platform.device import Device from sonic_platform.component import Component + from sonic_platform.watchdog import Watchdog except ImportError as e: raise ImportError(str(e) + "- required module not found") CONFIG_DB_PATH = "/etc/sonic/config_db.json" NUM_FAN = 5 NUM_PSU = 2 +RESET_REGISTER = "0x103" +REBOOT_CAUSE_PATH = "/host/reboot-cause/previous-reboot-cause.txt" class Chassis(ChassisBase): @@ -42,6 +45,7 @@ class Chassis(ChassisBase): ChassisBase.__init__(self) self._component_device = Device("component") self._component_name_list = self._component_device.get_name_list() + self._watchdog = Watchdog() def __read_config_db(self): try: @@ -51,6 +55,14 @@ class Chassis(ChassisBase): except IOError: raise IOError("Unable to open config_db file !") + def __read_txt_file(self, file_path): + try: + with open(file_path, 'r') as fd: + data = fd.read() + return data.strip() + except IOError: + raise IOError("Unable to open %s file !" % file_path) + def get_base_mac(self): """ Retrieves the base MAC address for the chassis @@ -63,7 +75,7 @@ class Chassis(ChassisBase): base_mac = self.config_data["DEVICE_METADATA"]["localhost"]["mac"] return str(base_mac) except KeyError: - raise KeyError("Base MAC not found") + return str(None) def get_firmware_version(self, component_name): """ @@ -94,3 +106,33 @@ class Chassis(ChassisBase): if component_name not in self._component_name_list: return False return self.component.upgrade_firmware(image_path) + + 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. + """ + self.component = Component("CPLD1") + description = 'None' + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + hw_reboot_cause = self.component.get_register_value(RESET_REGISTER) + sw_reboot_cause = self.__read_txt_file(REBOOT_CAUSE_PATH) + + if sw_reboot_cause != "Unexpected reboot": + reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE + description = sw_reboot_cause + elif hw_reboot_cause == "0x11": + reboot_cause = self.REBOOT_CAUSE_POWER_LOSS + elif hw_reboot_cause == "0x22": + reboot_cause = self.REBOOT_CAUSE_WATCHDOG, + else: + reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER + description = 'Unknown reason' + + return (reboot_cause, description) diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py index b85bab7432..d78adf7389 100644 --- a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/component.py @@ -55,16 +55,6 @@ class Component(DeviceBase): return False return True - def __get_register_value(self, path, register): - # Retrieves the cpld register value - cmd = "echo {1} > {0}; cat {0}".format(path, register) - p = subprocess.Popen( - cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - raw_data, err = p.communicate() - if err is not '': - return None - return raw_data.strip() - def __get_bios_version(self): # Retrieves the BIOS firmware version try: @@ -74,14 +64,23 @@ class Component(DeviceBase): except Exception as e: return None + def get_register_value(self, register): + # Retrieves the cpld register value + cmd = "echo {1} > {0}; cat {0}".format(GETREG_PATH, register) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err is not '': + return None + return raw_data.strip() + def __get_cpld_version(self): # Retrieves the CPLD firmware version cpld_version = dict() for cpld_name in CPLD_ADDR_MAPPING: try: cpld_addr = CPLD_ADDR_MAPPING[cpld_name] - cpld_version_raw = self.__get_register_value( - GETREG_PATH, cpld_addr) + cpld_version_raw = self.get_register_value(cpld_addr) cpld_version_str = "{}.{}".format(int(cpld_version_raw[2], 16), int( cpld_version_raw[3], 16)) if cpld_version_raw is not None else 'None' cpld_version[cpld_name] = cpld_version_str diff --git a/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py new file mode 100644 index 0000000000..b539bc06f6 --- /dev/null +++ b/device/celestica/x86_64-cel_seastone-r0/sonic_platform/watchdog.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Watchdog contains an implementation of SONiC Platform Base API +# +############################################################################# +import ctypes +import fcntl +import os +import subprocess +import time +import array + +try: + from sonic_platform_base.watchdog_base import WatchdogBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +""" ioctl constants """ +IO_WRITE = 0x40000000 +IO_READ = 0x80000000 +IO_READ_WRITE = 0xC0000000 +IO_SIZE_INT = 0x00040000 +IO_SIZE_40 = 0x00280000 +IO_TYPE_WATCHDOG = ord('W') << 8 + +WDR_INT = IO_READ | IO_SIZE_INT | IO_TYPE_WATCHDOG +WDR_40 = IO_READ | IO_SIZE_40 | IO_TYPE_WATCHDOG +WDWR_INT = IO_READ_WRITE | IO_SIZE_INT | IO_TYPE_WATCHDOG + +""" Watchdog ioctl commands """ +WDIOC_GETSUPPORT = 0 | WDR_40 +WDIOC_GETSTATUS = 1 | WDR_INT +WDIOC_GETBOOTSTATUS = 2 | WDR_INT +WDIOC_GETTEMP = 3 | WDR_INT +WDIOC_SETOPTIONS = 4 | WDR_INT +WDIOC_KEEPALIVE = 5 | WDR_INT +WDIOC_SETTIMEOUT = 6 | WDWR_INT +WDIOC_GETTIMEOUT = 7 | WDR_INT +WDIOC_SETPRETIMEOUT = 8 | WDWR_INT +WDIOC_GETPRETIMEOUT = 9 | WDR_INT +WDIOC_GETTIMELEFT = 10 | WDR_INT + +""" Watchdog status constants """ +WDIOS_DISABLECARD = 0x0001 +WDIOS_ENABLECARD = 0x0002 + +WDT_COMMON_ERROR = -1 +WD_MAIN_IDENTITY = "iTCO_wdt" +WDT_SYSFS_PATH = "/sys/class/watchdog/" + + +class Watchdog(WatchdogBase): + + def __init__(self): + + self.watchdog, self.wdt_main_dev_name = self._get_wdt() + self.status_path = "/sys/class/watchdog/%s/status" % self.wdt_main_dev_name + self.state_path = "/sys/class/watchdog/%s/state" % self.wdt_main_dev_name + self.timeout_path = "/sys/class/watchdog/%s/timeout" % self.wdt_main_dev_name + # Set default value + self._disable() + self.armed = False + self.timeout = self._gettimeout(self.timeout_path) + + def _is_wd_main(self, dev): + """ + Checks watchdog identity + """ + identity = self._read_file( + "{}/{}/identity".format(WDT_SYSFS_PATH, dev)) + return identity == WD_MAIN_IDENTITY + + def _get_wdt(self): + """ + Retrieves watchdog device + """ + wdt_main_dev_list = [dev for dev in os.listdir( + "/dev/") if dev.startswith("watchdog") and self._is_wd_main(dev)] + if not wdt_main_dev_list: + return None + wdt_main_dev_name = wdt_main_dev_list[0] + watchdog_device_path = "/dev/{}".format(wdt_main_dev_name) + watchdog = os.open(watchdog_device_path, os.O_RDWR) + return watchdog, wdt_main_dev_name + + def _read_file(self, file_path): + """ + Read text file + """ + try: + with open(file_path, "r") as fd: + txt = fd.read() + except IOError: + return WDT_COMMON_ERROR + return txt.strip() + + def _enable(self): + """ + Turn on the watchdog timer + """ + req = array.array('h', [WDIOS_ENABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _disable(self): + """ + Turn off the watchdog timer + """ + req = array.array('h', [WDIOS_DISABLECARD]) + fcntl.ioctl(self.watchdog, WDIOC_SETOPTIONS, req, False) + + def _keepalive(self): + """ + Keep alive watchdog timer + """ + fcntl.ioctl(self.watchdog, WDIOC_KEEPALIVE) + + def _settimeout(self, seconds): + """ + Set watchdog timer timeout + @param seconds - timeout in seconds + @return is the actual set timeout + """ + req = array.array('I', [seconds]) + fcntl.ioctl(self.watchdog, WDIOC_SETTIMEOUT, req, True) + return int(req[0]) + + def _gettimeout(self, timeout_path): + """ + Get watchdog timeout + @return watchdog timeout + """ + req = array.array('I', [0]) + fcntl.ioctl(self.watchdog, WDIOC_GETTIMEOUT, req, True) + + return int(req[0]) + + def _gettimeleft(self): + """ + Get time left before watchdog timer expires + @return time left in seconds + """ + req = array.array('I', [0]) + fcntl.ioctl(self.watchdog, WDIOC_GETTIMELEFT, req, True) + + return int(req[0]) + + ################################################################# + + def arm(self, seconds): + """ + Arm the hardware watchdog with a timeout of 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 , 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. + """ + + ret = WDT_COMMON_ERROR + if seconds < 0: + return ret + + try: + if self.timeout != seconds: + self.timeout = self._settimeout(seconds) + if self.armed: + self._keepalive() + else: + self._enable() + self.armed = True + ret = self.timeout + except IOError as e: + pass + + return ret + + def disarm(self): + """ + Disarm the hardware watchdog + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + disarmed = False + if self.is_armed(): + try: + self._disable() + self.armed = False + disarmed = True + except IOError: + pass + + 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 + """ + + return self.armed + + 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 thei + watchdog timer. If the watchdog is not armed, returns -1. + """ + + timeleft = WDT_COMMON_ERROR + + if self.armed: + try: + timeleft = self._gettimeleft() + except IOError: + pass + + return timeleft + + def __del__(self): + """ + Close watchdog + """ + + os.close(self.watchdog)