Skip to content

Commit

Permalink
[platform/device] - Implement Silverstone platform API [PSU] (#3784)
Browse files Browse the repository at this point in the history
Implement part of the Chassis and Fan related APIs.

Psu APIs

get_voltage()
get_current()
get_power()
get_powergood_status()
set_status_led()
get_status_led()
Update Fan APIs to support PSU FAN

get_direction()
get_speed()
get_target_speed()
get_speed_tolerance()
get_target_speed()
PSU APIs base on Device API

get_name()
get_presence()
get_model()
get_serial()
get_status()

- How I did it

Implement PSU APIs
Update FAN API to support PSU Fans
Add PSU object to Chassis API
  • Loading branch information
Wirut Getbamrung authored and lguohan committed Dec 5, 2019
1 parent 1848fb2 commit 77b8e74
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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):
"""
Expand Down
36 changes: 31 additions & 5 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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"""
Expand All @@ -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

Expand All @@ -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))

Expand Down Expand Up @@ -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))
Expand All @@ -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,
Expand All @@ -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))
Expand Down Expand Up @@ -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)))
Expand All @@ -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(
Expand All @@ -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(
Expand Down
243 changes: 243 additions & 0 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/psu.py
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 77b8e74

Please sign in to comment.