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

[Mellanox] Implement new fan platform API #2747

Merged
merged 5 commits into from
Apr 21, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
try:
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform_api.psu import Psu
jleveque marked this conversation as resolved.
Show resolved Hide resolved
from sonic_platform_api.fan import Fan
from sonic_platform_api.fan import FAN_PATH
from sonic_platform_api.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")

Expand All @@ -32,4 +37,34 @@ def __init__(self):

# 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



246 changes: 246 additions & 0 deletions platform/mellanox/mlnx-platform-api/sonic_platform_api/fan.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

try:
from sonic_platform_base.psu_base import PsuBase
from sonic_platform_api.fan import Fan
except ImportError as e:
raise ImportError (str(e) + "- required module not found")

Expand All @@ -24,14 +25,17 @@ def __init__(self, psu_index):
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)
if os.path.exists(os.path.join(self.psu_path, self.psu_presence)):
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):
"""
Expand Down