From 818ba436a91f53490a9d354ca869672b7e2132fc Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Mon, 22 Apr 2019 05:34:28 +0800 Subject: [PATCH] [Mellanox] Implement new fan platform API (#2747) --- platform/mellanox/mlnx-platform-api/setup.py | 4 +- .../__init__.py | 0 .../sonic_platform/chassis.py | 70 +++++ .../mlnx-platform-api/sonic_platform/fan.py | 246 ++++++++++++++++++ .../psu.py | 6 +- .../watchdog.py | 0 .../sonic_platform_api/chassis.py | 35 --- 7 files changed, 323 insertions(+), 38 deletions(-) rename platform/mellanox/mlnx-platform-api/{sonic_platform_api => sonic_platform}/__init__.py (100%) create mode 100644 platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py create mode 100644 platform/mellanox/mlnx-platform-api/sonic_platform/fan.py rename platform/mellanox/mlnx-platform-api/{sonic_platform_api => sonic_platform}/psu.py (92%) rename platform/mellanox/mlnx-platform-api/{sonic_platform_api => sonic_platform}/watchdog.py (100%) delete mode 100644 platform/mellanox/mlnx-platform-api/sonic_platform_api/chassis.py diff --git a/platform/mellanox/mlnx-platform-api/setup.py b/platform/mellanox/mlnx-platform-api/setup.py index 3adddd2905..12809c4085 100644 --- a/platform/mellanox/mlnx-platform-api/setup.py +++ b/platform/mellanox/mlnx-platform-api/setup.py @@ -11,7 +11,7 @@ setup( maintainer='Kevin Wang', maintainer_email='kevinw@mellanox.com', packages=[ - 'sonic_platform_api', + 'sonic_platform', ], classifiers=[ 'Development Status :: 3 - Alpha', @@ -25,6 +25,6 @@ setup( 'Programming Language :: Python :: 2.7', 'Topic :: Utilities', ], - keywords='sonic SONiC platform-api PLATFORM-API', + keywords='sonic SONiC platform PLATFORM', ) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform_api/__init__.py b/platform/mellanox/mlnx-platform-api/sonic_platform/__init__.py similarity index 100% rename from platform/mellanox/mlnx-platform-api/sonic_platform_api/__init__.py rename to platform/mellanox/mlnx-platform-api/sonic_platform/__init__.py diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py new file mode 100644 index 0000000000..b8657a68f8 --- /dev/null +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +############################################################################# +# Mellanox +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Chassis information which are available in the platform +# +############################################################################# + +import sys + +try: + from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.psu import Psu + from sonic_platform.fan import Fan + from sonic_platform.fan import FAN_PATH + from sonic_platform.watchdog import get_watchdog + from os import listdir + from os.path import isfile, join + import re +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +MLNX_NUM_PSU = 2 + +class Chassis(ChassisBase): + """Platform-specific Chassis class""" + + def __init__(self): + ChassisBase.__init__(self) + + # Initialize PSU list + for index in range(MLNX_NUM_PSU): + psu = Psu(index) + self._psu_list.append(psu) + + # Initialize watchdog + self._watchdog = get_watchdog() + + # Initialize FAN list + multi_rotor_in_drawer = False + num_of_fan, num_of_drawer = self._extract_num_of_fans_and_fan_drawers() + multi_rotor_in_drawer = num_of_fan > num_of_drawer + + for index in range(num_of_fan): + if multi_rotor_in_drawer: + fan = Fan(index, index/2) + else: + fan = Fan(index, index) + self._fan_list.append(fan) + + def _extract_num_of_fans_and_fan_drawers(self): + num_of_fan = 0 + num_of_drawer = 0 + for f in listdir(FAN_PATH): + if isfile(join(FAN_PATH, f)): + match_obj = re.match('fan(\d+)_speed_get', f) + if match_obj != None: + if int(match_obj.group(1)) > num_of_fan: + num_of_fan = int(match_obj.group(1)) + else: + match_obj = re.match('fan(\d+)_status', f) + if match_obj != None and int(match_obj.group(1)) > num_of_drawer: + num_of_drawer = int(match_obj.group(1)) + + return num_of_fan, num_of_drawer + + + diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py b/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py new file mode 100644 index 0000000000..8b057e4123 --- /dev/null +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python + +############################################################################# +# Mellanox +# +# Module contains an implementation of SONiC Platform Base API and +# provides the FANs status which are available in the platform +# +############################################################################# + +import os.path + +try: + from sonic_platform_base.fan_base import FanBase +except ImportError as e: + raise ImportError (str(e) + "- required module not found") + +LED_ON = 1 +LED_OFF = 0 + +PWM_MAX = 255 + +FAN_PATH = "/var/run/hw-management/thermal/" +LED_PATH = "/var/run/hw-management/led/" + +class Fan(FanBase): + """Platform-specific Fan class""" + def __init__(self, fan_index, drawer_index = 1, psu_fan = False): + # API index is starting from 0, Mellanox platform index is starting from 1 + self.index = fan_index + 1 + self.drawer_index = drawer_index + 1 + + self.is_psu_fan = psu_fan + + self.fan_min_speed_path = "fan{}_min".format(self.index) + if not self.is_psu_fan: + self.fan_speed_get_path = "fan{}_speed_get".format(self.index) + self.fan_speed_set_path = "fan{}_speed_set".format(self.index) + self.fan_presence_path = "fan{}_status".format(self.drawer_index) + self.fan_max_speed_path = "fan{}_max".format(self.index) + else: + self.fan_speed_get_path = "psu{}_fan1_speed_get".format(self.index) + self.fan_presence_path = "psu{}_fan1_speed_get".format(self.index) + self.fan_max_speed_path = "psu{}_max".format(self.index) + self.fan_status_path = "fan{}_fault".format(self.index) + self.fan_green_led_path = "led_fan{}_green".format(self.drawer_index) + self.fan_red_led_path = "led_fan{}_red".format(self.drawer_index) + self.fan_orange_led_path = "led_fan{}_orange".format(self.drawer_index) + self.fan_pwm_path = "pwm1" + self.fan_led_cap_path = "led_fan{}_capability".format(self.drawer_index) + + def get_status(self): + """ + Retrieves the operational status of fan + + Returns: + bool: True if fan is operating properly, False if not + """ + status = 0 + if self.is_psu_fan: + status = 1 + else: + try: + with open(os.path.join(FAN_PATH, self.fan_status_path), 'r') as fault_status: + status = int(fault_status.read()) + except (ValueError, IOError): + status = 0 + + return status == 1 + + def get_presence(self): + """ + Retrieves the presence status of fan + + Returns: + bool: True if fan is present, False if not + """ + status = 0 + if self.is_psu_fan: + if os.path.exists(os.path.join(FAN_PATH, self.fan_presence_path)): + status = 1 + else: + status = 0 + else: + try: + with open(os.path.join(FAN_PATH, self.fan_presence_path), 'r') as presence_status: + status = int(presence_status.read()) + except (ValueError, IOError): + status = 0 + + return status == 1 + + def _get_min_speed_in_rpm(self): + speed = 0 + try: + with open(os.path.join(FAN_PATH, self.fan_min_speed_path), 'r') as min_fan_speed: + speed = int(min_fan_speed.read()) + except (ValueError, IOError): + speed = 0 + + return speed + + def _get_max_speed_in_rpm(self): + speed = 0 + try: + with open(os.path.join(FAN_PATH, self.fan_max_speed_path), 'r') as max_fan_speed: + speed = int(max_fan_speed.read()) + except (ValueError, IOError): + speed = 0 + + return speed + + def get_speed(self): + """ + Retrieves the speed of fan + + Returns: + int: percentage of the max fan speed + """ + speed = 0 + try: + with open(os.path.join(FAN_PATH, self.fan_speed_get_path), 'r') as fan_curr_speed: + speed_in_rpm = int(fan_curr_speed.read()) + except (ValueError, IOError): + speed_in_rpm = 0 + + max_speed_in_rpm = self._get_max_speed_in_rpm() + speed = 100*speed_in_rpm/max_speed_in_rpm + + return speed + + def get_target_speed(self): + """ + Retrieves the expected speed of fan + + Returns: + int: percentage of the max fan speed + """ + speed = 0 + + if self.is_psu_fan: + # Not like system fan, psu fan speed can not be modified, so target speed is N/A + return speed + try: + with open(os.path.join(FAN_PATH, self.fan_speed_set_path), 'r') as fan_pwm: + pwm = int(fan_pwm.read()) + except (ValueError, IOError): + pwm = 0 + + speed = int(round(pwm*100.0/PWM_MAX)) + + return speed + + def set_speed(self, speed): + """ + Set fan speed to expected value + + Args: + speed: An integer, the percentage of full fan speed to set fan to, + in the range 0 (off) to 100 (full speed) + + Returns: + bool: True if set success, False if fail. + """ + status = True + pwm = int(round(PWM_MAX*speed/100.0)) + + if self.is_psu_fan: + #PSU fan speed is not setable. + return False + + try: + with open(os.path.join(FAN_PATH, self.fan_speed_set_path), 'w') as fan_pwm: + fan_pwm.write(str(pwm)) + except (ValueError, IOError): + status = False + + return status + + def _get_led_capability(self): + cap_list = None + try: + with open(os.path.join(LED_PATH, self.fan_led_cap_path), 'r') as fan_led_cap: + caps = fan_led_cap.read() + cap_list = caps.split() + except (ValueError, IOError): + status = 0 + + return cap_list + + def set_status_led(self, color): + """ + Set led to expected color + + Args: + color: A string representing the color with which to set the + fan module status LED + + Returns: + bool: True if set success, False if fail. + """ + led_cap_list = self._get_led_capability() + if led_cap_list is None: + return False + + if self.is_psu_fan: + # PSU fan led status is not able to set + return False + status = False + try: + if color == 'green': + with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led: + fan_led.write(str(LED_ON)) + elif color == 'red': + # Some fan don't support red led but support orange led, in this case we set led to orange + if 'red' in led_cap_list: + led_path = os.path.join(LED_PATH, self.fan_red_led_path) + elif 'orange' in led_cap_list: + led_path = os.path.join(LED_PATH, self.fan_orange_led_path) + else: + return False + with open(led_path, 'w') as fan_led: + fan_led.write(str(LED_ON)) + + elif color == 'off': + with open(os.path.join(LED_PATH, self.fan_green_led_path), 'w') as fan_led: + fan_led.write(str(LED_OFF)) + + with open(os.path.join(LED_PATH, self.fan_red_led_path), 'w') as fan_led: + fan_led.write(str(LED_OFF)) + else: + status = False + except (ValueError, IOError): + status = False + return status + + 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 + """ + # The tolerance value is fixed as 20% for all the Mellanox platform + return 20 \ No newline at end of file diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform_api/psu.py b/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py similarity index 92% rename from platform/mellanox/mlnx-platform-api/sonic_platform_api/psu.py rename to platform/mellanox/mlnx-platform-api/sonic_platform/psu.py index 073a217f3b..bcbd643eb0 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform_api/psu.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py @@ -12,6 +12,7 @@ import os.path try: from sonic_platform_base.psu_base import PsuBase + from sonic_platform.fan import Fan except ImportError as e: raise ImportError (str(e) + "- required module not found") @@ -24,7 +25,7 @@ class Psu(PsuBase): PsuBase.__init__(self) # PSU is 1-based on Mellanox platform self.index = psu_index + 1 - psu_list.append(psu_index) + psu_list.append(self.index) self.psu_path = "/var/run/hw-management/thermal/" self.psu_oper_status = "psu{}_pwr_status".format(self.index) self.psu_presence = "psu{}_status".format(self.index) @@ -32,6 +33,9 @@ class Psu(PsuBase): self.presence_file_exists = True else: self.presence_file_exists = False + fan = Fan(psu_index, psu_index, True) + if fan.get_presence(): + self._fan = fan def get_status(self): """ diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform_api/watchdog.py b/platform/mellanox/mlnx-platform-api/sonic_platform/watchdog.py similarity index 100% rename from platform/mellanox/mlnx-platform-api/sonic_platform_api/watchdog.py rename to platform/mellanox/mlnx-platform-api/sonic_platform/watchdog.py diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform_api/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform_api/chassis.py deleted file mode 100644 index e521007a1f..0000000000 --- a/platform/mellanox/mlnx-platform-api/sonic_platform_api/chassis.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -############################################################################# -# Mellanox -# -# Module contains an implementation of SONiC Platform Base API and -# provides the Chassis information which are available in the platform -# -############################################################################# - -import sys - -try: - from sonic_platform_base.chassis_base import ChassisBase - from sonic_platform_api.psu import Psu - from sonic_platform_api.watchdog import get_watchdog -except ImportError as e: - raise ImportError (str(e) + "- required module not found") - -MLNX_NUM_PSU = 2 - -class Chassis(ChassisBase): - """Platform-specific Chassis class""" - - def __init__(self): - ChassisBase.__init__(self) - - # Initialize PSU list - for index in range(MLNX_NUM_PSU): - psu = Psu(index) - self._psu_list.append(psu) - - # Initialize watchdog - self._watchdog = get_watchdog() -