From 8b7ae8c7ff00caf1d4d1029e4c61c30ad235d147 Mon Sep 17 00:00:00 2001 From: jostar-yang Date: Mon, 28 Nov 2022 16:56:16 +0800 Subject: [PATCH] [Edgecore][as7726/PDDF] Add needed api to sonic_platform Signed-off-by: jostar-yang --- .../pddf/pd-plugin.json | 4 +- .../system_health_monitoring_config.json | 17 ++ .../as7726-32x/sonic_platform/chassis.py | 114 +++++++---- .../as7726-32x/sonic_platform/component.py | 190 ++++++++++++++++++ .../as7726-32x/sonic_platform/fan.py | 25 ++- .../as7726-32x/sonic_platform/fan_drawer.py | 20 ++ .../as7726-32x/sonic_platform/helper.py | 117 +++++++++++ .../as7726-32x/sonic_platform/psu.py | 35 ++++ .../as7726-32x/sonic_platform/sfp.py | 14 ++ .../as7726-32x/sonic_platform/thermal.py | 11 + 10 files changed, 506 insertions(+), 41 deletions(-) create mode 100644 device/accton/x86_64-accton_as7726_32x-r0/system_health_monitoring_config.json create mode 100644 platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/component.py create mode 100644 platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/helper.py diff --git a/device/accton/x86_64-accton_as7726_32x-r0/pddf/pd-plugin.json b/device/accton/x86_64-accton_as7726_32x-r0/pddf/pd-plugin.json index b4e2cc4ac8cb..a0beb6fda4d9 100644 --- a/device/accton/x86_64-accton_as7726_32x-r0/pddf/pd-plugin.json +++ b/device/accton/x86_64-accton_as7726_32x-r0/pddf/pd-plugin.json @@ -34,7 +34,7 @@ { "i2c": { - "valmap": { "F2B":"EXHAUST", "B2F":"INTAKE" } + "valmap": { "F2B":"exhaust", "B2F":"intake" } } }, @@ -47,7 +47,7 @@ { "i2c": { - "valmap": {"1":"EXHAUST", "0":"INTAKE"} + "valmap": {"1":"exhaust", "0":"intake"} } }, diff --git a/device/accton/x86_64-accton_as7726_32x-r0/system_health_monitoring_config.json b/device/accton/x86_64-accton_as7726_32x-r0/system_health_monitoring_config.json new file mode 100644 index 000000000000..53b14bec17be --- /dev/null +++ b/device/accton/x86_64-accton_as7726_32x-r0/system_health_monitoring_config.json @@ -0,0 +1,17 @@ +{ + "services_to_ignore": [], + "devices_to_ignore": [ + "asic", + "psu.voltage", + "psu.temperature", + "PSU1_FAN1.speed", + "PSU2_FAN1.speed" + ], + "user_defined_checkers": [], + "polling_interval": 60, + "led_color": { + "fault": "STATUS_LED_COLOR_AMBER", + "normal": "STATUS_LED_COLOR_GREEN", + "booting": "STATUS_LED_COLOR_OFF" + } +} diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/chassis.py index 8ff20a760562..7a6414841a07 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/chassis.py +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/chassis.py @@ -8,58 +8,59 @@ try: import sys - import time from sonic_platform_pddf_base.pddf_chassis import PddfChassis + from .event import SfpEvent + from .helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") +NUM_COMPONENT = 6 +HOST_REBOOT_CAUSE_PATH = "/host/reboot-cause/" +PMON_REBOOT_CAUSE_PATH = "/usr/share/sonic/platform/api_files/reboot-cause/" +REBOOT_CAUSE_FILE = "reboot-cause.txt" class Chassis(PddfChassis): """ PDDF Platform-specific Chassis class """ + SYSLED_DEV_NAME = "DIAG_LED" + def __init__(self, pddf_data=None, pddf_plugin_data=None): PddfChassis.__init__(self, pddf_data, pddf_plugin_data) + self.__initialize_components() + self._api_helper = APIHelper() + self._sfpevent = SfpEvent(self.get_all_sfps()) + + + def __initialize_components(self): + from sonic_platform.component import Component + for index in range(NUM_COMPONENT): + component = Component(index) + self._component_list.append(component) # Provide the functions/variables below for which implementation is to be overwritten - sfp_change_event_data = {'valid': 0, 'last': 0, 'present': 0} - def get_change_event(self, timeout=2000): - now = time.time() - port_dict = {} - change_dict = {} - change_dict['sfp'] = port_dict - - if timeout < 1000: - timeout = 1000 - timeout = timeout / float(1000) # Convert to secs - - if now < (self.sfp_change_event_data['last'] + timeout) and self.sfp_change_event_data['valid']: - return True, change_dict - - bitmap = 0 - for i in range(34): - modpres = self.get_sfp(i+1).get_presence() - if modpres: - bitmap = bitmap | (1 << i) - - changed_ports = self.sfp_change_event_data['present'] ^ bitmap - if changed_ports: - for i in range(34): - if (changed_ports & (1 << i)): - if (bitmap & (1 << i)) == 0: - port_dict[i+1] = '0' - else: - port_dict[i+1] = '1' - - - # Update teh cache dict - self.sfp_change_event_data['present'] = bitmap - self.sfp_change_event_data['last'] = now - self.sfp_change_event_data['valid'] = 1 - return True, change_dict - else: - return True, change_dict + def get_change_event(self, timeout=0): + return self._sfpevent.get_sfp_event(timeout) + + 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. + """ + + reboot_cause_path = (HOST_REBOOT_CAUSE_PATH + REBOOT_CAUSE_FILE) + sw_reboot_cause = self._api_helper.read_txt_file( + reboot_cause_path) or "Unknown" + + + return ('REBOOT_CAUSE_NON_HARDWARE', sw_reboot_cause) def get_sfp(self, index): @@ -84,3 +85,40 @@ def get_sfp(self, index): sys.stderr.write("SFP index {} out of range (1-{})\n".format( index, len(self._sfp_list))) return sfp + + 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 get_num_sfps(self): + return len(self._sfp_list) + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + + def initizalize_system_led(self): + return + + def get_status_led(self): + return self.get_system_led(self.SYSLED_DEV_NAME) + + def set_status_led(self, color): + return self.set_system_led(self.SYSLED_DEV_NAME, color) + + def get_port_or_cage_type(self, port): + from sonic_platform_base.sfp_base import SfpBase + if port in range(1, 32): + return SfpBase.SFP_PORT_TYPE_BIT_QSFP | SfpBase.SFP_PORT_TYPE_BIT_QSFP_PLUS | SfpBase.SFP_PORT_TYPE_BIT_QSFP28 + else: + return SfpBase.SFP_PORT_TYPE_BIT_SFP | SfpBase.SFP_PORT_TYPE_BIT_SFP_PLUS diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/component.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/component.py new file mode 100644 index 000000000000..451e794a2498 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/component.py @@ -0,0 +1,190 @@ +############################################################################# +# +# Component contains an implementation of SONiC Platform Base API and +# provides the components firmware management function +# +############################################################################# + +try: + import subprocess + from sonic_platform_base.component_base import ComponentBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +CPLD_ADDR_MAPPING = { + "CPLD1": ['11', '0x60'], + "CPLD2": ['12', '0x62'], + "CPLD3": ['13', '0x64'], + "CPLD4": ['54', '0x66'] +} +SYSFS_PATH = "/sys/bus/i2c/devices/" +BIOS_VERSION_PATH = "/sys/class/dmi/id/bios_version" +COMPONENT_LIST= [ + ("CPLD1", "CPLD 1"), + ("CPLD2", "CPLD 2"), + ("CPLD3", "CPLD 3"), + ("CPLD4", "CPLD 4"), + ("CPLD5", "CPLD 5"), + ("BIOS", "Basic Input/Output System") + +] + +class Component(ComponentBase): + """Platform-specific Component class""" + + DEVICE_TYPE = "component" + + def __init__(self, component_index=0): + self.index = component_index + self.name = self.get_name() + + def __run_command(self, command): + # Run bash command and print output to stdout + try: + process = subprocess.Popen( + shlex.split(command), stdout=subprocess.PIPE) + while True: + output = process.stdout.readline() + if output == '' and process.poll() is not None: + break + rc = process.poll() + if rc != 0: + return False + except Exception: + return False + return True + + def __get_bios_version(self): + # Retrieves the BIOS firmware version + try: + with open(BIOS_VERSION_PATH, 'r') as fd: + bios_version = fd.read() + return bios_version.strip() + except Exception as e: + return None + + def __get_cpld_version(self): + # Retrieves the CPLD firmware version + cpld_version = dict() + for cpld_name in CPLD_ADDR_MAPPING: + cmd = "i2cget -f -y {0} {1} 0x1".format(CPLD_ADDR_MAPPING[cpld_name][0], CPLD_ADDR_MAPPING[cpld_name][1]) + status, value = subprocess.getstatusoutput(cmd) + if not status: + cpld_version_raw = value.rstrip() + cpld_version[cpld_name] = "{}".format(int(cpld_version_raw,16)) + + return cpld_version + + + def __get_cpld_cpu_version(self): + try: + cpu_version = dict() + cmd = "i2cget -f -y 0 0x65 0x01" + status, output1 = subprocess.getstatusoutput(cmd) + if status == 0 : + cpu_version = "{}".format(output1[2:]) + else : + cpu_version = 'None' + except Exception as e: + cpu_version = 'None' + + return cpu_version + + def get_name(self): + """ + Retrieves the name of the component + Returns: + A string containing the name of the component + """ + return COMPONENT_LIST[self.index][0] + + def get_description(self): + """ + Retrieves the description of the component + Returns: + A string containing the description of the component + """ + return COMPONENT_LIST[self.index][1] + + def get_firmware_version(self): + """ + Retrieves the firmware version of module + Returns: + string: The firmware versions of the module + """ + fw_version = None + + if self.name == "BIOS": + fw_version = self.__get_bios_version() + elif "CPLD5" in self.name: + fw_version = self.__get_cpld_cpu_version() + elif "CPLD" in self.name: + cpld_version = self.__get_cpld_version() + fw_version = cpld_version.get(self.name) + + return fw_version + + def install_firmware(self, image_path): + """ + Install firmware to module + Args: + image_path: A string, path to firmware image + Returns: + A boolean, True if install successfully, False if not + """ + raise NotImplementedError + + def get_presence(self): + """ + Retrieves the presence of the device + Returns: + bool: True if device is present, False if not + """ + return True + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + return True + + 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/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan.py index 23d8be5a8a9e..7038b9d1db99 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan.py +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan.py @@ -6,6 +6,9 @@ except ImportError as e: raise ImportError(str(e) + "- required module not found") +FAN_NAME_LIST = ["FAN-1F", "FAN-1R", "FAN-2F", "FAN-2R", + "FAN-3F", "FAN-3R", "FAN-4F", "FAN-4R", + "FAN-5F", "FAN-5R", "FAN-6F", "FAN-6R"] class Fan(PddfFan): """PDDF Platform-Specific Fan class""" @@ -15,5 +18,25 @@ def __init__(self, tray_idx, fan_idx=0, pddf_data=None, pddf_plugin_data=None, i PddfFan.__init__(self, tray_idx, fan_idx, pddf_data, pddf_plugin_data, is_psu_fan, psu_index) # Provide the functions/variables below for which implementation is to be overwritten - # Since AS4630 psu_fan airflow direction cant be read from sysfs, it is fixed as 'F2B' or 'intake' + + 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 (self.fan_index) \ + if not self.is_psu_fan else (self.fans_psu_index+1) + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True if not self.is_psu_fan else False diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan_drawer.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan_drawer.py index 3b9bb607f632..088b6df0eaf0 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan_drawer.py +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/fan_drawer.py @@ -15,3 +15,23 @@ def __init__(self, tray_idx, pddf_data=None, pddf_plugin_data=None): PddfFanDrawer.__init__(self, tray_idx, pddf_data, pddf_plugin_data) # Provide the functions/variables below for which implementation is to be overwritten + 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 self.fantray_index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/helper.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/helper.py new file mode 100644 index 000000000000..b124ca29f0df --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/helper.py @@ -0,0 +1,117 @@ +import os +import struct +import subprocess +from mmap import * +from sonic_py_common import device_info + +HOST_CHK_CMD = "docker > /dev/null 2>&1" +EMPTY_STRING = "" + + +class APIHelper(): + + def __init__(self): + (self.platform, self.hwsku) = device_info.get_platform_and_hwsku() + + def is_host(self): + return os.system(HOST_CHK_CMD) == 0 + + def pci_get_value(self, resource, offset): + status = True + result = "" + try: + fd = os.open(resource, os.O_RDWR) + mm = mmap(fd, 0) + mm.seek(int(offset)) + read_data_stream = mm.read(4) + result = struct.unpack('I', read_data_stream) + except Exception: + status = False + return status, result + + def run_command(self, cmd): + status = True + result = "" + try: + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + except Exception: + status = False + return status, result + + def run_interactive_command(self, cmd): + try: + os.system(cmd) + except Exception: + return False + return True + + def read_txt_file(self, file_path): + try: + with open(file_path, 'r', errors='replace') as fd: + data = fd.read() + return data.strip() + except IOError: + pass + return None + + def write_txt_file(self, file_path, value): + try: + with open(file_path, 'w') as fd: + fd.write(str(value)) + except IOError: + return False + return True + + def ipmi_raw(self, netfn, cmd): + status = True + result = "" + try: + cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd)) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_fru_id(self, id, key=None): + status = True + result = "" + try: + cmd = "ipmitool fru print {}".format(str( + id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key)) + + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result + + def ipmi_set_ss_thres(self, id, threshold_key, value): + status = True + result = "" + try: + cmd = "ipmitool sensor thresh '{}' {} {}".format(str(id), str(threshold_key), str(value)) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raw_data, err = p.communicate() + if err == '': + result = raw_data.strip() + else: + status = False + except Exception: + status = False + return status, result diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/psu.py index a2a330206551..d79d484d52a2 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/psu.py +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/psu.py @@ -17,6 +17,24 @@ def __init__(self, index, pddf_data=None, pddf_plugin_data=None): PddfPsu.__init__(self, index, pddf_data, pddf_plugin_data) # Provide the functions/variables below for which implementation is to be overwritten + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + return 14.72 + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + return 7.68 + def get_maximum_supplied_power(self): """ Retrieves the maximum supplied power by PSU (or PSU capacity) @@ -42,3 +60,20 @@ def get_type(self): A string, the type of PSU (AC/DC) """ return "AC" + + 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 psu_index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/sfp.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/sfp.py index d9b6e491bef4..73355a6bbac9 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/sfp.py +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/sfp.py @@ -1,7 +1,10 @@ #!/usr/bin/env python try: + import natsort from sonic_platform_pddf_base.pddf_sfp import PddfSfp + from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper + from sonic_py_common import device_info except ImportError as e: raise ImportError (str(e) + "- required module not found") @@ -15,3 +18,14 @@ def __init__(self, index, pddf_data=None, pddf_plugin_data=None): PddfSfp.__init__(self, index, pddf_data, pddf_plugin_data) # Provide the functions/variables below for which implementation is to be overwritten + def get_position_in_parent(self): + """Retrieves 1-based relative physical position in parent device.""" + return self.port_index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/thermal.py b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/thermal.py index 77d6ec7ae886..96ffd705b0d9 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/thermal.py +++ b/platform/broadcom/sonic-platform-modules-accton/as7726-32x/sonic_platform/thermal.py @@ -15,3 +15,14 @@ def __init__(self, index, pddf_data=None, pddf_plugin_data=None, is_psu_thermal= PddfThermal.__init__(self, index, pddf_data, pddf_plugin_data, is_psu_thermal, psu_index) # Provide the functions/variables below for which implementation is to be overwritten + def get_status(self): + get_temp=self.get_temperature() + + if get_temp is not None: + return True if get_temp else False + + def get_position_in_parent(self): + return self.thermal_index + + def is_replaceable(self): + return False \ No newline at end of file