diff --git a/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/chassis.py b/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/chassis.py index dc622016cbb2..d984b3578b27 100644 --- a/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/chassis.py +++ b/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/chassis.py @@ -18,6 +18,7 @@ from sonic_platform_base.chassis_base import ChassisBase from sonic_platform.eeprom import Tlv from sonic_platform.fan import Fan + from sonic_platform.psu import Psu from helper import APIHelper except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -46,6 +47,9 @@ def __init__(self): for fan_index in range(0, NUM_FAN): fan = Fan(fant_index, fan_index) self._fan_list.append(fan) + for index in range(0, NUM_PSU): + psu = Psu(index) + self._psu_list.append(psu) def get_base_mac(self): """ diff --git a/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/fan.py b/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/fan.py index 902de261f8cb..d1b36effc1ac 100644 --- a/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/fan.py +++ b/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/fan.py @@ -30,7 +30,7 @@ IPMI_SET_FAN_LED_CMD = "0x07 {} {}" IPMI_GET_FAN_LED_CMD = "0x08 {}" IPMI_SET_PWM = "0x03 0x01 0x02 {} {}" -IPMI_FRU_PRINT_ID = "ipmitool fru print {}" + IPMI_FRU_MODEL_KEY = "Board Part Number" IPMI_FRU_SERIAL_KEY = "Board Serial" @@ -48,6 +48,9 @@ FAN_PWM_REGISTER_STEP = 0x10 FAN1_FRU_ID = 6 +NUM_OF_FAN_TRAY = 7 +PSU_FAN1_FRONT_SS_ID = "0x33" + class Fan(FanBase): """Platform-specific Fan class""" @@ -69,8 +72,10 @@ def get_direction(self): depending on fan direction """ direction = self.FAN_DIRECTION_EXHAUST + fan_direction_key = hex(self.fan_tray_index) if not self.is_psu_fan else hex( + self.psu_index + NUM_OF_FAN_TRAY) status, raw_flow = self._api_helper.ipmi_raw( - IPMI_OEM_NETFN, IPMI_AIR_FLOW_CMD.format(hex(self.fan_tray_index))) + IPMI_OEM_NETFN, IPMI_AIR_FLOW_CMD.format(fan_direction_key)) if status and raw_flow == "01": direction = self.FAN_DIRECTION_INTAKE @@ -88,13 +93,12 @@ def get_speed(self): Max F2B = 24700 RPM Max B2F = 29700 RPM """ - # ipmitool raw 0x3a 0x03 0x01 0x01 {register} - # register = 22 32 42 52 62 72 82 max_rpm = MAX_OUTLET if self.fan_index % 2 == 0 else MAX_INLET fan1_ss_start = FAN1_FRONT_SS_ID if self.fan_index % 2 == 0 else FAN1_REAR_SS_ID - ss_id = hex(int(fan1_ss_start, 16) + self.fan_tray_index) + ss_id = hex(int(fan1_ss_start, 16) + self.fan_tray_index) if not self.psu_index else hex( + int(PSU_FAN1_FRONT_SS_ID, 16) + self.fan_tray_index) status, raw_ss_read = self._api_helper.ipmi_raw( IPMI_SENSOR_NETFN, IPMI_FAN_SPEED_CMD.format(ss_id)) @@ -145,6 +149,10 @@ def set_speed(self, speed): # ipmitool raw 0x3a 0x03 0x01 0x02 {register} {pwm_speed} # register = 22 32 42 52 62 72 82 + if self.is_psu_fan: + ## TODO + return False + speed_hex = hex(int(float(speed)/100 * 255)) fan_register_hex = hex(FAN_PWM_REGISTER_START + (self.fan_tray_index*FAN_PWM_REGISTER_STEP)) @@ -171,6 +179,11 @@ def set_status_led(self, color): manual: ipmitool raw 0x3A 0x09 0x02 0x00 auto: ipmitool raw 0x3A 0x09 0x02 0x01 """ + + if self.is_psu_fan: + # Not support + return False + led_cmd = { self.STATUS_LED_COLOR_GREEN: FAN_LED_GREEN_CMD, self.STATUS_LED_COLOR_RED: FAN_LED_RED_CMD, @@ -196,6 +209,10 @@ def get_status_led(self): STATUS_LED_COLOR_RED = "red" STATUS_LED_COLOR_OFF = "off" """ + if self.is_psu_fan: + # Not support + return self.STATUS_LED_COLOR_OFF + fan_selector = hex(int(FAN1_LED_CMD, 16) + self.fan_tray_index) status, hx_color = self._api_helper.ipmi_raw( IPMI_OEM_NETFN, IPMI_GET_FAN_LED_CMD.format(fan_selector)) @@ -225,6 +242,9 @@ def get_presence(self): Returns: bool: True if FAN is present, False if not """ + if self.is_psu_fan: + return True + presence = False status, raw_present = self._api_helper.ipmi_raw( IPMI_OEM_NETFN, IPMI_FAN_PRESENT_CMD.format(hex(self.index))) @@ -239,6 +259,9 @@ def get_model(self): Returns: string: Model/part number of device """ + if self.is_psu_fan: + return "Unknown" + model = "Unknown" ipmi_fru_idx = self.fan_tray_index + FAN1_FRU_ID status, raw_model = self._api_helper.ipmi_fru_id( @@ -256,6 +279,9 @@ def get_serial(self): Returns: string: Serial number of device """ + if self.is_psu_fan: + return "Unknown" + serial = "Unknown" ipmi_fru_idx = self.fan_tray_index + FAN1_FRU_ID status, raw_model = self._api_helper.ipmi_fru_id( diff --git a/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/psu.py b/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/psu.py new file mode 100644 index 000000000000..c5d2270d2657 --- /dev/null +++ b/device/celestica/x86_64-cel_silverstone-r0/sonic_platform/psu.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python + +############################################################################# +# Celestica +# +# Module contains an implementation of SONiC Platform Base API and +# provides the PSUs status which are available in the platform +# +############################################################################# + +import os +import re +import math +import sonic_platform + +try: + from sonic_platform_base.psu_base import PsuBase + from helper import APIHelper + from sonic_platform.fan import Fan +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +PSU_NAME_LIST = ["PSU-1", "PSU-2"] +PSU_NUM_FAN = [1, 1] + +IPMI_SENSOR_NETFN = "0x04" +IPMI_OEM_NETFN = "0x3A" +IPMI_SS_READ_CMD = "0x2D {}" +IPMI_SET_PSU_LED_CMD = "0x07 0x02 {}" +IPMI_GET_PSU_LED_CMD = "0x08 0x02" +IPMI_FRU_MODEL_KEY = "Board Part Number" +IPMI_FRU_SERIAL_KEY = "Board Serial" + +PSU_LED_OFF_CMD = "0x00" +PSU_LED_GREEN_CMD = "0x01" +PSU_LED_AMBER_CMD = "0x02" + +PSU1_VOUT_SS_ID = "0x36" +PSU1_COUT_SS_ID = "0x37" +PSU1_POUT_SS_ID = "0x38" +PSU1_STATUS_REG = "0x39" + +PSU2_VOUT_SS_ID = "0x40" +PSU2_COUT_SS_ID = "0x41" +PSU2_POUT_SS_ID = "0x42" +PSU2_STATUS_REG = "0x2f" + +PSU1_FRU_ID = 3 + +SS_READ_OFFSET = 0 + + +class Psu(PsuBase): + """Platform-specific Psu class""" + + def __init__(self, psu_index): + PsuBase.__init__(self) + self.index = psu_index + for fan_index in range(0, PSU_NUM_FAN[self.index]): + fan = Fan(fan_index, 0, is_psu_fan=True, psu_index=self.index) + self._fan_list.append(fan) + self._api_helper = APIHelper() + + def find_value(self, in_string): + result = re.search("^.+ ([0-9a-f]{2}) .+$", in_string) + return result.group(1) if result else result + + def get_voltage(self): + """ + Retrieves current PSU voltage output + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + psu_voltage = 0.0 + psu_vout_key = globals()['PSU{}_VOUT_SS_ID'.format(self.index+1)] + status, raw_ss_read = self._api_helper.ipmi_raw( + IPMI_SENSOR_NETFN, IPMI_SS_READ_CMD.format(psu_vout_key)) + ss_read = raw_ss_read.split()[SS_READ_OFFSET] + # Formula: Rx1x10^-1 + psu_voltage = int(ss_read, 16) * math.pow(10, -1) + + return psu_voltage + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + psu_current = 0.0 + psu_cout_key = globals()['PSU{}_COUT_SS_ID'.format(self.index+1)] + status, raw_ss_read = self._api_helper.ipmi_raw( + IPMI_SENSOR_NETFN, IPMI_SS_READ_CMD.format(psu_cout_key)) + ss_read = raw_ss_read.split()[SS_READ_OFFSET] + # Formula: Rx5x10^-1 + psu_current = int(ss_read, 16) * 5 * math.pow(10, -1) + + return psu_current + + def get_power(self): + """ + Retrieves current energy supplied by PSU + Returns: + A float number, the power in watts, e.g. 302.6 + """ + psu_power = 0.0 + psu_pout_key = globals()['PSU{}_POUT_SS_ID'.format(self.index+1)] + status, raw_ss_read = self._api_helper.ipmi_raw( + IPMI_SENSOR_NETFN, IPMI_SS_READ_CMD.format(psu_pout_key)) + ss_read = raw_ss_read.split()[SS_READ_OFFSET] + # Formula: Rx6x10^0 + psu_power = int(ss_read, 16) * 6 + return psu_power + + def get_powergood_status(self): + """ + Retrieves the powergood status of PSU + Returns: + A boolean, True if PSU has stablized its output voltages and passed all + its internal self-tests, False if not. + """ + return self.get_status() + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the PSU status LED + Note: Only support green and off + Returns: + bool: True if status LED state is set successfully, False if not + Note + Set manual + ipmitool raw 0x3a 0x09 0x2 0x0 + """ + led_cmd = { + self.STATUS_LED_COLOR_GREEN: PSU_LED_GREEN_CMD, + self.STATUS_LED_COLOR_AMBER: PSU_LED_AMBER_CMD, + self.STATUS_LED_COLOR_OFF: PSU_LED_OFF_CMD + }.get(color) + + status, set_led = self._api_helper.ipmi_raw( + IPMI_OEM_NETFN, IPMI_SET_PSU_LED_CMD.format(led_cmd)) + set_status_led = False if not status else True + + return set_status_led + + def get_status_led(self): + """ + Gets the state of the PSU status LED + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings above + """ + status, hx_color = self._api_helper.ipmi_raw( + IPMI_OEM_NETFN, IPMI_GET_PSU_LED_CMD) + + status_led = { + "00": self.STATUS_LED_COLOR_OFF, + "01": self.STATUS_LED_COLOR_GREEN, + "02": self.STATUS_LED_COLOR_AMBER, + }.get(hx_color, self.STATUS_LED_COLOR_OFF) + + return status_led + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return PSU_NAME_LIST[self.index] + + def get_presence(self): + """ + Retrieves the presence of the PSU + Returns: + bool: True if PSU is present, False if not + """ + psu_presence = False + psu_pstatus_key = globals()['PSU{}_STATUS_REG'.format(self.index+1)] + status, raw_status_read = self._api_helper.ipmi_raw( + IPMI_SENSOR_NETFN, IPMI_SS_READ_CMD.format(psu_pstatus_key)) + status_byte = self.find_value(raw_status_read) + + if status: + presence_int = (int(status_byte, 16) >> 0) & 1 + psu_presence = True if presence_int else False + + return psu_presence + + def get_model(self): + """ + Retrieves the model number (or part number) of the device + Returns: + string: Model/part number of device + """ + model = "Unknown" + ipmi_fru_idx = self.index + PSU1_FRU_ID + status, raw_model = self._api_helper.ipmi_fru_id( + ipmi_fru_idx, IPMI_FRU_MODEL_KEY) + + fru_pn_list = raw_model.split() + if len(fru_pn_list) > 4: + model = fru_pn_list[4] + + return model + + def get_serial(self): + """ + Retrieves the serial number of the device + Returns: + string: Serial number of device + """ + serial = "Unknown" + ipmi_fru_idx = self.index + PSU1_FRU_ID + status, raw_model = self._api_helper.ipmi_fru_id( + ipmi_fru_idx, IPMI_FRU_SERIAL_KEY) + + fru_sr_list = raw_model.split() + if len(fru_sr_list) > 3: + serial = fru_sr_list[3] + + return serial + + def get_status(self): + """ + Retrieves the operational status of the device + Returns: + A boolean value, True if device is operating properly, False if not + """ + psu_status = False + psu_pstatus_key = globals()['PSU{}_STATUS_REG'.format(self.index+1)] + status, raw_status_read = self._api_helper.ipmi_raw( + IPMI_SENSOR_NETFN, IPMI_SS_READ_CMD.format(psu_pstatus_key)) + status_byte = self.find_value(raw_status_read) + + if status: + failure_detected = (int(status_byte, 16) >> 1) & 1 + input_lost = (int(status_byte, 16) >> 3) & 1 + psu_status = False if (input_lost or failure_detected) else True + + return psu_status