From b595a6eadfbb15949890c2490a17cfdc0d7cdf60 Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com> Date: Tue, 17 Nov 2020 10:56:03 +0800 Subject: [PATCH] [Mellanox] Implement new platform API for SONiC physical entity mib extension (#5645) In order to support SONiC physical entity mib extension, a few new platform API are added to sonic-platform-common, this PR is to provide an mellanox platform implementation for those new APIs. --- .../sonic_platform/chassis.py | 28 +++++++-- .../mlnx-platform-api/sonic_platform/fan.py | 28 ++++++--- .../sonic_platform/fan_drawer.py | 16 +++++ .../mlnx-platform-api/sonic_platform/psu.py | 22 ++++++- .../mlnx-platform-api/sonic_platform/sfp.py | 16 ++++- .../sonic_platform/thermal.py | 63 +++++++++++++------ .../mlnx-platform-api/tests/test_fan_api.py | 6 +- 7 files changed, 144 insertions(+), 35 deletions(-) diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py index 1682d4649d..77cf27da2d 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/chassis.py @@ -104,7 +104,7 @@ class Chassis(ChassisBase): drawer = drawer_ctor(drawer_index, fan_data) self._fan_drawer_list.append(drawer) for index in range(fan_num_per_drawer): - fan = Fan(fan_index, drawer) + fan = Fan(fan_index, drawer, index + 1) fan_index += 1 drawer._fan_list.append(fan) self._fan_list.append(fan) @@ -130,18 +130,19 @@ class Chassis(ChassisBase): for index in range(self.PORT_START, self.PORT_END + 1): if index in range(self.QSFP_PORT_START, self.PORTS_IN_BLOCK + 1): - sfp_module = SFP(index, 'QSFP', self.sdk_handle) + sfp_module = SFP(index, 'QSFP', self.sdk_handle, self.platform_name) else: - sfp_module = SFP(index, 'SFP', self.sdk_handle) + sfp_module = SFP(index, 'SFP', self.sdk_handle, self.platform_name) + self._sfp_list.append(sfp_module) self.sfp_module_initialized = True def initialize_thermals(self): - from sonic_platform.thermal import initialize_thermals + from sonic_platform.thermal import initialize_chassis_thermals # Initialize thermals - initialize_thermals(self.platform_name, self._thermal_list, self._psu_list) + initialize_chassis_thermals(self.platform_name, self._thermal_list) def initialize_eeprom(self): @@ -514,3 +515,20 @@ class Chassis(ChassisBase): specified. """ return None if not Chassis._led else Chassis._led.get_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py b/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py index 3b5be3a25d..78e79ca513 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/fan.py @@ -37,14 +37,16 @@ class Fan(FanBase): PSU_FAN_SPEED = ['0x3c', '0x3c', '0x3c', '0x3c', '0x3c', '0x3c', '0x3c', '0x46', '0x50', '0x5a', '0x64'] - def __init__(self, fan_index, fan_drawer, psu_fan = False): + def __init__(self, fan_index, fan_drawer, position, psu_fan = False, psu=None): super(Fan, self).__init__() # API index is starting from 0, Mellanox platform index is starting from 1 self.index = fan_index + 1 self.fan_drawer = fan_drawer + self.position = position self.is_psu_fan = psu_fan + self.psu = psu if self.fan_drawer: self.led = ComponentFaultyIndicator(self.fan_drawer.get_led()) elif self.is_psu_fan: @@ -123,13 +125,8 @@ class Fan(FanBase): 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 - return status == 1 + return self.psu.get_presence() and self.psu.get_powergood_status() and os.path.exists(os.path.join(FAN_PATH, self.fan_presence_path)) else: return self.fan_drawer.get_presence() @@ -254,6 +251,22 @@ class Fan(FanBase): # The tolerance value is fixed as 50% for all the Mellanox platform return 50 + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.position + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + @classmethod def set_cooling_level(cls, level, cur_state): """ @@ -288,3 +301,4 @@ class Fan(FanBase): except (ValueError, IOError) as e: raise RuntimeError("Failed to get cooling level - {}".format(e)) + \ No newline at end of file diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/fan_drawer.py b/platform/mellanox/mlnx-platform-api/sonic_platform/fan_drawer.py index bed6c7ab04..66ee394917 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/fan_drawer.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/fan_drawer.py @@ -84,6 +84,22 @@ class MellanoxFanDrawer(FanDrawerBase): """ return self._led.get_status() + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self._index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return self._fan_data['hot_swappable'] + class RealDrawer(MellanoxFanDrawer): def __init__(self, index, fan_data): diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py b/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py index d26df89f04..897555ed80 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/psu.py @@ -103,7 +103,7 @@ class Psu(PsuBase): # unplugable PSU has no FAN if self.psu_data['hot_swappable']: - fan = Fan(psu_index, None, True) + fan = Fan(psu_index, None, 1, True, self) self._fan_list.append(fan) if self.psu_data['led_num'] == 1: @@ -111,6 +111,10 @@ class Psu(PsuBase): else: # 2010/2100 self.led = PsuLed(self.index) + # initialize thermal for PSU + from .thermal import initialize_psu_thermals + initialize_psu_thermals(platform, self._thermal_list, self.index, self.get_power_available_status) + def get_name(self): return self._name @@ -244,6 +248,22 @@ class Psu(PsuBase): else: return True, "" + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return self.psu_data['hot_swappable'] + @classmethod def get_shared_led(cls): if not cls.shared_led: diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py index 89b4b520a4..611758daa8 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/sfp.py @@ -309,7 +309,8 @@ def deinitialize_sdk_handle(sdk_handle): class SFP(SfpBase): """Platform-specific SFP class""" - def __init__(self, sfp_index, sfp_type, sdk_handle): + def __init__(self, sfp_index, sfp_type, sdk_handle, platform): + SfpBase.__init__(self) self.index = sfp_index + 1 self.sfp_eeprom_path = "qsfp{}".format(self.index) self.sfp_status_path = "qsfp{}_status".format(self.index) @@ -319,7 +320,12 @@ class SFP(SfpBase): self.sdk_handle = sdk_handle self.sdk_index = sfp_index + # initialize SFP thermal list + from .thermal import initialize_sfp_thermals + initialize_sfp_thermals(platform, self._thermal_list, self.index) + def reinit(self): + """ Re-initialize this SFP object when a new SFP inserted :return: @@ -2034,3 +2040,11 @@ class SFP(SfpBase): False if not """ return NotImplementedError + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/platform/mellanox/mlnx-platform-api/sonic_platform/thermal.py b/platform/mellanox/mlnx-platform-api/sonic_platform/thermal.py index 4ebe282524..48b277345b 100644 --- a/platform/mellanox/mlnx-platform-api/sonic_platform/thermal.py +++ b/platform/mellanox/mlnx-platform-api/sonic_platform/thermal.py @@ -113,12 +113,10 @@ thermal_name = { } thermal_device_categories_all = [ - THERMAL_DEV_CATEGORY_CPU_CORE, - THERMAL_DEV_CATEGORY_CPU_PACK, - THERMAL_DEV_CATEGORY_MODULE, - THERMAL_DEV_CATEGORY_PSU, THERMAL_DEV_CATEGORY_AMBIENT, - THERMAL_DEV_CATEGORY_GEARBOX + THERMAL_DEV_CATEGORY_CPU_PACK, + THERMAL_DEV_CATEGORY_CPU_CORE, + THERMAL_DEV_CATEGORY_GEARBOX, ] thermal_device_categories_singleton = [ @@ -305,18 +303,34 @@ thermal_profile_list = [ } ] +def initialize_psu_thermals(platform, thermal_list, psu_index, dependency): + tp_index = platform_dict_thermal[platform] + thermal_profile = thermal_profile_list[tp_index] + _, count = thermal_profile[THERMAL_DEV_CATEGORY_PSU] + if count == 0: + return + thermal = Thermal(THERMAL_DEV_CATEGORY_PSU, psu_index, True, 1, dependency) + thermal_list.append(thermal) -def initialize_thermals(platform, thermal_list, psu_list): + +def initialize_sfp_thermals(platform, thermal_list, sfp_index): + thermal = Thermal(THERMAL_DEV_CATEGORY_MODULE, sfp_index, True, 1) + thermal_list.append(thermal) + + +def initialize_chassis_thermals(platform, thermal_list): # create thermal objects for all categories of sensors tp_index = platform_dict_thermal[platform] thermal_profile = thermal_profile_list[tp_index] Thermal.thermal_profile = thermal_profile + position = 1 for category in thermal_device_categories_all: if category == THERMAL_DEV_CATEGORY_AMBIENT: count, ambient_list = thermal_profile[category] for ambient in ambient_list: - thermal = Thermal(category, ambient, True) - thermal_list.append(thermal) + thermal = Thermal(category, ambient, True, position) + thermal_list.append(thermal), + position += 1 else: start, count = 0, 0 if category in thermal_profile: @@ -324,17 +338,14 @@ def initialize_thermals(platform, thermal_list, psu_list): if count == 0: continue if count == 1: - thermal = Thermal(category, 0, False) + thermal = Thermal(category, 0, False, position) thermal_list.append(thermal) + position += 1 else: - if category == THERMAL_DEV_CATEGORY_PSU: - for index in range(count): - thermal = Thermal(category, start + index, True, psu_list[index].get_power_available_status) - thermal_list.append(thermal) - else: - for index in range(count): - thermal = Thermal(category, start + index, True) - thermal_list.append(thermal) + for index in range(count): + thermal = Thermal(category, start + index, True, position) + thermal_list.append(thermal) + position += 1 @@ -342,7 +353,7 @@ class Thermal(ThermalBase): thermal_profile = None thermal_algorithm_status = False - def __init__(self, category, index, has_index, dependency = None): + def __init__(self, category, index, has_index, position, dependency = None): """ index should be a string for category ambient and int for other categories """ @@ -357,6 +368,7 @@ class Thermal(ThermalBase): self.index = 0 self.category = category + self.position = position self.temperature = self._get_file_from_api(THERMAL_API_GET_TEMPERATURE) self.high_threshold = self._get_file_from_api(THERMAL_API_GET_HIGH_THRESHOLD) self.high_critical_threshold = self._get_file_from_api(THERMAL_API_GET_HIGH_CRITICAL_THRESHOLD) @@ -480,6 +492,21 @@ class Thermal(ThermalBase): return None return value_float / 1000.0 + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.position + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False @classmethod def _write_generic_file(cls, filename, content): diff --git a/platform/mellanox/mlnx-platform-api/tests/test_fan_api.py b/platform/mellanox/mlnx-platform-api/tests/test_fan_api.py index 24158997ab..bb9ee7e125 100644 --- a/platform/mellanox/mlnx-platform-api/tests/test_fan_api.py +++ b/platform/mellanox/mlnx-platform-api/tests/test_fan_api.py @@ -16,7 +16,7 @@ from sonic_platform.device_data import DEVICE_DATA def test_get_absence_fan_direction(): fan_drawer = RealDrawer(0, DEVICE_DATA['x86_64-mlnx_msn2700-r0']['fans']) - fan = Fan(0, fan_drawer) + fan = Fan(0, fan_drawer, 1) fan_drawer.get_presence = MagicMock(return_value=False) assert not fan.is_psu_fan @@ -31,8 +31,8 @@ def test_fan_drawer_set_status_led(): with pytest.raises(Exception): fan_drawer.set_status_led(None, Fan.STATUS_LED_COLOR_RED) - fan1 = Fan(0, fan_drawer) - fan2 = Fan(1, fan_drawer) + fan1 = Fan(0, fan_drawer, 1) + fan2 = Fan(1, fan_drawer, 2) fan_list = fan_drawer.get_all_fans() fan_list.append(fan1) fan_list.append(fan2)