Skip to content

Commit

Permalink
[device/celestica] Implement Watchdog APIs based on the new platform …
Browse files Browse the repository at this point in the history
…API (#3123)
  • Loading branch information
Wirut Getbamrung authored and jleveque committed Jul 8, 2019
1 parent 8329ce1 commit bd67200
Show file tree
Hide file tree
Showing 6 changed files with 574 additions and 27 deletions.
43 changes: 42 additions & 1 deletion device/celestica/x86_64-cel_e1031-r0/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
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):
Expand All @@ -42,6 +45,7 @@ def __init__(self):
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:
Expand All @@ -51,6 +55,14 @@ def __read_config_db(self):
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
Expand All @@ -63,7 +75,7 @@ def get_base_mac(self):
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):
"""
Expand Down Expand Up @@ -94,3 +106,32 @@ def install_component_firmware(self, component_name, image_path):
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)
25 changes: 12 additions & 13 deletions device/celestica/x86_64-cel_e1031-r0/sonic_platform/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
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):
Expand Down Expand Up @@ -51,16 +51,6 @@ def __run_command(self, command):
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:
Expand All @@ -70,6 +60,16 @@ def __get_bios_version(self):
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()
Expand All @@ -78,8 +78,7 @@ def __get_cpld_version(self):
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))

Expand Down
233 changes: 233 additions & 0 deletions device/celestica/x86_64-cel_e1031-r0/sonic_platform/watchdog.py
Original file line number Diff line number Diff line change
@@ -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> 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.
"""

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)
Loading

0 comments on commit bd67200

Please sign in to comment.