[device/celestica]: Add thermalctld support on Haliburton platform APIs (#6493)
- 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
This commit is contained in:
parent
e1ff8b6ad6
commit
cfda77b3de
@ -1,13 +1,11 @@
|
|||||||
# Configuration file generated by pwmconfig, changes will be lost
|
# Configuration file generated by pwmconfig, changes will be lost
|
||||||
INTERVAL=2
|
INTERVAL=2
|
||||||
DEVPATH=hwmon3=devices/pci0000:00/0000:00:13.0/i2c-0/i2c-8/i2c-23/23-004d hwmon2=devices/pci0000:00/0000:00:13.0/i2c-0/i2c-8/i2c-11/11-001a
|
FCTEMPS=23-004d/pwm1=/sys/bus/i2c/devices/11-001a/hwmon/hwmon*/temp1_input 23-004d/pwm2=/sys/bus/i2c/devices/11-001a/hwmon/hwmon*/temp1_input 23-004d/pwm4=/sys/bus/i2c/devices/11-001a/hwmon/hwmon*/temp1_input
|
||||||
DEVNAME=hwmon3=emc2305 hwmon2=max6699
|
FCFANS=23-004d/pwm1=23-004d/fan1_input 23-004d/pwm2=23-004d/fan2_input 23-004d/pwm4=23-004d/fan4_input
|
||||||
FCTEMPS=hwmon3/device/pwm1=hwmon2/temp1_input hwmon3/device/pwm2=hwmon2/temp1_input hwmon3/device/pwm4=hwmon2/temp1_input
|
MINTEMP=23-004d/pwm1=27 23-004d/pwm2=27 23-004d/pwm4=27
|
||||||
FCFANS=hwmon3/device/pwm1=hwmon3/device/fan1_input hwmon3/device/pwm2=hwmon3/device/fan2_input hwmon3/device/pwm4=hwmon3/device/fan4_input
|
MAXTEMP=23-004d/pwm1=46 23-004d/pwm2=46 23-004d/pwm4=46
|
||||||
MINTEMP=hwmon3/device/pwm1=27 hwmon3/device/pwm2=27 hwmon3/device/pwm4=27
|
MINSTART=23-004d/pwm1=102 23-004d/pwm2=102 23-004d/pwm4=102
|
||||||
MAXTEMP=hwmon3/device/pwm1=46 hwmon3/device/pwm2=46 hwmon3/device/pwm4=46
|
MINSTOP=23-004d/pwm1=102 23-004d/pwm2=102 23-004d/pwm4=102
|
||||||
MINSTART=hwmon3/device/pwm1=102 hwmon3/device/pwm2=102 hwmon3/device/pwm4=102
|
MINPWM=23-004d/pwm1=102 23-004d/pwm2=102 23-004d/pwm4=102
|
||||||
MINSTOP=hwmon3/device/pwm1=102 hwmon3/device/pwm2=102 hwmon3/device/pwm4=102
|
MAXPWM=23-004d/pwm1=255 23-004d/pwm2=255 23-004d/pwm4=255
|
||||||
MINPWM=hwmon3/device/pwm1=102 hwmon3/device/pwm2=102 hwmon3/device/pwm4=102
|
THYST=23-004d/pwm1=2 23-004d/pwm2=2 23-004d/pwm4=2
|
||||||
MAXPWM=hwmon3/device/pwm1=255 hwmon3/device/pwm2=255 hwmon3/device/pwm4=255
|
|
||||||
THYST=hwmon3/device/pwm1=2 hwmon3/device/pwm2=2 hwmon3/device/pwm4=2
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
# Configuration file generated by pwmconfig, changes will be lost
|
# Configuration file generated by pwmconfig, changes will be lost
|
||||||
INTERVAL=2
|
INTERVAL=2
|
||||||
DEVPATH=hwmon3=devices/pci0000:00/0000:00:13.0/i2c-0/i2c-8/i2c-23/23-004d hwmon2=devices/pci0000:00/0000:00:13.0/i2c-0/i2c-8/i2c-11/11-001a
|
FCTEMPS=23-004d/pwm1=/sys/bus/i2c/devices/11-001a/hwmon/hwmon*/temp3_input 23-004d/pwm2=/sys/bus/i2c/devices/11-001a/hwmon/hwmon*/temp3_input 23-004d/pwm4=/sys/bus/i2c/devices/11-001a/hwmon/hwmon*/temp3_input
|
||||||
DEVNAME=hwmon3=emc2305 hwmon2=max6697
|
FCFANS=23-004d/pwm1=23-004d/fan1_input 23-004d/pwm2=23-004d/fan2_input 23-004d/pwm4=23-004d/fan4_input
|
||||||
FCTEMPS=hwmon3/device/pwm1=hwmon2/temp3_input hwmon3/device/pwm2=hwmon2/temp3_input hwmon3/device/pwm4=hwmon2/temp3_input
|
MINTEMP=23-004d/pwm1=29 23-004d/pwm2=29 23-004d/pwm4=29
|
||||||
FCFANS=hwmon3/device/pwm1=hwmon3/device/fan1_input hwmon3/device/pwm2=hwmon3/device/fan2_input hwmon3/device/pwm4=hwmon3/device/fan4_input
|
MAXTEMP=23-004d/pwm1=46 23-004d/pwm2=46 23-004d/pwm4=46
|
||||||
MINTEMP=hwmon3/device/pwm1=29 hwmon3/device/pwm2=29 hwmon3/device/pwm4=29
|
MINSTART=23-004d/pwm1=102 23-004d/pwm2=102 23-004d/pwm4=102
|
||||||
MAXTEMP=hwmon3/device/pwm1=46 hwmon3/device/pwm2=46 hwmon3/device/pwm4=46
|
MINSTOP=23-004d/pwm1=102 23-004d/pwm2=102 23-004d/pwm4=102
|
||||||
MINSTART=hwmon3/device/pwm1=102 hwmon3/device/pwm2=102 hwmon3/device/pwm4=102
|
MINPWM=23-004d/pwm1=102 23-004d/pwm2=102 23-004d/pwm4=102
|
||||||
MINSTOP=hwmon3/device/pwm1=102 hwmon3/device/pwm2=102 hwmon3/device/pwm4=102
|
MAXPWM=23-004d/pwm1=255 23-004d/pwm2=255 23-004d/pwm4=255
|
||||||
MINPWM=hwmon3/device/pwm1=102 hwmon3/device/pwm2=102 hwmon3/device/pwm4=102
|
THYST=23-004d/pwm1=2 23-004d/pwm2=2 23-004d/pwm4=2
|
||||||
MAXPWM=hwmon3/device/pwm1=255 hwmon3/device/pwm2=255 hwmon3/device/pwm4=255
|
|
||||||
THYST=hwmon3/device/pwm1=2 hwmon3/device/pwm2=2 hwmon3/device/pwm4=2
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
__all__ = ["platform", "chassis"]
|
from . import chassis
|
||||||
from sonic_platform import *
|
from . import platform
|
||||||
|
@ -6,18 +6,12 @@
|
|||||||
#
|
#
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import json
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import sys
|
||||||
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
|
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
|
||||||
from sonic_platform_base.chassis_base import ChassisBase
|
from sonic_platform_base.chassis_base import ChassisBase
|
||||||
from sonic_py_common import device_info
|
from .common import Common
|
||||||
from .event import SfpEvent
|
from .event import SfpEvent
|
||||||
from .helper import APIHelper
|
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
@ -38,11 +32,10 @@ class Chassis(ChassisBase):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
ChassisBase.__init__(self)
|
ChassisBase.__init__(self)
|
||||||
self._api_helper = APIHelper()
|
self._api_common = Common()
|
||||||
self.sfp_module_initialized = False
|
self.sfp_module_initialized = False
|
||||||
self.__initialize_eeprom()
|
self.__initialize_eeprom()
|
||||||
self.is_host = self._api_helper.is_host()
|
self.is_host = self._api_common.is_host()
|
||||||
|
|
||||||
|
|
||||||
if not self.is_host:
|
if not self.is_host:
|
||||||
self.__initialize_fan()
|
self.__initialize_fan()
|
||||||
@ -81,8 +74,9 @@ class Chassis(ChassisBase):
|
|||||||
|
|
||||||
def __initialize_thermals(self):
|
def __initialize_thermals(self):
|
||||||
from sonic_platform.thermal import Thermal
|
from sonic_platform.thermal import Thermal
|
||||||
|
airflow = self.__get_air_flow()
|
||||||
for index in range(0, NUM_THERMAL):
|
for index in range(0, NUM_THERMAL):
|
||||||
thermal = Thermal(index)
|
thermal = Thermal(index, airflow)
|
||||||
self._thermal_list.append(thermal)
|
self._thermal_list.append(thermal)
|
||||||
|
|
||||||
def __initialize_eeprom(self):
|
def __initialize_eeprom(self):
|
||||||
@ -95,17 +89,11 @@ class Chassis(ChassisBase):
|
|||||||
component = Component(index)
|
component = Component(index)
|
||||||
self._component_list.append(component)
|
self._component_list.append(component)
|
||||||
|
|
||||||
def __is_host(self):
|
def __get_air_flow(self):
|
||||||
return os.system(HOST_CHK_CMD) == 0
|
air_flow_path = '/usr/share/sonic/device/{}/fan_airflow'.format(
|
||||||
|
self._api_common.platform) if self.is_host else '/usr/share/sonic/platform/fan_airflow'
|
||||||
def __read_txt_file(self, file_path):
|
air_flow = self._api_common.read_txt_file(air_flow_path)
|
||||||
try:
|
return air_flow or 'B2F'
|
||||||
with open(file_path, 'r') as fd:
|
|
||||||
data = fd.read()
|
|
||||||
return data.strip()
|
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_base_mac(self):
|
def get_base_mac(self):
|
||||||
"""
|
"""
|
||||||
@ -116,6 +104,14 @@ class Chassis(ChassisBase):
|
|||||||
"""
|
"""
|
||||||
return self._eeprom.get_mac()
|
return self._eeprom.get_mac()
|
||||||
|
|
||||||
|
def get_serial_number(self):
|
||||||
|
"""
|
||||||
|
Retrieves the hardware serial number for the chassis
|
||||||
|
Returns:
|
||||||
|
A string containing the hardware serial number for this chassis.
|
||||||
|
"""
|
||||||
|
return self._eeprom.get_serial()
|
||||||
|
|
||||||
def get_system_eeprom_info(self):
|
def get_system_eeprom_info(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the full content of system EEPROM information for the chassis
|
Retrieves the full content of system EEPROM information for the chassis
|
||||||
@ -141,7 +137,7 @@ class Chassis(ChassisBase):
|
|||||||
reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
|
reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
|
||||||
hw_reboot_cause = self._component_list[0].get_register_value(
|
hw_reboot_cause = self._component_list[0].get_register_value(
|
||||||
RESET_REGISTER)
|
RESET_REGISTER)
|
||||||
sw_reboot_cause = self.__read_txt_file(
|
sw_reboot_cause = self._api_common.read_txt_file(
|
||||||
self._reboot_cause_path) or "Unknown"
|
self._reboot_cause_path) or "Unknown"
|
||||||
|
|
||||||
if hw_reboot_cause == "0x55":
|
if hw_reboot_cause == "0x55":
|
||||||
@ -151,8 +147,12 @@ class Chassis(ChassisBase):
|
|||||||
reboot_cause = self.REBOOT_CAUSE_POWER_LOSS
|
reboot_cause = self.REBOOT_CAUSE_POWER_LOSS
|
||||||
elif hw_reboot_cause == "0x33":
|
elif hw_reboot_cause == "0x33":
|
||||||
reboot_cause = self.REBOOT_CAUSE_WATCHDOG
|
reboot_cause = self.REBOOT_CAUSE_WATCHDOG
|
||||||
|
elif hw_reboot_cause == "0x88":
|
||||||
|
reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU
|
||||||
|
elif hw_reboot_cause == "0x99":
|
||||||
|
reboot_cause = self.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC
|
||||||
else:
|
else:
|
||||||
reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
|
reboot_cause = self.REBOOT_CAUSE_NON_HARDWARE
|
||||||
description = 'Unknown reason'
|
description = 'Unknown reason'
|
||||||
|
|
||||||
return (reboot_cause, description)
|
return (reboot_cause, description)
|
||||||
@ -251,6 +251,14 @@ class Chassis(ChassisBase):
|
|||||||
index, len(self._sfp_list)))
|
index, len(self._sfp_list)))
|
||||||
return sfp
|
return sfp
|
||||||
|
|
||||||
|
##############################################################
|
||||||
|
################## ThermalManager methods ####################
|
||||||
|
##############################################################
|
||||||
|
|
||||||
|
def get_thermal_manager(self):
|
||||||
|
from .thermal_manager import ThermalManager
|
||||||
|
return ThermalManager
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
###################### Device methods ########################
|
###################### Device methods ########################
|
||||||
##############################################################
|
##############################################################
|
||||||
@ -261,13 +269,13 @@ class Chassis(ChassisBase):
|
|||||||
Returns:
|
Returns:
|
||||||
string: The name of the device
|
string: The name of the device
|
||||||
"""
|
"""
|
||||||
return self._api_helper.hwsku
|
return self._api_common.hwsku
|
||||||
|
|
||||||
def get_presence(self):
|
def get_presence(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the presence of the Chassis
|
Retrieves the presence of the PSU
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if Chassis is present, False if not
|
bool: True if PSU is present, False if not
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -285,7 +293,7 @@ class Chassis(ChassisBase):
|
|||||||
Returns:
|
Returns:
|
||||||
string: Serial number of device
|
string: Serial number of device
|
||||||
"""
|
"""
|
||||||
return self._eeprom.get_serial()
|
return self.get_serial_number()
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
@ -294,3 +302,4 @@ class Chassis(ChassisBase):
|
|||||||
A boolean value, True if device is operating properly, False if not
|
A boolean value, True if device is operating properly, False if not
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
283
device/celestica/x86_64-cel_e1031-r0/sonic_platform/common.py
Normal file
283
device/celestica/x86_64-cel_e1031-r0/sonic_platform/common.py
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
import os
|
||||||
|
import imp
|
||||||
|
import yaml
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from sonic_py_common import device_info
|
||||||
|
|
||||||
|
|
||||||
|
class Common:
|
||||||
|
|
||||||
|
DEVICE_PATH = '/usr/share/sonic/device/'
|
||||||
|
PMON_PLATFORM_PATH = '/usr/share/sonic/platform/'
|
||||||
|
CONFIG_DIR = 'sonic_platform_config'
|
||||||
|
|
||||||
|
OUTPUT_SOURCE_IPMI = 'ipmitool'
|
||||||
|
OUTPUT_SOURCE_GIVEN_LIST = 'value_list'
|
||||||
|
OUTPUT_SOURCE_GIVEN_VALUE = 'value'
|
||||||
|
OUTPUT_SOURCE_GIVEN_CLASS = 'class'
|
||||||
|
OUTPUT_SOURCE_SYSFS = 'sysfs_value'
|
||||||
|
OUTPUT_SOURCE_FUNC = 'function'
|
||||||
|
OUTPUT_SOURCE_GIVEN_TXT_FILE = 'txt_file'
|
||||||
|
OUTPUT_SOURCE_GIVEN_VER_HEX_FILE = 'hex_version_file'
|
||||||
|
OUTPUT_SOURCE_GIVEN_VER_HEX_ADDR = 'hex_version_getreg'
|
||||||
|
|
||||||
|
SET_METHOD_IPMI = 'ipmitool'
|
||||||
|
NULL_VAL = 'N/A'
|
||||||
|
HOST_CHK_CMD = "docker > /dev/null 2>&1"
|
||||||
|
REF_KEY = '$ref:'
|
||||||
|
|
||||||
|
def __init__(self, conf=None):
|
||||||
|
self._main_conf = conf
|
||||||
|
(self.platform, self.hwsku) = device_info.get_platform_and_hwsku()
|
||||||
|
|
||||||
|
def run_command(self, command):
|
||||||
|
status = False
|
||||||
|
output = ""
|
||||||
|
try:
|
||||||
|
p = subprocess.Popen(
|
||||||
|
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
raw_data, err = p.communicate()
|
||||||
|
if err == '':
|
||||||
|
status, output = True, raw_data.strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return status, output
|
||||||
|
|
||||||
|
def _clean_input(self, input, config):
|
||||||
|
cleaned_input = input
|
||||||
|
|
||||||
|
ai = config.get('avaliable_input')
|
||||||
|
if ai and input not in ai:
|
||||||
|
return None
|
||||||
|
|
||||||
|
input_translator = config.get('input_translator')
|
||||||
|
if type(input_translator) is dict:
|
||||||
|
cleaned_input = input_translator.get(input)
|
||||||
|
|
||||||
|
elif type(input_translator) is str:
|
||||||
|
cleaned_input = eval(input_translator.format(input))
|
||||||
|
|
||||||
|
return cleaned_input
|
||||||
|
|
||||||
|
def _clean_output(self, index, output, config):
|
||||||
|
output_translator = config.get('output_translator')
|
||||||
|
|
||||||
|
if type(output_translator) is dict:
|
||||||
|
output = output_translator.get(output)
|
||||||
|
elif type(output_translator) is str:
|
||||||
|
output = eval(output_translator.format(output))
|
||||||
|
elif type(output_translator) is list:
|
||||||
|
output = eval(output_translator[index].format(output))
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _ipmi_get(self, index, config):
|
||||||
|
argument = config.get('argument')
|
||||||
|
cmd = config['command'].format(
|
||||||
|
config['argument'][index]) if argument else config['command']
|
||||||
|
status, output = self.run_command(cmd)
|
||||||
|
return output if status else None
|
||||||
|
|
||||||
|
def _sysfs_read(self, index, config):
|
||||||
|
sysfs_path = config.get('sysfs_path')
|
||||||
|
argument = config.get('argument', '')
|
||||||
|
|
||||||
|
if self.REF_KEY in argument:
|
||||||
|
argument = self._main_conf[argument.split(":")[1]]
|
||||||
|
|
||||||
|
if type(argument) is list:
|
||||||
|
sysfs_path = sysfs_path.format(argument[index])
|
||||||
|
|
||||||
|
content = ""
|
||||||
|
try:
|
||||||
|
content = open(sysfs_path)
|
||||||
|
content = content.readline().rstrip()
|
||||||
|
except IOError as e:
|
||||||
|
print("Error: unable to open file: %s" % str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def _sysfs_write(self, index, config, input):
|
||||||
|
sysfs_path = config.get('sysfs_path')
|
||||||
|
argument = config.get('argument', '')
|
||||||
|
|
||||||
|
if self.REF_KEY in argument:
|
||||||
|
argument = self._main_conf[argument.split(":")[1]]
|
||||||
|
|
||||||
|
if type(argument) is list:
|
||||||
|
sysfs_path = sysfs_path.format(argument[index])
|
||||||
|
|
||||||
|
write_offset = int(config.get('write_offset', 0))
|
||||||
|
output = ""
|
||||||
|
try:
|
||||||
|
open_file = open(sysfs_path, "r+")
|
||||||
|
open_file.seek(write_offset)
|
||||||
|
open_file.write(input)
|
||||||
|
open_file.close()
|
||||||
|
except IOError as e:
|
||||||
|
print("Error: unable to open file: %s" % str(e))
|
||||||
|
return False, output
|
||||||
|
return True, output
|
||||||
|
|
||||||
|
def _ipmi_set(self, index, config, input):
|
||||||
|
arg = config['argument'][index].format(input)
|
||||||
|
return self.run_command(config['command'].format(arg))
|
||||||
|
|
||||||
|
def _hex_ver_decode(self, hver, num_of_bits, num_of_points):
|
||||||
|
ver_list = []
|
||||||
|
c_bit = 0
|
||||||
|
bin_val = bin(int(hver, 16))[2:].zfill(num_of_bits)
|
||||||
|
bit_split = num_of_bits / (num_of_points + 1)
|
||||||
|
for x in range(0, num_of_points+1):
|
||||||
|
split_bin = bin_val[c_bit:c_bit+bit_split]
|
||||||
|
ver_list.append(str(int(split_bin, 2)))
|
||||||
|
c_bit += bit_split
|
||||||
|
return '.'.join(ver_list)
|
||||||
|
|
||||||
|
def _get_class(self, config):
|
||||||
|
"""
|
||||||
|
Retreives value of expected attribute
|
||||||
|
Returns:
|
||||||
|
A value of the attribute of object
|
||||||
|
"""
|
||||||
|
path = config['host_path'] if self.is_host() else config['pmon_path']
|
||||||
|
module = imp.load_source(config['class'], path)
|
||||||
|
class_ = getattr(module, config['class'])
|
||||||
|
return class_
|
||||||
|
|
||||||
|
def get_reg(self, path, reg_addr):
|
||||||
|
cmd = "echo {1} > {0}; cat {0}".format(path, reg_addr)
|
||||||
|
status, output = self.run_command(cmd)
|
||||||
|
return output if status else None
|
||||||
|
|
||||||
|
def read_txt_file(self, path):
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
output = f.readline()
|
||||||
|
return output.strip('\n')
|
||||||
|
|
||||||
|
def write_txt_file(self, file_path, value):
|
||||||
|
try:
|
||||||
|
with open(file_path, 'w') as fd:
|
||||||
|
fd.write(str(value))
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_host(self):
|
||||||
|
return os.system(self.HOST_CHK_CMD) == 0
|
||||||
|
|
||||||
|
def load_json_file(self, path):
|
||||||
|
"""
|
||||||
|
Retrieves the json object from json file path
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A json object
|
||||||
|
"""
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
json_data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
return json_data
|
||||||
|
|
||||||
|
def get_config_path(self, config_name):
|
||||||
|
"""
|
||||||
|
Retrieves the path to platform api config directory
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_name: A string containing the name of config file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the path to json file
|
||||||
|
"""
|
||||||
|
return os.path.join(self.DEVICE_PATH, self.platform, self.CONFIG_DIR, config_name) if self.is_host() else os.path.join(self.PMON_PLATFORM_PATH, self.CONFIG_DIR, config_name)
|
||||||
|
|
||||||
|
def get_output(self, index, config, default):
|
||||||
|
"""
|
||||||
|
Retrieves the output for each function base on config
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index: An integer containing the index of device.
|
||||||
|
config: A dict object containing the configuration of specified function.
|
||||||
|
default: A string containing the default output of specified function.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the output of specified function in config
|
||||||
|
"""
|
||||||
|
output_source = config.get('output_source')
|
||||||
|
|
||||||
|
if output_source == self.OUTPUT_SOURCE_IPMI:
|
||||||
|
output = self._ipmi_get(index, config)
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_GIVEN_VALUE:
|
||||||
|
output = config["value"]
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_GIVEN_CLASS:
|
||||||
|
output = self._get_class(config)
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_GIVEN_LIST:
|
||||||
|
output = config["value_list"][index]
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_SYSFS:
|
||||||
|
output = self._sysfs_read(index, config)
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_FUNC:
|
||||||
|
func_conf = self._main_conf[config['function'][index]]
|
||||||
|
output = self.get_output(index, func_conf, default)
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_GIVEN_TXT_FILE:
|
||||||
|
path = config.get('path')
|
||||||
|
output = self.read_txt_file(path)
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_GIVEN_VER_HEX_FILE:
|
||||||
|
path = config.get('path')
|
||||||
|
hex_ver = self.read_txt_file(path)
|
||||||
|
output = self._hex_ver_decode(
|
||||||
|
hex_ver, config['num_of_bits'], config['num_of_points'])
|
||||||
|
|
||||||
|
elif output_source == self.OUTPUT_SOURCE_GIVEN_VER_HEX_ADDR:
|
||||||
|
path = config.get('path')
|
||||||
|
addr = config.get('reg_addr')
|
||||||
|
hex_ver = self.get_reg(path, addr)
|
||||||
|
output = self._hex_ver_decode(
|
||||||
|
hex_ver, config['num_of_bits'], config['num_of_points'])
|
||||||
|
|
||||||
|
else:
|
||||||
|
output = default
|
||||||
|
|
||||||
|
return self._clean_output(index, output, config) or default
|
||||||
|
|
||||||
|
def set_output(self, index, input, config):
|
||||||
|
"""
|
||||||
|
Sets the output of specified function on config
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: A dict object containing the configuration of specified function.
|
||||||
|
index: An integer containing the index of device.
|
||||||
|
input: A string containing the input of specified function.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if set function is successfully, False if not
|
||||||
|
"""
|
||||||
|
cleaned_input = self._clean_input(input, config)
|
||||||
|
if not cleaned_input:
|
||||||
|
return False
|
||||||
|
|
||||||
|
set_method = config.get('set_method')
|
||||||
|
if set_method == self.SET_METHOD_IPMI:
|
||||||
|
output = self._ipmi_set(index, config, cleaned_input)[0]
|
||||||
|
elif set_method == self.OUTPUT_SOURCE_SYSFS:
|
||||||
|
output = self._sysfs_write(index, config, cleaned_input)[0]
|
||||||
|
else:
|
||||||
|
output = False
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
def get_event(self, timeout, config, sfp_list):
|
||||||
|
"""
|
||||||
|
Returns a nested dictionary containing all devices which have
|
||||||
|
experienced a change at chassis level
|
||||||
|
|
||||||
|
"""
|
||||||
|
event_class = self._get_class(config)
|
||||||
|
return event_class(sfp_list).get_event(timeout)
|
@ -6,7 +6,6 @@
|
|||||||
#
|
#
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
import json
|
|
||||||
import os.path
|
import os.path
|
||||||
import shutil
|
import shutil
|
||||||
import shlex
|
import shlex
|
||||||
@ -49,7 +48,7 @@ class Component(ComponentBase):
|
|||||||
rc = process.poll()
|
rc = process.poll()
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return False
|
return False
|
||||||
except:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import glob
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
from array import array
|
|
||||||
|
|
||||||
if sys.version_info.major == 3:
|
if sys.version_info.major == 3:
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@ -52,7 +50,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
|||||||
value = match.group(3).rstrip('\0')
|
value = match.group(3).rstrip('\0')
|
||||||
|
|
||||||
_eeprom_info_dict[idx] = value
|
_eeprom_info_dict[idx] = value
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return _eeprom_info_dict
|
return _eeprom_info_dict
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
|||||||
sys.stdout = StringIO()
|
sys.stdout = StringIO()
|
||||||
try:
|
try:
|
||||||
self.read_eeprom_db()
|
self.read_eeprom_db()
|
||||||
except:
|
except Exception:
|
||||||
decode_output = sys.stdout.getvalue()
|
decode_output = sys.stdout.getvalue()
|
||||||
sys.stdout = original_stdout
|
sys.stdout = original_stdout
|
||||||
return self.__parse_output(decode_output)
|
return self.__parse_output(decode_output)
|
||||||
@ -73,7 +71,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
|||||||
if not os.path.exists(CACHE_ROOT):
|
if not os.path.exists(CACHE_ROOT):
|
||||||
try:
|
try:
|
||||||
os.makedirs(CACHE_ROOT)
|
os.makedirs(CACHE_ROOT)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -82,7 +80,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
|||||||
#
|
#
|
||||||
try:
|
try:
|
||||||
self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE))
|
self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE))
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
e = self.read_eeprom()
|
e = self.read_eeprom()
|
||||||
@ -91,7 +89,7 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.update_cache(e)
|
self.update_cache(e)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.decode_eeprom(e)
|
self.decode_eeprom(e)
|
||||||
@ -112,3 +110,6 @@ class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
|||||||
|
|
||||||
def get_mac(self):
|
def get_mac(self):
|
||||||
return self._eeprom.get('0x24', "Undefined.")
|
return self._eeprom.get('0x24', "Undefined.")
|
||||||
|
|
||||||
|
def get_pn(self):
|
||||||
|
return self._eeprom.get('0x21', "Undefined.")
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
#
|
#
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
import json
|
|
||||||
import math
|
import math
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from sonic_platform_base.fan_base import FanBase
|
from sonic_platform_base.fan_base import FanBase
|
||||||
|
from .common import Common
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
@ -67,25 +67,10 @@ class Fan(FanBase):
|
|||||||
self.STATUS_LED_COLOR_RED: "amber",
|
self.STATUS_LED_COLOR_RED: "amber",
|
||||||
self.STATUS_LED_COLOR_OFF: "off"
|
self.STATUS_LED_COLOR_OFF: "off"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._api_common = Common()
|
||||||
FanBase.__init__(self)
|
FanBase.__init__(self)
|
||||||
|
|
||||||
def __read_txt_file(self, file_path):
|
|
||||||
try:
|
|
||||||
with open(file_path, 'r') as fd:
|
|
||||||
data = fd.read()
|
|
||||||
return data.strip()
|
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def __write_txt_file(self, file_path, value):
|
|
||||||
try:
|
|
||||||
with open(file_path, 'w') as fd:
|
|
||||||
fd.write(str(value))
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __search_file_by_name(self, directory, file_name):
|
def __search_file_by_name(self, directory, file_name):
|
||||||
for dirpath, dirnames, files in os.walk(directory):
|
for dirpath, dirnames, files in os.walk(directory):
|
||||||
for name in files:
|
for name in files:
|
||||||
@ -105,7 +90,8 @@ class Fan(FanBase):
|
|||||||
if not self.is_psu_fan:
|
if not self.is_psu_fan:
|
||||||
fan_direction_file = (FAN_PATH +
|
fan_direction_file = (FAN_PATH +
|
||||||
self.fan_e1031_direction.format(self.fan_tray_index+1))
|
self.fan_e1031_direction.format(self.fan_tray_index+1))
|
||||||
raw = self.__read_txt_file(fan_direction_file).strip('\r\n')
|
raw = self._api_common.read_txt_file(
|
||||||
|
fan_direction_file).strip('\r\n')
|
||||||
direction = self.FAN_DIRECTION_INTAKE if str(
|
direction = self.FAN_DIRECTION_INTAKE if str(
|
||||||
raw).upper() == "F2B" else self.FAN_DIRECTION_EXHAUST
|
raw).upper() == "F2B" else self.FAN_DIRECTION_EXHAUST
|
||||||
|
|
||||||
@ -126,8 +112,8 @@ class Fan(FanBase):
|
|||||||
fan_speed_sysfs_name = "fan{}_input".format(self.fan_index+1)
|
fan_speed_sysfs_name = "fan{}_input".format(self.fan_index+1)
|
||||||
fan_speed_sysfs_path = self.__search_file_by_name(
|
fan_speed_sysfs_path = self.__search_file_by_name(
|
||||||
self.psu_hwmon_path, fan_speed_sysfs_name)
|
self.psu_hwmon_path, fan_speed_sysfs_name)
|
||||||
fan_speed_rpm = self.__read_txt_file(fan_speed_sysfs_path) or 0
|
fan_speed_rpm = self._api_common.read_txt_file(
|
||||||
fan_speed_raw = float(fan_speed_rpm)/PSU_FAN_MAX_RPM * 100
|
fan_speed_sysfs_path) or 0
|
||||||
speed = math.ceil(float(fan_speed_rpm) * 100 / PSU_FAN_MAX_RPM)
|
speed = math.ceil(float(fan_speed_rpm) * 100 / PSU_FAN_MAX_RPM)
|
||||||
elif self.get_presence():
|
elif self.get_presence():
|
||||||
chip = self.emc2305_chip_mapping[self.fan_index]
|
chip = self.emc2305_chip_mapping[self.fan_index]
|
||||||
@ -136,7 +122,7 @@ class Fan(FanBase):
|
|||||||
sysfs_path = "%s%s/%s" % (
|
sysfs_path = "%s%s/%s" % (
|
||||||
EMC2305_PATH, device, EMC2305_FAN_INPUT)
|
EMC2305_PATH, device, EMC2305_FAN_INPUT)
|
||||||
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
|
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
|
||||||
raw = self.__read_txt_file(sysfs_path).strip('\r\n')
|
raw = self._api_common.read_txt_file(sysfs_path).strip('\r\n')
|
||||||
pwm = int(raw, 10) if raw else 0
|
pwm = int(raw, 10) if raw else 0
|
||||||
speed = math.ceil(float(pwm * 100 / EMC2305_MAX_PWM))
|
speed = math.ceil(float(pwm * 100 / EMC2305_MAX_PWM))
|
||||||
|
|
||||||
@ -163,7 +149,7 @@ class Fan(FanBase):
|
|||||||
sysfs_path = "%s%s/%s" % (
|
sysfs_path = "%s%s/%s" % (
|
||||||
EMC2305_PATH, device, EMC2305_FAN_TARGET)
|
EMC2305_PATH, device, EMC2305_FAN_TARGET)
|
||||||
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
|
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
|
||||||
raw = self.__read_txt_file(sysfs_path).strip('\r\n')
|
raw = self._api_common.read_txt_file(sysfs_path).strip('\r\n')
|
||||||
pwm = int(raw, 10) if raw else 0
|
pwm = int(raw, 10) if raw else 0
|
||||||
target = math.ceil(float(pwm) * 100 / EMC2305_MAX_PWM)
|
target = math.ceil(float(pwm) * 100 / EMC2305_MAX_PWM)
|
||||||
|
|
||||||
@ -202,7 +188,7 @@ class Fan(FanBase):
|
|||||||
sysfs_path = "%s%s/%s" % (
|
sysfs_path = "%s%s/%s" % (
|
||||||
EMC2305_PATH, device, EMC2305_FAN_PWM)
|
EMC2305_PATH, device, EMC2305_FAN_PWM)
|
||||||
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
|
sysfs_path = sysfs_path.format(fan_index[self.fan_tray_index])
|
||||||
return self.__write_txt_file(sysfs_path, int(pwm))
|
return self._api_common.write_txt_file(sysfs_path, int(pwm))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -220,7 +206,7 @@ class Fan(FanBase):
|
|||||||
fan_led_file = (FAN_PATH +
|
fan_led_file = (FAN_PATH +
|
||||||
self.fan_e1031_led.format(self.fan_tray_index+1))
|
self.fan_e1031_led.format(self.fan_tray_index+1))
|
||||||
|
|
||||||
set_status_led = self.__write_txt_file(
|
set_status_led = self._api_common.write_txt_file(
|
||||||
fan_led_file, self.fan_e1031_led_col_map[color]) if self.get_presence() else False
|
fan_led_file, self.fan_e1031_led_col_map[color]) if self.get_presence() else False
|
||||||
|
|
||||||
return set_status_led
|
return set_status_led
|
||||||
@ -244,14 +230,55 @@ class Fan(FanBase):
|
|||||||
"""
|
"""
|
||||||
fan_direction_file = (FAN_PATH +
|
fan_direction_file = (FAN_PATH +
|
||||||
self.fan_e1031_presence.format(self.fan_tray_index+1))
|
self.fan_e1031_presence.format(self.fan_tray_index+1))
|
||||||
present_str = self.__read_txt_file(fan_direction_file) or '1'
|
present_str = self._api_common.read_txt_file(fan_direction_file) or '1'
|
||||||
|
|
||||||
return int(present_str) == 0 if not self.is_psu_fan else True
|
return int(present_str) == 0 if not self.is_psu_fan else True
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Retrieves the model number (or part number) of the device
|
||||||
|
Returns:
|
||||||
|
string: Model/part number of device
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return NULL_VAL
|
||||||
|
|
||||||
|
model = NULL_VAL
|
||||||
|
return model
|
||||||
|
|
||||||
|
def get_serial(self):
|
||||||
|
"""
|
||||||
|
Retrieves the serial number of the device
|
||||||
|
Returns:
|
||||||
|
string: Serial number of device
|
||||||
|
"""
|
||||||
|
if self.is_psu_fan:
|
||||||
|
return NULL_VAL
|
||||||
|
|
||||||
|
serial = NULL_VAL
|
||||||
|
return serial
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the operational status of the device
|
Retrieves the operational status of the device
|
||||||
Returns:
|
Returns:
|
||||||
A boolean value, True if device is operating properly, False if not
|
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_common.read_txt_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_common.read_txt_file(sysfs_path)
|
||||||
|
|
||||||
|
return False if int(status) != 0 else True
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sonic_platform
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from sonic_platform_base.psu_base import PsuBase
|
from sonic_platform_base.psu_base import PsuBase
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import subprocess
|
|
||||||
from ctypes import create_string_buffer
|
from ctypes import create_string_buffer
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -159,7 +158,7 @@ class Sfp(SfpBase):
|
|||||||
raw = sysfsfile_eeprom.read(num_bytes)
|
raw = sysfsfile_eeprom.read(num_bytes)
|
||||||
for n in range(0, num_bytes):
|
for n in range(0, num_bytes):
|
||||||
eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2)
|
eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
if sysfsfile_eeprom:
|
if sysfsfile_eeprom:
|
||||||
@ -469,7 +468,6 @@ class Sfp(SfpBase):
|
|||||||
A Boolean, True if tx_disable is enabled, False if disabled
|
A Boolean, True if tx_disable is enabled, False if disabled
|
||||||
"""
|
"""
|
||||||
tx_disable = False
|
tx_disable = False
|
||||||
tx_fault = False
|
|
||||||
status_control_raw = self.__read_eeprom_specific_bytes(
|
status_control_raw = self.__read_eeprom_specific_bytes(
|
||||||
SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
|
SFP_STATUS_CONTROL_OFFSET, SFP_STATUS_CONTROL_WIDTH)
|
||||||
if status_control_raw:
|
if status_control_raw:
|
||||||
@ -602,7 +600,7 @@ class Sfp(SfpBase):
|
|||||||
# Write to eeprom
|
# Write to eeprom
|
||||||
sysfsfile_eeprom.seek(SFP_STATUS_CONTROL_OFFSET)
|
sysfsfile_eeprom.seek(SFP_STATUS_CONTROL_OFFSET)
|
||||||
sysfsfile_eeprom.write(buffer[0])
|
sysfsfile_eeprom.write(buffer[0])
|
||||||
except:
|
except Exception:
|
||||||
#print("Error: unable to open file: %s" % str(e))
|
#print("Error: unable to open file: %s" % str(e))
|
||||||
return False
|
return False
|
||||||
finally:
|
finally:
|
||||||
|
@ -6,16 +6,90 @@
|
|||||||
#
|
#
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import os
|
||||||
from sonic_platform_base.thermal_base import ThermalBase
|
from sonic_platform_base.thermal_base import ThermalBase
|
||||||
|
from .common import Common
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ImportError(str(e) + "- required module not found")
|
raise ImportError(str(e) + "- required module not found")
|
||||||
|
|
||||||
|
|
||||||
|
THERMAL_INFO = {
|
||||||
|
0: {
|
||||||
|
"F2B_max": 55.000,
|
||||||
|
"F2B_max_crit": 58.000,
|
||||||
|
"B2F_max": 55.000,
|
||||||
|
"B2F_max_crit": 58.000,
|
||||||
|
"postion": "asic",
|
||||||
|
"name": "Inlet ambient sensor (Rear to Front)",
|
||||||
|
"ss_index": 1,
|
||||||
|
"i2c_path": "i2c-11/11-001a/hwmon", # U7273
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
"F2B_max": 85.000,
|
||||||
|
"F2B_max_crit": 90.000,
|
||||||
|
"B2F_max": 85.000,
|
||||||
|
"B2F_max_crit": 90.000,
|
||||||
|
"postion": "asic",
|
||||||
|
"name": "Helix shutdown sensor (Rear to Front)",
|
||||||
|
"ss_index": 2,
|
||||||
|
"i2c_path": "i2c-11/11-001a/hwmon", # Q7000
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
"F2B_max": 55.000,
|
||||||
|
"F2B_max_crit": 58.000,
|
||||||
|
"B2F_max": 55.000,
|
||||||
|
"B2F_max_crit": 58.000,
|
||||||
|
"postion": "asic",
|
||||||
|
"name": "Inlet ambient sensor (Front to Rear, right)",
|
||||||
|
"ss_index": 3,
|
||||||
|
"i2c_path": "i2c-11/11-001a/hwmon", # Q7001
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
"F2B_max": 85.000,
|
||||||
|
"F2B_max_crit": 90.000,
|
||||||
|
"B2F_max": 85.000,
|
||||||
|
"B2F_max_crit": 90.000,
|
||||||
|
"postion": "asic",
|
||||||
|
"name": "Helix shutdown sensor (Front to Rear)",
|
||||||
|
"ss_index": 4,
|
||||||
|
"i2c_path": "i2c-11/11-001a/hwmon", # Q7751
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
"F2B_max": 55.000,
|
||||||
|
"F2B_max_crit": 58.000,
|
||||||
|
"B2F_max": 55.000,
|
||||||
|
"B2F_max_crit": 58.000,
|
||||||
|
"postion": "cpu",
|
||||||
|
"name": "Inlet ambient sensor (Front to Rear, left)",
|
||||||
|
"ss_index": 5,
|
||||||
|
"i2c_path": "i2c-11/11-001a/hwmon" # Q7752
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
"F2B_max": 90.000,
|
||||||
|
"F2B_max_crit": 95.000,
|
||||||
|
"B2F_max": 90.000,
|
||||||
|
"B2F_max_crit": 95.000,
|
||||||
|
"postion": "cpu",
|
||||||
|
"name": "CPU errata sensor (Front to Rear)",
|
||||||
|
"ss_index": 2,
|
||||||
|
"i2c_path": "i2c-3/3-001a/hwmon" # Q5
|
||||||
|
},
|
||||||
|
6: {
|
||||||
|
"F2B_max": 90.000,
|
||||||
|
"F2B_max_crit": 95.000,
|
||||||
|
"B2F_max": 90.000,
|
||||||
|
"B2F_max_crit": 95.000,
|
||||||
|
"postion": "cpu",
|
||||||
|
"name": "CPU errata sensor (Rear to Front)",
|
||||||
|
"ss_index": 3,
|
||||||
|
"i2c_path": "i2c-3/3-001a/hwmon" # Q6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NULL_VAL = "N/A"
|
||||||
|
I2C_ADAPTER_PATH = "/sys/class/i2c-adapter"
|
||||||
|
|
||||||
|
|
||||||
class Thermal(ThermalBase):
|
class Thermal(ThermalBase):
|
||||||
"""Platform-specific Thermal class"""
|
"""Platform-specific Thermal class"""
|
||||||
|
|
||||||
@ -24,57 +98,33 @@ class Thermal(ThermalBase):
|
|||||||
CPUBOARD_SS_PATH = "/sys/class/i2c-adapter/i2c-3/3-001a/hwmon/hwmon1"
|
CPUBOARD_SS_PATH = "/sys/class/i2c-adapter/i2c-3/3-001a/hwmon/hwmon1"
|
||||||
SS_CONFIG_PATH = "/usr/share/sonic/device/x86_64-cel_e1031-r0/sensors.conf"
|
SS_CONFIG_PATH = "/usr/share/sonic/device/x86_64-cel_e1031-r0/sensors.conf"
|
||||||
|
|
||||||
def __init__(self, thermal_index):
|
def __init__(self, thermal_index, airflow):
|
||||||
ThermalBase.__init__(self)
|
ThermalBase.__init__(self)
|
||||||
|
|
||||||
self.index = thermal_index
|
self.index = thermal_index
|
||||||
|
self._api_common = Common()
|
||||||
|
|
||||||
# Add thermal name
|
self._airflow = airflow
|
||||||
self.THERMAL_NAME_LIST.append("Rear panel-Inlet ambient sensor")
|
self._thermal_info = THERMAL_INFO[self.index]
|
||||||
self.THERMAL_NAME_LIST.append("Rear panel-Helix shutdown sensor")
|
self._hwmon_path = self._get_hwmon_path()
|
||||||
self.THERMAL_NAME_LIST.append(
|
self._ss_index = self._thermal_info["ss_index"]
|
||||||
"Front panel-Inlet ambient sensor (right)")
|
|
||||||
self.THERMAL_NAME_LIST.append("Front panel-Helix shutdown sensor")
|
|
||||||
self.THERMAL_NAME_LIST.append(
|
|
||||||
"Front panel-Inlet ambient sensor (left)")
|
|
||||||
self.THERMAL_NAME_LIST.append("CPU board temperature sensor : 1")
|
|
||||||
self.THERMAL_NAME_LIST.append("CPU board temperature sensor : 2")
|
|
||||||
|
|
||||||
# Set hwmon path
|
self.name = self.get_name()
|
||||||
self.ss_index, self.hwmon_path = self.__get_ss_info(self.index)
|
self.postion = self._thermal_info["postion"]
|
||||||
self.ss_key = self.THERMAL_NAME_LIST[self.index]
|
|
||||||
|
|
||||||
def __get_ss_info(self, index):
|
def _get_hwmon_path(self):
|
||||||
if self.index <= 4:
|
hwmon_path = os.path.join(I2C_ADAPTER_PATH, self._thermal_info["i2c_path"])
|
||||||
ss_path = self.MAINBOARD_SS_PATH
|
hwmon_dir = os.listdir(hwmon_path)[0]
|
||||||
ss_index = index+1
|
return os.path.join(hwmon_path, hwmon_dir)
|
||||||
else:
|
|
||||||
ss_path = self.CPUBOARD_SS_PATH
|
|
||||||
ss_index = index-3
|
|
||||||
return ss_index, ss_path
|
|
||||||
|
|
||||||
def __read_txt_file(self, file_path):
|
def _get_temp(self, temp_file):
|
||||||
try:
|
temp_file_path = os.path.join(self._hwmon_path, temp_file)
|
||||||
with open(file_path, 'r') as fd:
|
raw_temp = self._api_common.read_txt_file(temp_file_path)
|
||||||
data = fd.read()
|
|
||||||
return data.strip()
|
|
||||||
except IOError:
|
|
||||||
raise IOError("Unable to open %s file !" % file_path)
|
|
||||||
|
|
||||||
def __get_temp(self, temp_file):
|
|
||||||
temp_file_path = os.path.join(self.hwmon_path, temp_file)
|
|
||||||
raw_temp = self.__read_txt_file(temp_file_path)
|
|
||||||
temp = float(raw_temp)/1000
|
temp = float(raw_temp)/1000
|
||||||
return "{:.3f}".format(temp)
|
return float("{:.3f}".format(temp))
|
||||||
|
|
||||||
def __set_threshold(self, file_name, temperature):
|
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:
|
return self._api_common.write_txt_file(temp_file_path, str(temperature))
|
||||||
with open(temp_file_path, 'w') as fd:
|
|
||||||
fd.write(str(temperature))
|
|
||||||
return True
|
|
||||||
except IOError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_temperature(self):
|
def get_temperature(self):
|
||||||
"""
|
"""
|
||||||
@ -83,8 +133,8 @@ class Thermal(ThermalBase):
|
|||||||
A float number of current temperature in Celsius up to nearest thousandth
|
A float number of current temperature in Celsius up to nearest thousandth
|
||||||
of one degree Celsius, e.g. 30.125
|
of one degree Celsius, e.g. 30.125
|
||||||
"""
|
"""
|
||||||
temp_file = "temp{}_input".format(self.ss_index)
|
temp_file = "temp{}_input".format(self._ss_index)
|
||||||
return self.__get_temp(temp_file)
|
return self._get_temp(temp_file)
|
||||||
|
|
||||||
def get_high_threshold(self):
|
def get_high_threshold(self):
|
||||||
"""
|
"""
|
||||||
@ -93,8 +143,17 @@ class Thermal(ThermalBase):
|
|||||||
A float number, the high threshold temperature of thermal in Celsius
|
A float number, the high threshold temperature of thermal in Celsius
|
||||||
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
||||||
"""
|
"""
|
||||||
temp_file = "temp{}_max".format(self.ss_index)
|
max_crit_key = '{}_max'.format(self._airflow)
|
||||||
return self.__get_temp(temp_file)
|
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):
|
def set_high_threshold(self, temperature):
|
||||||
"""
|
"""
|
||||||
@ -105,8 +164,8 @@ class Thermal(ThermalBase):
|
|||||||
Returns:
|
Returns:
|
||||||
A boolean, True if threshold is set successfully, False if not
|
A boolean, True if threshold is set successfully, False if not
|
||||||
"""
|
"""
|
||||||
temp_file = "temp{}_max".format(self.ss_index)
|
temp_file = "temp{}_max".format(self._ss_index)
|
||||||
is_set = self.__set_threshold(temp_file, int(temperature*1000))
|
is_set = self._set_threshold(temp_file, int(temperature*1000))
|
||||||
file_set = False
|
file_set = False
|
||||||
if is_set:
|
if is_set:
|
||||||
try:
|
try:
|
||||||
@ -115,7 +174,7 @@ class Thermal(ThermalBase):
|
|||||||
f.seek(0)
|
f.seek(0)
|
||||||
ss_found = False
|
ss_found = False
|
||||||
for idx, val in enumerate(content):
|
for idx, val in enumerate(content):
|
||||||
if self.ss_key in val:
|
if self.name in val:
|
||||||
ss_found = True
|
ss_found = True
|
||||||
elif ss_found and temp_file in val:
|
elif ss_found and temp_file in val:
|
||||||
content[idx] = " set {} {}\n".format(
|
content[idx] = " set {} {}\n".format(
|
||||||
@ -128,13 +187,43 @@ class Thermal(ThermalBase):
|
|||||||
|
|
||||||
return is_set & file_set
|
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):
|
def get_name(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the name of the thermal device
|
Retrieves the name of the thermal device
|
||||||
Returns:
|
Returns:
|
||||||
string: The name of the thermal device
|
string: The name of the thermal device
|
||||||
"""
|
"""
|
||||||
return self.THERMAL_NAME_LIST[self.index]
|
return self._thermal_info["name"]
|
||||||
|
|
||||||
def get_presence(self):
|
def get_presence(self):
|
||||||
"""
|
"""
|
||||||
@ -142,10 +231,26 @@ class Thermal(ThermalBase):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if PSU is present, False if not
|
bool: True if PSU is present, False if not
|
||||||
"""
|
"""
|
||||||
temp_file = "temp{}_input".format(self.ss_index)
|
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)
|
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):
|
def get_status(self):
|
||||||
"""
|
"""
|
||||||
Retrieves the operational status of the device
|
Retrieves the operational status of the device
|
||||||
@ -155,10 +260,10 @@ class Thermal(ThermalBase):
|
|||||||
if not self.get_presence():
|
if not self.get_presence():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
fault_file = "temp{}_fault".format(self.ss_index)
|
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):
|
if not os.path.isfile(fault_file_path):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
raw_txt = self.__read_txt_file(fault_file_path)
|
raw_txt = self._api_common.read_txt_file(fault_file_path)
|
||||||
return int(raw_txt) == 0
|
return int(raw_txt) == 0
|
||||||
|
@ -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 .common import Common
|
||||||
|
|
||||||
|
|
||||||
|
@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 = Common().read_txt_file(
|
||||||
|
thermal_overload_position_path)
|
||||||
|
|
||||||
|
cmd = 'bash /usr/share/sonic/platform/thermal_overload_control.sh {}'.format(
|
||||||
|
thermal_overload_position)
|
||||||
|
Common().run_command(cmd)
|
@ -0,0 +1,78 @@
|
|||||||
|
from sonic_platform_base.sonic_thermal_control.thermal_condition_base import ThermalPolicyConditionBase
|
||||||
|
from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object
|
||||||
|
|
||||||
|
|
||||||
|
class FanCondition(ThermalPolicyConditionBase):
|
||||||
|
def get_fan_info(self, thermal_info_dict):
|
||||||
|
from .thermal_infos import FanInfo
|
||||||
|
if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo):
|
||||||
|
return thermal_info_dict[FanInfo.INFO_NAME]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('fan.any.absence')
|
||||||
|
class AnyFanAbsenceCondition(FanCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
fan_info_obj = self.get_fan_info(thermal_info_dict)
|
||||||
|
return len(fan_info_obj.get_absence_fans()) > 0 if fan_info_obj else False
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('fan.any.fault')
|
||||||
|
class AnyFanFaultCondition(FanCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
fan_info_obj = self.get_fan_info(thermal_info_dict)
|
||||||
|
return len(fan_info_obj.get_fault_fans()) > 0 if fan_info_obj else False
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('fan.all.presence')
|
||||||
|
class AllFanPresenceCondition(FanCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
fan_info_obj = self.get_fan_info(thermal_info_dict)
|
||||||
|
return len(fan_info_obj.get_absence_fans()) == 0 if fan_info_obj else False
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('fan.all.good')
|
||||||
|
class AllFanGoodCondition(FanCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
fan_info_obj = self.get_fan_info(thermal_info_dict)
|
||||||
|
return len(fan_info_obj.get_fault_fans()) == 0 if fan_info_obj else False
|
||||||
|
|
||||||
|
|
||||||
|
class ThermalCondition(ThermalPolicyConditionBase):
|
||||||
|
def get_thermal_info(self, thermal_info_dict):
|
||||||
|
from .thermal_infos import ThermalInfo
|
||||||
|
if ThermalInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo):
|
||||||
|
return thermal_info_dict[ThermalInfo.INFO_NAME]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('thermal.over.high_threshold')
|
||||||
|
class ThermalOverHighCriticalCondition(ThermalCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
thermal_info_obj = self.get_thermal_info(thermal_info_dict)
|
||||||
|
if thermal_info_obj:
|
||||||
|
return thermal_info_obj.is_over_high_threshold()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('thermal.over.high_critical_threshold')
|
||||||
|
class ThermalOverHighCriticalCondition(ThermalCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
thermal_info_obj = self.get_thermal_info(thermal_info_dict)
|
||||||
|
if thermal_info_obj:
|
||||||
|
return thermal_info_obj.is_over_high_critical_threshold()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('thermal.all.good')
|
||||||
|
class ThermalGoodCondition(ThermalCondition):
|
||||||
|
def is_match(self, thermal_info_dict):
|
||||||
|
thermal_info_obj = self.get_thermal_info(thermal_info_dict)
|
||||||
|
if thermal_info_obj:
|
||||||
|
return not thermal_info_obj.is_over_threshold()
|
||||||
|
else:
|
||||||
|
return False
|
@ -0,0 +1,165 @@
|
|||||||
|
from sonic_platform_base.sonic_thermal_control.thermal_info_base import ThermalPolicyInfoBase
|
||||||
|
from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object
|
||||||
|
from .common import Common
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('fan_info')
|
||||||
|
class FanInfo(ThermalPolicyInfoBase):
|
||||||
|
"""
|
||||||
|
Fan information needed by thermal policy
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fan information name
|
||||||
|
INFO_NAME = 'fan_info'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._absence_fans = set()
|
||||||
|
self._presence_fans = set()
|
||||||
|
self._fault_fans = set()
|
||||||
|
self._status_changed = False
|
||||||
|
|
||||||
|
def collect(self, chassis):
|
||||||
|
"""
|
||||||
|
Collect absence and presence fans.
|
||||||
|
:param chassis: The chassis object
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self._status_changed = False
|
||||||
|
for fan in chassis.get_all_fans():
|
||||||
|
presence = fan.get_presence()
|
||||||
|
status = fan.get_status()
|
||||||
|
if presence and fan not in self._presence_fans:
|
||||||
|
self._presence_fans.add(fan)
|
||||||
|
self._status_changed = True
|
||||||
|
if fan in self._absence_fans:
|
||||||
|
self._absence_fans.remove(fan)
|
||||||
|
elif not presence and fan not in self._absence_fans:
|
||||||
|
self._absence_fans.add(fan)
|
||||||
|
self._status_changed = True
|
||||||
|
if fan in self._presence_fans:
|
||||||
|
self._presence_fans.remove(fan)
|
||||||
|
|
||||||
|
if not status and fan not in self._fault_fans:
|
||||||
|
self._fault_fans.add(fan)
|
||||||
|
self._status_changed = True
|
||||||
|
|
||||||
|
elif status and fan in self._fault_fans:
|
||||||
|
self._fault_fans.remove(fan)
|
||||||
|
self._status_changed = True
|
||||||
|
|
||||||
|
def get_absence_fans(self):
|
||||||
|
"""
|
||||||
|
Retrieves absence fans
|
||||||
|
:return: A set of absence fans
|
||||||
|
"""
|
||||||
|
return self._absence_fans
|
||||||
|
|
||||||
|
def get_presence_fans(self):
|
||||||
|
"""
|
||||||
|
Retrieves presence fans
|
||||||
|
:return: A set of presence fans
|
||||||
|
"""
|
||||||
|
return self._presence_fans
|
||||||
|
|
||||||
|
def get_fault_fans(self):
|
||||||
|
"""
|
||||||
|
Retrieves fault fans
|
||||||
|
:return: A set of fault fans
|
||||||
|
"""
|
||||||
|
return self._fault_fans
|
||||||
|
|
||||||
|
def is_status_changed(self):
|
||||||
|
"""
|
||||||
|
Retrieves if the status of fan information changed
|
||||||
|
:return: True if status changed else False
|
||||||
|
"""
|
||||||
|
return self._status_changed
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('thermal_info')
|
||||||
|
class ThermalInfo(ThermalPolicyInfoBase):
|
||||||
|
"""
|
||||||
|
Thermal information needed by thermal policy
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Fan information name
|
||||||
|
INFO_NAME = 'thermal_info'
|
||||||
|
|
||||||
|
def collect(self, chassis):
|
||||||
|
"""
|
||||||
|
Collect thermal sensor temperature change status
|
||||||
|
:param chassis: The chassis object
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self._over_high_threshold = False
|
||||||
|
self._over_high_critical_threshold = False
|
||||||
|
self._thermal_overload_position = 'cpu'
|
||||||
|
|
||||||
|
# Calculate average temp within the device
|
||||||
|
temp = 0
|
||||||
|
num_of_thermals = chassis.get_num_thermals()
|
||||||
|
for index in range(num_of_thermals):
|
||||||
|
thermal = chassis.get_thermal(index)
|
||||||
|
temp = thermal.get_temperature()
|
||||||
|
high_threshold = thermal.get_high_threshold()
|
||||||
|
high_critical_threshold = thermal.get_high_critical_threshold()
|
||||||
|
|
||||||
|
if high_threshold and temp > high_threshold:
|
||||||
|
self._over_high_threshold = True
|
||||||
|
|
||||||
|
if high_critical_threshold and temp > high_critical_threshold:
|
||||||
|
self._thermal_overload_position = thermal.postion
|
||||||
|
self._over_high_critical_threshold = True
|
||||||
|
|
||||||
|
def is_over_threshold(self):
|
||||||
|
"""
|
||||||
|
Retrieves if the temperature is over any threshold
|
||||||
|
:return: True if the temperature is over any threshold else False
|
||||||
|
"""
|
||||||
|
return self._over_high_threshold or self._over_high_critical_threshold
|
||||||
|
|
||||||
|
def is_over_high_critical_threshold(self):
|
||||||
|
"""
|
||||||
|
Retrieves if the temperature is over high critical threshold
|
||||||
|
:return: True if the temperature is over high critical threshold else False
|
||||||
|
"""
|
||||||
|
thermal_overload_position_path = '/tmp/thermal_overload_position'
|
||||||
|
if self._over_high_critical_threshold:
|
||||||
|
Common().write_txt_file(thermal_overload_position_path,
|
||||||
|
self._thermal_overload_position)
|
||||||
|
time.sleep(1)
|
||||||
|
return self._over_high_critical_threshold
|
||||||
|
|
||||||
|
def is_over_high_threshold(self):
|
||||||
|
"""
|
||||||
|
Retrieves if the temperature is over high threshold
|
||||||
|
:return: True if the temperature is over high threshold else False
|
||||||
|
"""
|
||||||
|
return self._over_high_threshold
|
||||||
|
|
||||||
|
|
||||||
|
@thermal_json_object('chassis_info')
|
||||||
|
class ChassisInfo(ThermalPolicyInfoBase):
|
||||||
|
"""
|
||||||
|
Chassis information needed by thermal policy
|
||||||
|
"""
|
||||||
|
INFO_NAME = 'chassis_info'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._chassis = None
|
||||||
|
|
||||||
|
def collect(self, chassis):
|
||||||
|
"""
|
||||||
|
Collect platform chassis.
|
||||||
|
:param chassis: The chassis object
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self._chassis = chassis
|
||||||
|
|
||||||
|
def get_chassis(self):
|
||||||
|
"""
|
||||||
|
Retrieves platform chassis object
|
||||||
|
:return: A platform chassis object.
|
||||||
|
"""
|
||||||
|
return self._chassis
|
@ -0,0 +1,47 @@
|
|||||||
|
from sonic_platform_base.sonic_thermal_control.thermal_manager_base import ThermalManagerBase
|
||||||
|
from .common import Common
|
||||||
|
from .thermal_actions import *
|
||||||
|
from .thermal_conditions import *
|
||||||
|
from .thermal_infos import *
|
||||||
|
|
||||||
|
|
||||||
|
class ThermalManager(ThermalManagerBase):
|
||||||
|
FSC_ALGORITHM_CMD = ' supervisorctl {} fancontrol'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def start_thermal_control_algorithm(cls):
|
||||||
|
"""
|
||||||
|
Start vendor specific thermal control algorithm. The default behavior of this function is a no-op.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cls._enable_fancontrol_service(True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stop_thermal_control_algorithm(cls):
|
||||||
|
"""
|
||||||
|
Stop thermal control algorithm
|
||||||
|
Returns:
|
||||||
|
bool: True if set success, False if fail.
|
||||||
|
"""
|
||||||
|
return cls._enable_fancontrol_service(False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def deinitialize(cls):
|
||||||
|
"""
|
||||||
|
Destroy thermal manager, including any vendor specific cleanup. The default behavior of this function
|
||||||
|
is a no-op.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return cls._enable_fancontrol_service(True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _enable_fancontrol_service(cls, enable):
|
||||||
|
"""
|
||||||
|
Control thermal by fcs algorithm
|
||||||
|
Args:
|
||||||
|
enable: Bool, indicate enable the algorithm or not
|
||||||
|
Returns:
|
||||||
|
bool: True if set success, False if fail.
|
||||||
|
"""
|
||||||
|
cmd = 'start' if enable else 'stop'
|
||||||
|
return Common().run_command(cls.FSC_ALGORITHM_CMD.format(cmd))
|
@ -4,11 +4,8 @@
|
|||||||
# Watchdog contains an implementation of SONiC Platform Base API
|
# Watchdog contains an implementation of SONiC Platform Base API
|
||||||
#
|
#
|
||||||
#############################################################################
|
#############################################################################
|
||||||
import ctypes
|
|
||||||
import fcntl
|
import fcntl
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
import array
|
import array
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
75
device/celestica/x86_64-cel_e1031-r0/thermal_overload_control.sh
Executable file
75
device/celestica/x86_64-cel_e1031-r0/thermal_overload_control.sh
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright 2020-present Celestica. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# This program file is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the
|
||||||
|
# Free Software Foundation; version 2 of the License.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
# for more details.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
|
||||||
|
SETREG_FILE=/sys/devices/platform/e1031.smc/setreg
|
||||||
|
TOVERREG=0x115
|
||||||
|
CPUOVER=0xa1
|
||||||
|
ASICOVER=0xa2
|
||||||
|
|
||||||
|
prog="$0"
|
||||||
|
command="$1"
|
||||||
|
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "This script must be run as root"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: thermal_overload_control.sh [option] <command>"
|
||||||
|
echo
|
||||||
|
echo "Options:"
|
||||||
|
echo " -h, --help : to print this message."
|
||||||
|
echo
|
||||||
|
echo "Commands:"
|
||||||
|
echo
|
||||||
|
echo " cpu: To enabling CPU thermal overload handler"
|
||||||
|
echo
|
||||||
|
echo " asic : To enabling ASIC thermal overload handler"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_overload() {
|
||||||
|
logger "Enable CPU thermal overload control"
|
||||||
|
set_reg=`echo ${TOVERREG} ${CPUOVER} > ${SETREG_FILE}`
|
||||||
|
}
|
||||||
|
|
||||||
|
asic_overload() {
|
||||||
|
logger "Enable ASIC thermal overload control"
|
||||||
|
set_reg=`echo ${TOVERREG} ${ASICOVER} > ${SETREG_FILE}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
usage
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$command" in
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
cpu)
|
||||||
|
cpu_overload
|
||||||
|
;;
|
||||||
|
asic)
|
||||||
|
asic_overload
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
exit -1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $?
|
93
device/celestica/x86_64-cel_e1031-r0/thermal_policy.json
Normal file
93
device/celestica/x86_64-cel_e1031-r0/thermal_policy.json
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"thermal_control_algorithm": {
|
||||||
|
"run_at_boot_up": "true"
|
||||||
|
},
|
||||||
|
"info_types": [
|
||||||
|
{
|
||||||
|
"type": "chassis_info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "fan_info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "thermal_info"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"name": "any fan absence",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "fan.any.absence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "thermal_control.control",
|
||||||
|
"status": "false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "any fan broken",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "fan.any.fault"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "thermal_control.control",
|
||||||
|
"status": "false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "any thermal over threshold",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "thermal.over.high_threshold"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "thermal_control.control",
|
||||||
|
"status": "false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "temp over high critical threshold",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "thermal.over.high_critical_threshold"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "switch.power_cycling"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "all fan presence / thermal no warning",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "fan.all.presence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "fan.all.good"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "thermal.all.good"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "thermal_control.control",
|
||||||
|
"status": "true"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -53,9 +53,12 @@ start)
|
|||||||
echo max6699 0x1a > /sys/bus/i2c/devices/i2c-3/new_device
|
echo max6699 0x1a > /sys/bus/i2c/devices/i2c-3/new_device
|
||||||
echo max6699 0x1a > /sys/bus/i2c/devices/i2c-11/new_device
|
echo max6699 0x1a > /sys/bus/i2c/devices/i2c-11/new_device
|
||||||
|
|
||||||
|
## TODO: fix dps200 driver cause kernel panic issue
|
||||||
|
# https://github.com/Azure/sonic-buildimage/issues/6602
|
||||||
|
|
||||||
# Attach PSUs
|
# Attach PSUs
|
||||||
echo dps200 0x5a > /sys/bus/i2c/devices/i2c-12/new_device
|
# echo dps200 0x5a > /sys/bus/i2c/devices/i2c-12/new_device
|
||||||
echo dps200 0x5b > /sys/bus/i2c/devices/i2c-13/new_device
|
# echo dps200 0x5b > /sys/bus/i2c/devices/i2c-13/new_device
|
||||||
|
|
||||||
# Attach fans
|
# Attach fans
|
||||||
echo emc2305 0x4d > /sys/bus/i2c/devices/i2c-23/new_device
|
echo emc2305 0x4d > /sys/bus/i2c/devices/i2c-23/new_device
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
depmod -a
|
depmod -a
|
||||||
|
|
||||||
sudo chmod +x /usr/local/bin/udev_prefix.sh
|
sudo chmod +x /usr/local/bin/udev_prefix.sh
|
||||||
sudo chmod +x /usr/local/bin/popmsg.sh
|
sudo chmod +x /usr/local/bin/popmsg.sh
|
||||||
sudo chmod +x /usr/local/bin/reload_udev.sh
|
sudo chmod +x /usr/local/bin/reload_udev.sh
|
||||||
|
|
||||||
|
/usr/local/bin/platform_api_mgnt.sh install
|
||||||
|
/etc/init.d/fancontrol.sh install
|
||||||
|
/usr/local/bin/reload_udev.sh
|
||||||
|
|
||||||
systemctl enable platform-modules-haliburton.service
|
systemctl enable platform-modules-haliburton.service
|
||||||
systemctl enable fancontrol.service
|
systemctl enable fancontrol.service
|
||||||
|
|
||||||
systemctl start platform-modules-haliburton.service
|
systemctl start platform-modules-haliburton.service
|
||||||
systemctl start fancontrol.service
|
systemctl start fancontrol.service
|
||||||
|
|
||||||
/usr/local/bin/platform_api_mgnt.sh install
|
|
||||||
/usr/local/bin/reload_udev.sh
|
|
||||||
|
@ -7,72 +7,37 @@
|
|||||||
# Default-Start: 2 3 4 5
|
# Default-Start: 2 3 4 5
|
||||||
# Default-Stop:
|
# Default-Stop:
|
||||||
# Short-Description: fancontrol
|
# Short-Description: fancontrol
|
||||||
# Description: fan speed regulator
|
# Description: fancontrol configuration selector
|
||||||
### END INIT INFO
|
### END INIT INFO
|
||||||
|
|
||||||
. /lib/lsb/init-functions
|
. /lib/lsb/init-functions
|
||||||
|
|
||||||
[ -f /etc/default/rcS ] && . /etc/default/rcS
|
[ -f /etc/default/rcS ] && . /etc/default/rcS
|
||||||
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
|
|
||||||
DAEMON=/usr/local/bin/fancontrol
|
|
||||||
DESC="fan speed regulator"
|
|
||||||
NAME="fancontrol"
|
|
||||||
PIDFILE=/var/run/fancontrol.pid
|
|
||||||
PLATFORMPATH=/sys/devices/platform/e1031.smc
|
|
||||||
MAIN_CONF=/usr/share/sonic/device/x86_64-cel_e1031-r0/fancontrol
|
MAIN_CONF=/usr/share/sonic/device/x86_64-cel_e1031-r0/fancontrol
|
||||||
DEVPATH=/sys/devices/pci0000:00/0000:00:13.0/i2c-0/i2c-8/i2c-23/23-004d
|
PLATFORM_PATH=/sys/devices/platform/e1031.smc
|
||||||
|
|
||||||
test -x $DAEMON || exit 0
|
init() {
|
||||||
|
FANDIR=$(cat ${PLATFORM_PATH}/fan1_dir)
|
||||||
|
CONF=${MAIN_CONF}-${FANDIR}
|
||||||
|
echo $FANDIR > /usr/share/sonic/device/x86_64-cel_e1031-r0/fan_airflow
|
||||||
|
}
|
||||||
|
|
||||||
for i in 1 2 3
|
install() {
|
||||||
do
|
find /var/lib/docker/overlay*/ -path */sbin/fancontrol -exec cp /usr/local/bin/fancontrol {} \;
|
||||||
j=$i
|
}
|
||||||
[ $i -eq 3 ] && j=4
|
|
||||||
FANFAULT=$(cat ${DEVPATH}/fan${j}_fault)
|
|
||||||
[ $FANFAULT = 1 ] && continue
|
|
||||||
FANDIR=$(cat ${PLATFORMPATH}/fan${i}_dir)
|
|
||||||
done
|
|
||||||
CONF=${MAIN_CONF}-${FANDIR}
|
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
start)
|
start)
|
||||||
if [ -f $CONF ] ; then
|
init
|
||||||
if $DAEMON --check $CONF 1>/dev/null 2>/dev/null ; then
|
cp $CONF $MAIN_CONF
|
||||||
log_daemon_msg "Starting $DESC" "$NAME\n"
|
;;
|
||||||
start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON $CONF
|
install)
|
||||||
log_end_msg $?
|
install
|
||||||
else
|
;;
|
||||||
log_failure_msg "Not starting fancontrol, broken configuration file; please re-run pwmconfig."
|
*)
|
||||||
fi
|
log_success_msg "Usage: /etc/init.d/fancontrol {start} | {install}"
|
||||||
else
|
exit 1
|
||||||
if [ "$VERBOSE" != no ]; then
|
;;
|
||||||
log_warning_msg "Not starting fancontrol; run pwmconfig first."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
stop)
|
|
||||||
log_daemon_msg "Stopping $DESC" "$NAME"
|
|
||||||
start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo --startas $DAEMON $CONF
|
|
||||||
rm -f $PIDFILE
|
|
||||||
log_end_msg $?
|
|
||||||
;;
|
|
||||||
restart)
|
|
||||||
$0 stop
|
|
||||||
sleep 3
|
|
||||||
$0 start
|
|
||||||
;;
|
|
||||||
force-reload)
|
|
||||||
if start-stop-daemon --stop --test --quiet --pidfile $PIDFILE --startas $DAEMON $CONF ; then
|
|
||||||
$0 restart
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status_of_proc $DAEMON $NAME $CONF && exit 0 || exit $?
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
log_success_msg "Usage: /etc/init.d/fancontrol {start|stop|restart|force-reload|status}"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
exit 0
|
exit 0
|
Loading…
Reference in New Issue
Block a user