From d4bf78c9db4daf21497274a778748da4d8301866 Mon Sep 17 00:00:00 2001 From: Joe LeVeque Date: Mon, 3 Dec 2018 18:24:52 -0800 Subject: [PATCH] Add new platform management API. Currently with support for chassis, PSUs, fans and watchdog (#13) --- setup.py | 1 + sonic_platform_base/__init__.py | 0 sonic_platform_base/chassis_base.py | 223 +++++++++++++++++++++++++++ sonic_platform_base/device_base.py | 66 ++++++++ sonic_platform_base/fan_base.py | 89 +++++++++++ sonic_platform_base/module_base.py | 125 +++++++++++++++ sonic_platform_base/platform_base.py | 28 ++++ sonic_platform_base/psu_base.py | 45 ++++++ sonic_platform_base/watchdog_base.py | 56 +++++++ 9 files changed, 633 insertions(+) create mode 100644 sonic_platform_base/__init__.py create mode 100644 sonic_platform_base/chassis_base.py create mode 100644 sonic_platform_base/device_base.py create mode 100644 sonic_platform_base/fan_base.py create mode 100644 sonic_platform_base/module_base.py create mode 100644 sonic_platform_base/platform_base.py create mode 100644 sonic_platform_base/psu_base.py create mode 100644 sonic_platform_base/watchdog_base.py diff --git a/setup.py b/setup.py index a5d7bc4df5cd..52b60b1d1569 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ packages=[ 'sonic_eeprom', 'sonic_led', + 'sonic_platform_base', 'sonic_psu', 'sonic_sfp', ], diff --git a/sonic_platform_base/__init__.py b/sonic_platform_base/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sonic_platform_base/chassis_base.py b/sonic_platform_base/chassis_base.py new file mode 100644 index 000000000000..8a2a197f6481 --- /dev/null +++ b/sonic_platform_base/chassis_base.py @@ -0,0 +1,223 @@ +# +# chassis_base.py +# +# Base class for implementing a platform-specific class with which +# to interact with a chassis device in SONiC. +# + +import sys +from . import device_base + + +class ChassisBase(device_base.DeviceBase): + """ + Base class for interfacing with a platform chassis + """ + + # Possible reboot causes + REBOOT_CAUSE_POWER_LOSS = "power_loss" + REBOOT_CAUSE_THERMAL_OVERLOAD_CPU = "thermal_overload_cpu" + REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC = "thermal_overload_asic" + REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER = "thermal_overload_other" + REBOOT_CAUSE_INSUFFICIENT_FAN = "insufficient_fan" + REBOOT_CAUSE_WATCHDOG = "watchdog" + REBOOT_CAUSE_HARDWARE_OTHER = "hardware_other" + REBOOT_CAUSE_NON_HARDWARE = "non_hardware" + + # List of ModuleBase-derived objects representing all modules + # available on the chassis (for use with modular chassis) + _module_list = [] + + # List of FanBase-derived objects representing all fans + # available on the chassis + _fan_list = [] + + # List of PsuBase-derived objects representing all power supply units + # available on the chassis + _psu_list = [] + + # Object derived from WatchdogBase for interacting with hardware watchdog + _watchdog = None + + 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' + """ + raise NotImplementedError + + 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. + """ + raise NotImplementedError + + def get_component_versions(self): + """ + Retrieves platform-specific hardware/firmware versions for chassis + componenets such as BIOS, CPLD, FPGA, etc. + + Returns: + A string containing platform-specific component versions + """ + raise NotImplementedError + + ############################################## + # Module methods + ############################################## + + def get_num_modules(self): + """ + Retrieves the number of modules available on this chassis + + Returns: + An integer, the number of modules available on this chassis + """ + return len(self._module_list) + + def get_all_modules(self): + """ + Retrieves all modules available on this chassis + + Returns: + A list of objects derived from ModuleBase representing all + modules available on this chassis + """ + return self._module_list + + def get_module(self, index): + """ + Retrieves module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the module to + retrieve + + Returns: + An object dervied from ModuleBase representing the specified + module + """ + module = None + + try: + module = self._module_list[index] + except IndexError: + sys.stderr.write("Module index {} out of range (0-{})\n".format( + index, len(self._module_list)-1)) + + return module + + ############################################## + # Fan methods + ############################################## + + def get_num_fans(self): + """ + Retrieves the number of fans available on this chassis + + Returns: + An integer, the number of fan modules available on this chassis + """ + return len(self._fan_list) + + def get_all_fans(self): + """ + Retrieves all fan modules available on this chassis + + Returns: + A list of objects derived from FanBase representing all fan + modules available on this chassis + """ + return self._fan_list + + def get_fan(self, index): + """ + Retrieves fan module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the fan module to + retrieve + + Returns: + An object dervied from FanBase representing the specified fan + module + """ + fan = None + + try: + fan = self._fan_list[index] + except IndexError: + sys.stderr.write("Fan index {} out of range (0-{})\n".format( + index, len(self._fan_list)-1)) + + return fan + + ############################################## + # PSU methods + ############################################## + + def get_num_psus(self): + """ + Retrieves the number of power supply units available on this chassis + + Returns: + An integer, the number of power supply units available on this + chassis + """ + return len(self._psu_list) + + def get_all_psus(self): + """ + Retrieves all power supply units available on this chassis + + Returns: + A list of objects derived from PsuBase representing all power + supply units available on this chassis + """ + return self._psu_list + + def get_psu(self, index): + """ + Retrieves power supply unit represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the power supply unit to + retrieve + + Returns: + An object dervied from PsuBase representing the specified power + supply unit + """ + psu = None + + try: + psu = self._psu_list[index] + except IndexError: + sys.stderr.write("PSU index {} out of range (0-{})\n".format( + index, len(self._psu_list)-1)) + + return psu + + ############################################## + # Other methods + ############################################## + + def get_watchdog(self): + """ + Retreives hardware watchdog device on this chassis + + Returns: + An object derived from WatchdogBase representing the hardware + watchdog device + """ + return _watchdog diff --git a/sonic_platform_base/device_base.py b/sonic_platform_base/device_base.py new file mode 100644 index 000000000000..9fba57c5ca6c --- /dev/null +++ b/sonic_platform_base/device_base.py @@ -0,0 +1,66 @@ +# +# device_base.py +# +# Abstract base class for interfacing with a generic type of platform +# peripheral device in SONiC +# + +class DeviceBase(object): + """ + Abstract base class for interfacing with a generic type of platform + peripheral device + """ + + def get_presence(self): + """ + Retrieves the presence of the device + + Returns: + bool: True if device is present, False if not + """ + raise NotImplementedError + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + + Returns: + string: Model/part number of device + """ + raise NotImplementedError + + def get_serial(self): + """ + Retrieves the serial number of the device + + Returns: + string: Serial number of device + """ + raise NotImplementedError + + def get_status(self): + """ + Retrieves the operational status of the device + + Returns: + A boolean value, True if device is operating properly, False if not + """ + raise NotImplementedError + + def get_change_event(self, timeout=0): + """ + Returns a dictionary containing all devices which have experienced a + change + + Args: + timeout: Timeout in milliseconds (optional). If timeout == 0, + this method will block until a change is detected. + + Returns: + (bool, dict): + - True if call successful, False if not; + - Dict where key is device ID and value is device event, + status='1' represents device inserted, + status='0' represents device removed. Ex. {'0': '1', '1': '0'} + """ + raise NotImplementedError diff --git a/sonic_platform_base/fan_base.py b/sonic_platform_base/fan_base.py new file mode 100644 index 000000000000..3c8a7150a843 --- /dev/null +++ b/sonic_platform_base/fan_base.py @@ -0,0 +1,89 @@ +# +# fan_base.py +# +# Abstract base class for implementing a platform-specific class with which +# to interact with a fan module in SONiC +# + +from . import device_base + + +class FanBase(device_base.DeviceBase): + """ + Abstract base class for interfacing with a fan module + """ + + # Possible fan directions (relative to port-side of device) + FAN_DIRECTION_INTAKE = "intake" + FAN_DIRECTION_EXHAUST = "exhaust" + + # Possible fan status LED colors + STATUS_LED_COLOR_GREEN = "green" + STATUS_LED_COLOR_RED = "red" + STATUS_LED_COLOR_OFF = "off" + + def get_direction(self): + """ + Retrieves the direction of fan + + Returns: + A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST + depending on fan direction + """ + raise NotImplementedError + + def get_speed(self): + """ + Retrieves the speed of fan as a percentage of full speed + + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + """ + raise NotImplementedError + + def get_target_speed(self): + """ + Retrieves the target (expected) speed of the fan + + Returns: + An integer, the percentage of full fan speed, in the range 0 (off) + to 100 (full speed) + """ + raise NotImplementedError + + def get_speed_tolerance(self): + """ + Retrieves the speed tolerance of the fan + + Returns: + An integer, the percentage of variance from target speed which is + considered tolerable + """ + raise NotImplementedError + + def set_speed(self, speed): + """ + Sets the fan speed + + Args: + speed: An integer, the percentage of full fan speed to set fan to, + in the range 0 (off) to 100 (full speed) + + Returns: + A boolean, True if speed is set successfully, False if not + """ + raise NotImplementedError + + def set_status_led(self, color): + """ + Sets the state of the fan module status LED + + Args: + color: A string representing the color with which to set the + fan module status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + raise NotImplementedError diff --git a/sonic_platform_base/module_base.py b/sonic_platform_base/module_base.py new file mode 100644 index 000000000000..274507846f22 --- /dev/null +++ b/sonic_platform_base/module_base.py @@ -0,0 +1,125 @@ +# +# module_base.py +# +# Base class for implementing a platform-specific class with which +# to interact with a module (as used in a modular chassis) SONiC. +# + +import sys +from . import device_base + + +class ModuleBase(device_base.DeviceBase): + """ + Base class for interfacing with a module (supervisor module, line card + module, etc. (applicable for a modular chassis) + """ + + # List of FanBase-derived objects representing all fans + # available on the chassis + _fan_list = [] + + # List of PsuBase-derived objects representing all power supply units + # available on the chassis + _psu_list = [] + + 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' + """ + raise NotImplementedError + + ############################################## + # Fan module methods + ############################################## + + def get_num_fans(self): + """ + Retrieves the number of fan modules available on this chassis + + Returns: + An integer, the number of fan modules available on this chassis + """ + return len(self._fan_list) + + def get_all_fans(self): + """ + Retrieves all fan modules available on this chassis + + Returns: + A list of objects derived from FanBase representing all fan + modules available on this chassis + """ + return self._fan_list + + def get_fan(self, index): + """ + Retrieves fan module represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the fan module to + retrieve + + Returns: + An object dervied from FanBase representing the specified fan + module + """ + fan = None + + try: + fan = self._fan_list[index] + except IndexError: + sys.stderr.write("Fan index {} out of range (0-{})\n".format( + index, len(self._fan_list)-1)) + + return fan + + ############################################## + # PSU module methods + ############################################## + + def get_num_psus(self): + """ + Retrieves the number of power supply units available on this chassis + + Returns: + An integer, the number of power supply units available on this + chassis + """ + return len(self._psu_list) + + def get_all_psus(self): + """ + Retrieves all power supply units available on this chassis + + Returns: + A list of objects derived from PsuBase representing all power + supply units available on this chassis + """ + return self._psu_list + + def get_psu(self, index): + """ + Retrieves power supply unit represented by (0-based) index + + Args: + index: An integer, the index (0-based) of the power supply unit to + retrieve + + Returns: + An object dervied from PsuBase representing the specified power + supply unit + """ + psu = None + + try: + psu = self._psu_list[index] + except IndexError: + sys.stderr.write("PSU index {} out of range (0-{})\n".format( + index, len(self._psu_list)-1)) + + return psu diff --git a/sonic_platform_base/platform_base.py b/sonic_platform_base/platform_base.py new file mode 100644 index 000000000000..4a422161ce3b --- /dev/null +++ b/sonic_platform_base/platform_base.py @@ -0,0 +1,28 @@ +# +# platform_base.py +# +# Base class for implementing platform-specific functionality in SONiC. +# This is the root class of sonic_platform_base hierarchy. A platform +# may contain one or more chassis. Classes derived from this class provide +# the ability to interact with all available chassis on the platform. +# + +from __future__ import print_function + +import sys + + +class PlatformBase(object): + + # ChassisBase-derived object representing the platform's chassis + _chassis = None + + def get_chassis(self): + """ + Retrieves the chassis for this platform + + Returns: + An object derived from ChassisBase representing the platform's + chassis + """ + return self._chassis diff --git a/sonic_platform_base/psu_base.py b/sonic_platform_base/psu_base.py new file mode 100644 index 000000000000..b3bdd8e99d96 --- /dev/null +++ b/sonic_platform_base/psu_base.py @@ -0,0 +1,45 @@ +# +# psu_base.py +# +# Abstract base class for implementing a platform-specific class with which +# to interact with a power supply unit (PSU) in SONiC +# + +from . import device_base + + +class PsuBase(device_base.DeviceBase): + """ + Abstract base class for interfacing with a power supply unit + """ + + # Possible fan status LED colors + STATUS_LED_COLOR_GREEN = "green" + STATUS_LED_COLOR_RED = "red" + STATUS_LED_COLOR_OFF = "off" + + # FanBase-derived object representing fan module available on the PSU + _fan = None + + def get_fan(self): + """ + Retrieves object representing the fan module contained in this PSU + + Returns: + An object dervied from FanBase representing the fan module + contained in this PSU + """ + return _fan + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + + Args: + color: A string representing the color with which to set the + PSU status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + raise NotImplementedError diff --git a/sonic_platform_base/watchdog_base.py b/sonic_platform_base/watchdog_base.py new file mode 100644 index 000000000000..42ecec2a6f6e --- /dev/null +++ b/sonic_platform_base/watchdog_base.py @@ -0,0 +1,56 @@ +# +# watchdog_base.py +# +# Abstract base class for implementing a platform-specific class with which +# to interact with a hardware watchdog module in SONiC +# + + +class WatchdogBase: + """ + Abstract base class for interfacing with a hardware watchdog module + """ + + 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. + """ + raise NotImplementedError + + def disarm(self): + """ + Disarm the hardware watchdog + + Returns: + A boolean, True if watchdog is disarmed successfully, False if not + """ + raise NotImplementedError + + def is_armed(self): + """ + Retrieves the armed state of the hardware watchdog. + + Returns: + A boolean, True if watchdog is armed, False if not + """ + raise NotImplementedError + + 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. + """ + raise NotImplementedError