[Mellanox] add PSU fan direction support (#14508)

- Why I did it
Add PSU fan direction support

- How I did it
Implement fan.get_direction for PSU fan

- How to verify it
Manual test
Unit test
This commit is contained in:
Junchao-Mellanox 2023-05-16 02:34:54 +08:00 committed by GitHub
parent 602b945f76
commit 7962a5c0fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 18 deletions

View File

@ -31,6 +31,7 @@ try:
from .led import ComponentFaultyIndicator from .led import ComponentFaultyIndicator
from . import utils from . import utils
from .thermal import Thermal from .thermal import Thermal
from .fan_drawer import VirtualDrawer
except ImportError as e: except ImportError as e:
raise ImportError (str(e) + "- required module not found") raise ImportError (str(e) + "- required module not found")
@ -45,7 +46,10 @@ CONFIG_PATH = "/var/run/hw-management/config"
FAN_DIR = "/var/run/hw-management/thermal/fan{}_dir" FAN_DIR = "/var/run/hw-management/thermal/fan{}_dir"
FAN_DIR_VALUE_EXHAUST = 0 FAN_DIR_VALUE_EXHAUST = 0
FAN_DIR_VALUE_INTAKE = 1 FAN_DIR_VALUE_INTAKE = 1
FAN_DIR_MAPPING = {
FAN_DIR_VALUE_EXHAUST: FanBase.FAN_DIRECTION_EXHAUST,
FAN_DIR_VALUE_INTAKE: FanBase.FAN_DIRECTION_INTAKE,
}
class MlnxFan(FanBase): class MlnxFan(FanBase):
def __init__(self, fan_index, position): def __init__(self, fan_index, position):
@ -125,6 +129,20 @@ class MlnxFan(FanBase):
""" """
return False return False
@classmethod
def get_fan_direction(cls, dir_path):
try:
fan_dir = utils.read_int_from_file(dir_path, raise_exception=True)
ret = FAN_DIR_MAPPING.get(fan_dir)
if ret is None:
logger.log_error(f"Got wrong value {fan_dir} for fan direction {dir_path}")
return FanBase.FAN_DIRECTION_NOT_APPLICABLE
else:
return ret
except (ValueError, IOError) as e:
logger.log_error(f"Failed to read fan direction from {dir_path} - {e}")
return FanBase.FAN_DIRECTION_NOT_APPLICABLE
class PsuFan(MlnxFan): class PsuFan(MlnxFan):
# PSU fan speed vector # PSU fan speed vector
@ -145,6 +163,7 @@ class PsuFan(MlnxFan):
self.psu_i2c_bus_path = os.path.join(CONFIG_PATH, 'psu{0}_i2c_bus'.format(self.index)) self.psu_i2c_bus_path = os.path.join(CONFIG_PATH, 'psu{0}_i2c_bus'.format(self.index))
self.psu_i2c_addr_path = os.path.join(CONFIG_PATH, 'psu{0}_i2c_addr'.format(self.index)) self.psu_i2c_addr_path = os.path.join(CONFIG_PATH, 'psu{0}_i2c_addr'.format(self.index))
self.psu_i2c_command_path = os.path.join(CONFIG_PATH, 'fan_command') self.psu_i2c_command_path = os.path.join(CONFIG_PATH, 'fan_command')
self.psu_fan_dir_path = os.path.join(FAN_PATH, "psu{}_fan_dir".format(self.index))
def get_direction(self): def get_direction(self):
""" """
@ -165,8 +184,11 @@ class PsuFan(MlnxFan):
1 stands for forward, in other words intake 1 stands for forward, in other words intake
0 stands for reverse, in other words exhaust 0 stands for reverse, in other words exhaust
""" """
if not os.path.exists(self.psu_fan_dir_path) or not self.get_presence():
return self.FAN_DIRECTION_NOT_APPLICABLE return self.FAN_DIRECTION_NOT_APPLICABLE
return MlnxFan.get_fan_direction(self.psu_fan_dir_path)
def get_status(self): def get_status(self):
""" """
Retrieves the operational status of fan Retrieves the operational status of fan
@ -263,7 +285,10 @@ class Fan(MlnxFan):
1 stands for forward, in other words intake 1 stands for forward, in other words intake
0 stands for reverse, in other words exhaust 0 stands for reverse, in other words exhaust
""" """
if not isinstance(self.fan_drawer, VirtualDrawer):
return self.fan_drawer.get_direction() return self.fan_drawer.get_direction()
else:
return MlnxFan.get_fan_direction(FAN_DIR.format(self.index))
def get_status(self): def get_status(self):
""" """

View File

@ -58,19 +58,8 @@ class MellanoxFanDrawer(FanDrawerBase):
if not self.get_presence(): if not self.get_presence():
return FanBase.FAN_DIRECTION_NOT_APPLICABLE return FanBase.FAN_DIRECTION_NOT_APPLICABLE
try: from .fan import FAN_DIR, MlnxFan
from .fan import FAN_DIR, FAN_DIR_VALUE_INTAKE, FAN_DIR_VALUE_EXHAUST return MlnxFan.get_fan_direction(FAN_DIR.format(self._index))
fan_dir = utils.read_int_from_file(FAN_DIR.format(self._index), raise_exception=True)
if fan_dir == FAN_DIR_VALUE_INTAKE:
return FanBase.FAN_DIRECTION_INTAKE
elif fan_dir == FAN_DIR_VALUE_EXHAUST:
return FanBase.FAN_DIRECTION_EXHAUST
else:
logger.log_error("Got wrong value {} for fan direction {}".format(fan_dir, self._index))
return FanBase.FAN_DIRECTION_NOT_APPLICABLE
except (ValueError, IOError) as e:
logger.log_error("Failed to read fan direction status to {}".format(repr(e)))
return FanBase.FAN_DIRECTION_NOT_APPLICABLE
def set_status_led(self, color): def set_status_led(self, color):
""" """

View File

@ -25,7 +25,7 @@ modules_path = os.path.dirname(test_path)
sys.path.insert(0, modules_path) sys.path.insert(0, modules_path)
from sonic_platform import utils from sonic_platform import utils
from sonic_platform.fan import Fan, PsuFan from sonic_platform.fan import Fan, PsuFan, FAN_DIR_VALUE_INTAKE, FAN_DIR_VALUE_EXHAUST
from sonic_platform.fan_drawer import RealDrawer, VirtualDrawer from sonic_platform.fan_drawer import RealDrawer, VirtualDrawer
from sonic_platform.psu import Psu from sonic_platform.psu import Psu
@ -107,11 +107,12 @@ class TestFan:
fan.set_speed(60) fan.set_speed(60)
mock_write_file.assert_called_with(fan.fan_speed_set_path, 153, raise_exception=True) mock_write_file.assert_called_with(fan.fan_speed_set_path, 153, raise_exception=True)
@patch('sonic_platform.utils.read_int_from_file')
@patch('sonic_platform.thermal.Thermal.get_cooling_level') @patch('sonic_platform.thermal.Thermal.get_cooling_level')
@patch('sonic_platform.psu.Psu.get_presence') @patch('sonic_platform.psu.Psu.get_presence')
@patch('sonic_platform.psu.Psu.get_powergood_status') @patch('sonic_platform.psu.Psu.get_powergood_status')
@patch('os.path.exists') @patch('os.path.exists')
def test_psu_fan_basic(self, mock_path_exists, mock_powergood, mock_presence, mock_cooling_level): def test_psu_fan_basic(self, mock_path_exists, mock_powergood, mock_presence, mock_cooling_level, mock_read_int):
mock_path_exists.return_value = False mock_path_exists.return_value = False
psu = Psu(0) psu = Psu(0)
fan = PsuFan(0, 1, psu) fan = PsuFan(0, 1, psu)
@ -126,6 +127,12 @@ class TestFan:
assert fan.get_presence() is True assert fan.get_presence() is True
mock_cooling_level.return_value = 7 mock_cooling_level.return_value = 7
assert fan.get_target_speed() == 70 assert fan.get_target_speed() == 70
mock_read_int.return_value = FAN_DIR_VALUE_INTAKE
assert fan.get_direction() == Fan.FAN_DIRECTION_INTAKE
mock_read_int.return_value = FAN_DIR_VALUE_EXHAUST
assert fan.get_direction() == Fan.FAN_DIRECTION_EXHAUST
mock_read_int.return_value = -1 # invalid value
assert fan.get_direction() == Fan.FAN_DIRECTION_NOT_APPLICABLE
def test_psu_fan_set_speed(self): def test_psu_fan_set_speed(self):
psu = Psu(0) psu = Psu(0)