Skip to content

Commit

Permalink
[device/celestica]: Add thermalctld support on DX010 platform APIs (#…
Browse files Browse the repository at this point in the history
…6089)

**- Why I did it**
- The thermalctld daemon on the Pmon docker requires support from the thermal manager API.

**- How I did it**
- Removed the old function for detecting a faulty fan.
- Removed the old function for detecting excess temperature.
- Implement thermal_manager APIs based on ThermalManagerBase
- Implement thermal_conditions APIs based on ThermalPolicyConditionBase
- Implement thermal_actions APIs based on ThermalPolicyActionBase
- Implement thermal_info APIs based on ThermalPolicyInfoBase
- Add thermal_policy.json
  • Loading branch information
Wirut Getbamrung authored and abdosi committed Feb 19, 2021
1 parent 06334ff commit a5de910
Show file tree
Hide file tree
Showing 16 changed files with 647 additions and 145 deletions.
3 changes: 1 addition & 2 deletions device/celestica/x86_64-cel_seastone-r0/fancontrol-B2F
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ MINSTOP=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/
MINPWM=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/pwm5=89 13-004d/pwm1=89 13-004d/pwm2=89 13-004d/pwm3=89 13-004d/pwm4=89 13-004d/pwm5=89
MAXPWM=13-002e/pwm1=255 13-002e/pwm2=255 13-002e/pwm3=255 13-002e/pwm4=255 13-002e/pwm5=255 13-004d/pwm1=255 13-004d/pwm2=255 13-004d/pwm3=255 13-004d/pwm4=255 13-004d/pwm5=255
THYST=13-002e/pwm1=3 13-002e/pwm2=3 13-002e/pwm3=3 13-002e/pwm4=3 13-002e/pwm5=3 13-004d/pwm1=3 13-004d/pwm2=3 13-004d/pwm3=3 13-004d/pwm4=3 13-004d/pwm5=3
MAXTEMPCRIT=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=65 /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=75
MAXTEMPTYPE=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=ASIC /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=CPU

3 changes: 1 addition & 2 deletions device/celestica/x86_64-cel_seastone-r0/fancontrol-F2B
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ MINSTOP=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/
MINPWM=13-002e/pwm1=89 13-002e/pwm2=89 13-002e/pwm3=89 13-002e/pwm4=89 13-002e/pwm5=89 13-004d/pwm1=89 13-004d/pwm2=89 13-004d/pwm3=89 13-004d/pwm4=89 13-004d/pwm5=89
MAXPWM=13-002e/pwm1=255 13-002e/pwm2=255 13-002e/pwm3=255 13-002e/pwm4=255 13-002e/pwm5=255 13-004d/pwm1=255 13-004d/pwm2=255 13-004d/pwm3=255 13-004d/pwm4=255 13-004d/pwm5=255
THYST=13-002e/pwm1=3 13-002e/pwm2=3 13-002e/pwm3=3 13-002e/pwm4=3 13-002e/pwm5=3 13-004d/pwm1=3 13-004d/pwm2=3 13-004d/pwm3=3 13-004d/pwm4=3 13-004d/pwm5=3
MAXTEMPCRIT=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=75 /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=75
MAXTEMPTYPE=/sys/bus/i2c/devices/7-004a/hwmon/hwmon*/temp1_input=ASIC /sys/bus/i2c/devices/14-0048/hwmon/hwmon*/temp1_input=CPU

Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ def __initialize_fan(self):

def __initialize_thermals(self):
from sonic_platform.thermal import Thermal
airflow = self.__get_air_flow()
for index in range(0, NUM_THERMAL):
thermal = Thermal(index)
thermal = Thermal(index, airflow)
self._thermal_list.append(thermal)

def __initialize_eeprom(self):
Expand All @@ -82,6 +83,11 @@ def __initialize_components(self):
component = Component(index)
self._component_list.append(component)

def __get_air_flow(self):
air_flow_path = '/usr/share/sonic/device/{}/fan_airflow'.format(self._api_helper.platform) if self.is_host else '/usr/share/sonic/platform/fan_airflow'
air_flow = self._api_helper.read_one_line_file(air_flow_path)
return air_flow or 'B2F'

def get_base_mac(self):
"""
Retrieves the base MAC address for the chassis
Expand Down Expand Up @@ -261,3 +267,7 @@ def get_status(self):
A boolean value, True if device is operating properly, False if not
"""
return True

