Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[device/celestica] Implement Watchdog APIs based on the new platform API #3123

Merged
merged 5 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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