def get_thermal_manager(self):
from .thermal_manager import ThermalManager
return ThermalManager
18 changes: 17 additions & 1 deletion device/celestica/x86_64-cel_seastone-r0/sonic_platform/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,4 +306,20 @@ def get_status(self):
Returns:
A boolean value, True if device is operating properly, False if not
"""
return self.get_presence() and self.get_speed() > 0
status = 1
if self.is_psu_fan:
fan_fault_sysfs_name = "fan1_fault"
fan_fault_sysfs_path = self.__search_file_by_name(
self.psu_hwmon_path, fan_fault_sysfs_name)
status = self._api_helper.read_one_line_file(fan_fault_sysfs_path)

elif self.get_presence():
chip = self.emc2305_chip_mapping[self.fan_index]
device = chip['device']
fan_index = chip['index_map']
sysfs_path = "%s%s/%s" % (
EMC2305_PATH, device, 'fan{}_fault')
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
status = self._api_helper.read_one_line_file(sysfs_path)

return False if int(status) != 0 else True
144 changes: 114 additions & 30 deletions device/celestica/x86_64-cel_seastone-r0/sonic_platform/thermal.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,75 @@
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

THERMAL_INFO = {
0: {
"F2B_max": 50,
"B2F_max": 55,
"postion": "asic",
"name": "Front-panel temp sensor 1",
"i2c_path": "i2c-5/5-0048/hwmon/hwmon1", # u4 system-inlet
},
1: {
"F2B_max": 50,
"B2F_max": 55,
"postion": "asic",
"name": "Front-panel temp sensor 2",
"i2c_path": "i2c-6/6-0049/hwmon/hwmon2", # u2 system-inlet
},
2: {
"F2B_max": 70,
"F2B_max_crit": 75,
"B2F_max": 60,
"B2F_max_crit": 65,
"postion": "asic",
"name": "ASIC temp sensor",
"i2c_path": "i2c-7/7-004a/hwmon/hwmon3", # u44 bmc56960-on-board
},
3: {
"F2B_max": 70,
"F2B_max_crit": 75,
"B2F_max": 70,
"B2F_max_crit": 75,
"postion": "cpu",
"name": "Rear-panel temp sensor 1",
"i2c_path": "i2c-14/14-0048/hwmon/hwmon4", # u9200 cpu-on-board
},
4: {
"F2B_max": 70,
"B2F_max": 55,
"postion": "cpu",
"name": "Rear-panel temp sensor 2",
"i2c_path": "i2c-15/15-004e/hwmon/hwmon5" # u9201 system-outlet
}
}
NULL_VAL = "N/A"
I2C_ADAPTER_PATH = "/sys/class/i2c-adapter"


class Thermal(ThermalBase):
"""Platform-specific Thermal class"""

THERMAL_NAME_LIST = []
I2C_ADAPTER_PATH = "/sys/class/i2c-adapter"
SS_CONFIG_PATH = "/usr/share/sonic/device/x86_64-cel_seastone-r0/sensors.conf"

def __init__(self, thermal_index):
def __init__(self, thermal_index, airflow):
self.index = thermal_index
self._api_helper = APIHelper()

# Add thermal name
self.THERMAL_NAME_LIST.append("Front-panel temp sensor 1")
self.THERMAL_NAME_LIST.append("Front-panel temp sensor 2")
self.THERMAL_NAME_LIST.append("ASIC temp sensor")
self.THERMAL_NAME_LIST.append("Rear-panel temp sensor 1")
self.THERMAL_NAME_LIST.append("Rear-panel temp sensor 2")

# Set hwmon path
i2c_path = {
0: "i2c-5/5-0048/hwmon/hwmon1", # u4 system-inlet
1: "i2c-6/6-0049/hwmon/hwmon2", # u2 system-inlet
2: "i2c-7/7-004a/hwmon/hwmon3", # u44 bmc56960-on-board
3: "i2c-14/14-0048/hwmon/hwmon4", # u9200 cpu-on-board
4: "i2c-15/15-004e/hwmon/hwmon5" # u9201 system-outlet
}.get(self.index, None)

self.hwmon_path = "{}/{}".format(self.I2C_ADAPTER_PATH, i2c_path)
self.ss_key = self.THERMAL_NAME_LIST[self.index]
self._airflow = airflow
self._thermal_info = THERMAL_INFO[self.index]
self._hwmon_path = "{}/{}".format(I2C_ADAPTER_PATH,
self._thermal_info["i2c_path"])
self.name = self.get_name()
self.postion = self._thermal_info["postion"]
self.ss_index = 1

def __get_temp(self, temp_file):
temp_file_path = os.path.join(self.hwmon_path, temp_file)
temp_file_path = os.path.join(self._hwmon_path, temp_file)
raw_temp = self._api_helper.read_txt_file(temp_file_path)
temp = float(raw_temp)/1000
return float("{:.3f}".format(temp))

def __set_threshold(self, file_name, temperature):
temp_file_path = os.path.join(self.hwmon_path, file_name)
temp_file_path = os.path.join(self._hwmon_path, file_name)
try:
with open(temp_file_path, 'w') as fd:
fd.write(str(temperature))
Expand All @@ -82,8 +111,17 @@ def get_high_threshold(self):
A float number, the high threshold temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
temp_file = "temp{}_max".format(self.ss_index)
return self.__get_temp(temp_file)
max_crit_key = '{}_max'.format(self._airflow)
return self._thermal_info.get(max_crit_key, None)

def get_low_threshold(self):
"""
Retrieves the low threshold temperature of thermal
Returns:
A float number, the low threshold temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
return 0.0

def set_high_threshold(self, temperature):
"""
Expand All @@ -104,7 +142,7 @@ def set_high_threshold(self, temperature):
f.seek(0)
ss_found = False
for idx, val in enumerate(content):
if self.ss_key in val:
if self.name in val:
ss_found = True
elif ss_found and temp_file in val:
content[idx] = " set {} {}\n".format(
Expand All @@ -117,13 +155,43 @@ def set_high_threshold(self, temperature):

return is_set & file_set

def set_low_threshold(self, temperature):
"""
Sets the low threshold temperature of thermal
Args :
temperature: A float number up to nearest thousandth of one degree Celsius,
e.g. 30.125
Returns:
A boolean, True if threshold is set successfully, False if not
"""
return False

def get_high_critical_threshold(self):
"""
Retrieves the high critical threshold temperature of thermal
Returns:
A float number, the high critical threshold temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
max_crit_key = '{}_max_crit'.format(self._airflow)
return self._thermal_info.get(max_crit_key, None)

def get_low_critical_threshold(self):
"""
Retrieves the low critical threshold temperature of thermal
Returns:
A float number, the low critical threshold temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
return 0.0

def get_name(self):
"""
Retrieves the name of the thermal device
Returns:
string: The name of the thermal device
"""
return self.THERMAL_NAME_LIST[self.index]
return self._thermal_info["name"]

def get_presence(self):
"""
Expand All @@ -132,9 +200,25 @@ def get_presence(self):
bool: True if PSU is present, False if not
"""
temp_file = "temp{}_input".format(self.ss_index)
temp_file_path = os.path.join(self.hwmon_path, temp_file)
temp_file_path = os.path.join(self._hwmon_path, temp_file)
return os.path.isfile(temp_file_path)

def get_model(self):
"""
Retrieves the model number (or part number) of the device
Returns:
string: Model/part number of device
"""
return NULL_VAL

def get_serial(self):
"""
Retrieves the serial number of the device
Returns:
string: Serial number of device
"""
return NULL_VAL

def get_status(self):
"""
Retrieves the operational status of the device
Expand All @@ -145,7 +229,7 @@ def get_status(self):
return False

fault_file = "temp{}_fault".format(self.ss_index)
fault_file_path = os.path.join(self.hwmon_path, fault_file)
fault_file_path = os.path.join(self._hwmon_path, fault_file)
if not os.path.isfile(fault_file_path):
return True

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

from sonic_platform_base.sonic_thermal_control.thermal_action_base import ThermalPolicyActionBase
from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object
from .thermal_infos import ChassisInfo
from .helper import APIHelper


@thermal_json_object('thermal_control.control')
class ControlThermalAlgoAction(ThermalPolicyActionBase):
"""
Action to control the thermal control algorithm
"""
# JSON field definition
JSON_FIELD_STATUS = 'status'

def __init__(self):
self.status = True

def load_from_json(self, json_obj):
"""
Construct ControlThermalAlgoAction via JSON. JSON example:
{
"type": "thermal_control.control"
"status": "true"
}
:param json_obj: A JSON object representing a ControlThermalAlgoAction action.
:return:
"""
if ControlThermalAlgoAction.JSON_FIELD_STATUS in json_obj:
status_str = json_obj[ControlThermalAlgoAction.JSON_FIELD_STATUS].lower()
if status_str == 'true':
self.status = True
elif status_str == 'false':
self.status = False
else:
raise ValueError('Invalid {} field value, please specify true of false'.
format(ControlThermalAlgoAction.JSON_FIELD_STATUS))
else:
raise ValueError('ControlThermalAlgoAction '
'missing mandatory field {} in JSON policy file'.
format(ControlThermalAlgoAction.JSON_FIELD_STATUS))

def execute(self, thermal_info_dict):
"""
Disable thermal control algorithm
:param thermal_info_dict: A dictionary stores all thermal information.
:return:
"""
if ChassisInfo.INFO_NAME in thermal_info_dict:
chassis_info_obj = thermal_info_dict[ChassisInfo.INFO_NAME]
chassis = chassis_info_obj.get_chassis()
thermal_manager = chassis.get_thermal_manager()
if self.status:
thermal_manager.start_thermal_control_algorithm()
else:
thermal_manager.stop_thermal_control_algorithm()


@thermal_json_object('switch.power_cycling')
class SwitchPolicyAction(ThermalPolicyActionBase):
"""
Base class for thermal action. Once all thermal conditions in a thermal policy are matched,
all predefined thermal action will be executed.
"""

def execute(self, thermal_info_dict):
"""
Take action when thermal condition matches. For example, power cycle the switch.
:param thermal_info_dict: A dictionary stores all thermal information.
:return:
"""
thermal_overload_position_path = '/tmp/thermal_overload_position'
thermal_overload_position = APIHelper().read_one_line_file(
thermal_overload_position_path)

cmd = 'bash /usr/share/sonic/platform/thermal_overload_control.sh {}'.format(
thermal_overload_position)
APIHelper().run_command(cmd)
Loading

0 comments on commit a5de910

Please sign in to comment.