[device/celestica]: Implement Seastone2 platform API (#5080)
Implement Seastone2 platform API: 1. Implement Fan APIs. 2. Implement Psu APIs. 3. Implement Thermal APIs. 4. Implement Component APIs. 5. Implement Chassis APIs. 6. Implement Sfp APIs. 7. Implement Watchdog APIs. (Required new CPLD version) 8. Upgrade switchboard driver to support port insert/remove interrupt 9. Init all above APIs class on chassis API. Co-authored-by: pjaipakdee <jai.peerapong@gmail.com> Co-authored-by: Pradchaya P <pphuchar@celestica.com>
This commit is contained in:
parent
e9918bac07
commit
aa601f4b0a
@ -2,5 +2,6 @@
|
||||
"skip_ledd": true,
|
||||
"skip_xcvrd": false,
|
||||
"skip_psud": false,
|
||||
"skip_syseepromd": false
|
||||
"skip_syseepromd": false,
|
||||
"skip_fancontrol": true
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
{
|
||||
"eeprom": "/sys/class/i2c-adapter/i2c-0/0-0056/eeprom",
|
||||
"get_reboot_cause": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x0c 0x0 0x2 0x06",
|
||||
"output_translator": {
|
||||
"00": "Hardware - Other",
|
||||
"11": "Hardware - Other",
|
||||
"22": "Non-Hardware",
|
||||
"33": "Hardware - Other",
|
||||
"44": "Non-Hardware",
|
||||
"55": "Non-Hardware",
|
||||
"77": "Watchdog",
|
||||
"88": "Thermal Overload: CPU",
|
||||
"99": "Thermal Overload: ASIC"
|
||||
}
|
||||
},
|
||||
"get_reboot_description": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x0c 0x0 0x2 0x06",
|
||||
"output_translator": {
|
||||
"00": "The last reset is power cycle reset (set register 0xA164)",
|
||||
"11": "The last reset is Power on reset",
|
||||
"22": "The last reset is soft-set CPU warm reset",
|
||||
"33": "The last reset is soft-set CPU cold reset",
|
||||
"44": "The last reset is CPU warm reset",
|
||||
"55": "The last reset is CPU cold reset",
|
||||
"77": "The last reset is watchdog reset",
|
||||
"88": "The last reset is CPU thermal overload",
|
||||
"99": "The last reset is ASIC thermal overload"
|
||||
}
|
||||
},
|
||||
"get_watchdog": {
|
||||
"output_source": "class",
|
||||
"host_path": "/usr/share/sonic/device/x86_64-cel_seastone_2-r0/sonic_platform_config/watchdog.py",
|
||||
"pmon_path": "/usr/share/sonic/platform/sonic_platform_config/watchdog.py",
|
||||
"class": "Watchdog"
|
||||
},
|
||||
"get_change_event": {
|
||||
"output_source": "class",
|
||||
"host_path": "/usr/share/sonic/device/x86_64-cel_seastone_2-r0/sonic_platform_config/event.py",
|
||||
"pmon_path": "/usr/share/sonic/platform/sonic_platform_config/event.py",
|
||||
"class": "SfpEvent"
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
{
|
||||
"component_num": 5,
|
||||
"get_name": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"BIOS",
|
||||
"CPLD_BASEBOARD",
|
||||
"CPLD_SWITCHBOARD",
|
||||
"FPGA",
|
||||
"BMC"
|
||||
]
|
||||
},
|
||||
"get_description": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"Used to perform hardware initialization during the booting process",
|
||||
"Used to control the system power & reset, Control FAN, UART Mux etc",
|
||||
"Used for managing QSFP ports",
|
||||
"Used for managing I2C, SPI, PCIe etc",
|
||||
"Used for monitoring and managing whole system"
|
||||
]
|
||||
},
|
||||
"get_firmware_version": {
|
||||
"output_source": "function",
|
||||
"function": [
|
||||
"_get_bios_ver",
|
||||
"_get_base_cpld_ver",
|
||||
"_get_sw_cpld_ver",
|
||||
"_get_fpga_ver",
|
||||
"_get_bmc_ver"
|
||||
]
|
||||
},
|
||||
"_get_bmc_ver": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool mc info | grep 'Firmware Revision'",
|
||||
"output_translator": "'{}'.split(':')[-1].strip()"
|
||||
},
|
||||
"_get_bios_ver": {
|
||||
"output_source": "txt_file",
|
||||
"path": "/sys/class/dmi/id/bios_version"
|
||||
},
|
||||
"_get_base_cpld_ver": {
|
||||
"output_source": "hex_version_file",
|
||||
"num_of_points": 1,
|
||||
"num_of_bits": 8,
|
||||
"path": "/sys/devices/platform/baseboard/version"
|
||||
},
|
||||
"_get_sw_cpld_ver": {
|
||||
"output_source": "hex_version_getreg",
|
||||
"num_of_points": 1,
|
||||
"num_of_bits": 8,
|
||||
"reg_addr": "0x00",
|
||||
"path": "/sys/devices/platform/switchboard/CPLD1/getreg"
|
||||
},
|
||||
"_get_fpga_ver": {
|
||||
"output_source": "hex_version_getreg",
|
||||
"num_of_points": 1,
|
||||
"num_of_bits": 32,
|
||||
"reg_addr": "0x00",
|
||||
"path": "/sys/devices/platform/switchboard/FPGA/getreg"
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica Seastone2
|
||||
#
|
||||
# SfpEvent contains an implementation of SONiC Platform Base API
|
||||
#
|
||||
#############################################################################
|
||||
try:
|
||||
import time
|
||||
import os
|
||||
from sonic_platform.common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
PLATFORM_PATH = "/sys/devices/platform/"
|
||||
SWITCH_BRD_PLATFORM = "switchboard"
|
||||
POLL_INTERVAL = 1
|
||||
|
||||
|
||||
class SfpEvent:
|
||||
''' Listen to insert/remove sfp events '''
|
||||
|
||||
PORT_INFO_DIR = 'SFF'
|
||||
PATH_INT_SYSFS = "{0}/{port_name}/{type_prefix}_isr_flags"
|
||||
PATH_INTMASK_SYSFS = "{0}/{port_name}/{type_prefix}_isr_mask"
|
||||
PATH_PRS_SYSFS = "{0}/{port_name}/{prs_file_name}"
|
||||
PRESENT_EN = 0x01
|
||||
|
||||
def __init__(self, sfp_list):
|
||||
self.num_sfp = len(sfp_list)
|
||||
self._api_common = Common()
|
||||
self._initialize_interrupts()
|
||||
|
||||
def _initialize_interrupts(self):
|
||||
sfp_info_obj = {}
|
||||
port_info_path = os.path.join(
|
||||
PLATFORM_PATH, SWITCH_BRD_PLATFORM, self.PORT_INFO_DIR)
|
||||
|
||||
for index in range(self.num_sfp):
|
||||
port_num = index + 1
|
||||
port_name = "QSFP{}".format(port_num)
|
||||
port_type = "qsfp"
|
||||
sysfs_prs_file = "{}_modprs".format(port_type)
|
||||
|
||||
sfp_info_obj[index] = {}
|
||||
sfp_info_obj[index]['intmask_sysfs'] = self.PATH_INTMASK_SYSFS.format(
|
||||
port_info_path,
|
||||
port_name=port_name,
|
||||
type_prefix=port_type)
|
||||
|
||||
sfp_info_obj[index]['int_sysfs'] = self.PATH_INT_SYSFS.format(
|
||||
port_info_path,
|
||||
port_name=port_name,
|
||||
type_prefix=port_type)
|
||||
|
||||
sfp_info_obj[index]['prs_sysfs'] = self.PATH_PRS_SYSFS.format(
|
||||
port_info_path,
|
||||
port_name=port_name,
|
||||
prs_file_name=sysfs_prs_file)
|
||||
|
||||
self._api_common.write_txt_file(
|
||||
sfp_info_obj[index]["intmask_sysfs"], hex(self.PRESENT_EN))
|
||||
|
||||
self.sfp_info_obj = sfp_info_obj
|
||||
|
||||
def _is_port_device_present(self, port_idx):
|
||||
prs_path = self.sfp_info_obj[port_idx]["prs_sysfs"]
|
||||
is_present = 1 - int(self._api_common.read_txt_file(prs_path))
|
||||
return is_present
|
||||
|
||||
def _update_port_event_object(self, interrup_devices, port_dict):
|
||||
for port_idx in interrup_devices:
|
||||
device_id = str(port_idx + 1)
|
||||
port_dict[device_id] = str(self._is_port_device_present(port_idx))
|
||||
return port_dict
|
||||
|
||||
def _clear_event_flag(self, path):
|
||||
self._api_common.write_txt_file(path, hex(0xff))
|
||||
time.sleep(0.1)
|
||||
self._api_common.write_txt_file(path, hex(0x0))
|
||||
|
||||
def _check_all_port_interrupt_event(self):
|
||||
interrupt_devices = {}
|
||||
for i in range(self.num_sfp):
|
||||
int_sysfs = self.sfp_info_obj[i]["int_sysfs"]
|
||||
interrupt_flags = self._api_common.read_txt_file(int_sysfs)
|
||||
if interrupt_flags != '0x00':
|
||||
interrupt_devices[i] = 1
|
||||
self._clear_event_flag(int_sysfs)
|
||||
return interrupt_devices
|
||||
|
||||
def get_event(self, timeout):
|
||||
sleep_time = min(
|
||||
timeout, POLL_INTERVAL) if timeout != 0 else POLL_INTERVAL
|
||||
start_milli_time = int(round(time.time() * 1000))
|
||||
int_sfp = {}
|
||||
|
||||
while True:
|
||||
chk_sfp = self._check_all_port_interrupt_event()
|
||||
int_sfp = self._update_port_event_object(
|
||||
chk_sfp, int_sfp) if chk_sfp else int_sfp
|
||||
current_milli_time = int(round(time.time() * 1000))
|
||||
if (int_sfp) or \
|
||||
(timeout != 0 and current_milli_time - start_milli_time > timeout):
|
||||
break
|
||||
|
||||
time.sleep(sleep_time)
|
||||
|
||||
change_dict = dict()
|
||||
change_dict['sfp'] = int_sfp
|
||||
|
||||
return True, change_dict
|
@ -0,0 +1,200 @@
|
||||
{
|
||||
"fan_num_per_drawer": 2,
|
||||
"drawer_num": 4,
|
||||
"get_name": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"Fan1-F",
|
||||
"Fan1-R",
|
||||
"Fan2-F",
|
||||
"Fan2-R",
|
||||
"Fan3-F",
|
||||
"Fan3-R",
|
||||
"Fan4-F",
|
||||
"Fan4-R"
|
||||
]
|
||||
},
|
||||
"get_presence": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x03 0x03 {}",
|
||||
"argument": [
|
||||
"0x00",
|
||||
"0x00",
|
||||
"0x01",
|
||||
"0x01",
|
||||
"0x02",
|
||||
"0x02",
|
||||
"0x03",
|
||||
"0x03"
|
||||
],
|
||||
"output_translator": "True if '00' in '{}' else False"
|
||||
},
|
||||
"get_model": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool fru list {} | grep 'Board Part Number'",
|
||||
"argument": [
|
||||
"5",
|
||||
"5",
|
||||
"6",
|
||||
"6",
|
||||
"7",
|
||||
"7",
|
||||
"8",
|
||||
"8"
|
||||
],
|
||||
"output_translator": "'{}'.split()[-1]"
|
||||
},
|
||||
"get_serial": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool fru list {} | grep 'Board Serial'",
|
||||
"argument": [
|
||||
"5",
|
||||
"5",
|
||||
"6",
|
||||
"6",
|
||||
"7",
|
||||
"7",
|
||||
"8",
|
||||
"8"
|
||||
],
|
||||
"output_translator": "'{}'.split()[-1]"
|
||||
},
|
||||
"get_direction": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool fru list {} | grep 'F2B\\|B2F'",
|
||||
"argument": [
|
||||
"5",
|
||||
"5",
|
||||
"6",
|
||||
"6",
|
||||
"7",
|
||||
"7",
|
||||
"8",
|
||||
"8"
|
||||
],
|
||||
"output_translator": "'intake' if 'B2F' in '{}' else 'exhaust'"
|
||||
},
|
||||
"get_speed": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x04 0x2d {}",
|
||||
"argument": [
|
||||
"0x81",
|
||||
"0x80",
|
||||
"0x83",
|
||||
"0x82",
|
||||
"0x85",
|
||||
"0x84",
|
||||
"0x87",
|
||||
"0x86"
|
||||
],
|
||||
"output_translator": "int('{}'.split()[0],16)*150",
|
||||
"max_front": 23000,
|
||||
"max_rear": 20500
|
||||
},
|
||||
"get_target_speed": {
|
||||
"output_source": "value",
|
||||
"value": "N/A"
|
||||
},
|
||||
"get_speed_tolerance": {
|
||||
"output_source": "value",
|
||||
"value": 10
|
||||
},
|
||||
"set_speed": {
|
||||
"set_method": "ipmitool",
|
||||
"input_translator": "hex(int({} * 255 / 100.0))",
|
||||
"command": "ipmitool raw 0x3a 0x0c 0x00 0x03 {}",
|
||||
"argument": [
|
||||
"0x40 {}",
|
||||
"0x40 {}",
|
||||
"0x44 {}",
|
||||
"0x44 {}",
|
||||
"0x4c {}",
|
||||
"0x4c {}",
|
||||
"0x50 {}",
|
||||
"0x50 {}"
|
||||
]
|
||||
},
|
||||
"set_status_led": {
|
||||
"set_method": "ipmitool",
|
||||
"avaliable_input": [
|
||||
"off",
|
||||
"amber",
|
||||
"green"
|
||||
],
|
||||
"input_translator": {
|
||||
"off": "0x0",
|
||||
"amber": "0x1",
|
||||
"green": "0x2"
|
||||
},
|
||||
"command": "ipmitool raw 0x3a 0x0a {}",
|
||||
"argument": [
|
||||
"0x4 {}",
|
||||
"0x4 {}",
|
||||
"0x5 {}",
|
||||
"0x5 {}",
|
||||
"0x6 {}",
|
||||
"0x6 {}",
|
||||
"0x7 {}",
|
||||
"0x7 {}"
|
||||
]
|
||||
},
|
||||
"get_status_led": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x0b {}",
|
||||
"argument": [
|
||||
"0x4",
|
||||
"0x4",
|
||||
"0x5",
|
||||
"0x5",
|
||||
"0x6",
|
||||
"0x6",
|
||||
"0x7",
|
||||
"0x7"
|
||||
],
|
||||
"output_translator": {
|
||||
"00": "off",
|
||||
"01": "amber",
|
||||
"02": "green"
|
||||
}
|
||||
},
|
||||
"psu_fan": [
|
||||
{
|
||||
"num_of_fan": 1,
|
||||
"get_name": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"PSU-R-Fan"
|
||||
]
|
||||
},
|
||||
"get_speed": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x04 0x2d {}",
|
||||
"argument": [
|
||||
"0x8b"
|
||||
],
|
||||
"output_translator": "int('{}'.split()[0],16)*100",
|
||||
"max_front": 22600,
|
||||
"max_rear": 22600
|
||||
}
|
||||
},
|
||||
{
|
||||
"num_of_fan": 1,
|
||||
"get_name": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"PSU-L-Fan"
|
||||
]
|
||||
},
|
||||
"get_speed": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x04 0x2d {}",
|
||||
"argument": [
|
||||
"0x8a"
|
||||
],
|
||||
"output_translator": "int('{}'.split()[0],16)*100",
|
||||
"max_front": 22600,
|
||||
"max_rear": 22600
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
{
|
||||
"psu_num": 2,
|
||||
"fan_per_psu_num": 1,
|
||||
"get_name": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"PSU-R",
|
||||
"PSU-L"
|
||||
]
|
||||
},
|
||||
"get_power": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sdr | grep {}",
|
||||
"argument": [
|
||||
"PSUR_POut",
|
||||
"PSUL_POut"
|
||||
],
|
||||
"output_translator": "float('{}'.split()[2])"
|
||||
},
|
||||
"get_current": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sdr | grep {}",
|
||||
"argument": [
|
||||
"PSUR_COut",
|
||||
"PSUL_COut"
|
||||
],
|
||||
"output_translator": "float('{}'.split()[2])"
|
||||
},
|
||||
"get_voltage": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sdr | grep {}",
|
||||
"argument": [
|
||||
"PSUR_VOut",
|
||||
"PSUL_VOut"
|
||||
],
|
||||
"output_translator": "float('{}'.split()[2])"
|
||||
},
|
||||
"get_voltage_high_threshold": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sensor list | grep {}",
|
||||
"argument": [
|
||||
"PSUR_Temp2",
|
||||
"PSUL_Temp2"
|
||||
],
|
||||
"output_translator": "float(0 if '{0}'.split()[-3]=='na' else '{0}'.split()[-3])"
|
||||
},
|
||||
"get_voltage_low_threshold": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sensor list | grep {}",
|
||||
"argument": [
|
||||
"PSUR_Temp2",
|
||||
"PSUL_Temp2"
|
||||
],
|
||||
"output_translator": "float(0 if '{0}'.split()[-9]=='na' else '{0}'.split()[-9])"
|
||||
},
|
||||
"get_presence": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x0c 0x00 0x2 0x60",
|
||||
"output_translator": [
|
||||
"True if (int('{}', 16) >> 4 & 1) == 0 else False",
|
||||
"True if (int('{}', 16) >> 5 & 1) == 0 else False"
|
||||
]
|
||||
},
|
||||
"get_model": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool fru list {} | grep 'Product Part Number'",
|
||||
"argument": [
|
||||
"4",
|
||||
"3"
|
||||
],
|
||||
"output_translator": "'{}'.split()[-1]"
|
||||
},
|
||||
"get_serial": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool fru list {} | grep 'Product Serial'",
|
||||
"argument": [
|
||||
"4",
|
||||
"3"
|
||||
],
|
||||
"output_translator": "'{}'.split()[-1]"
|
||||
},
|
||||
"get_powergood_status": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x0c 0x0 0x2 0x60",
|
||||
"output_translator": [
|
||||
"True if (int('{}', 16) >> 2 & 1) == 1 else False",
|
||||
"True if (int('{}', 16) >> 3 & 1) == 1 else False"
|
||||
]
|
||||
},
|
||||
"set_status_led": {
|
||||
"set_method": "ipmitool",
|
||||
"avaliable_input": [
|
||||
"amber"
|
||||
],
|
||||
"input_translator": {
|
||||
"amber": "0x1"
|
||||
},
|
||||
"command": "ipmitool raw 0x3a 0x0a {}",
|
||||
"argument": [
|
||||
"0x3 {}",
|
||||
"0x2 {}"
|
||||
]
|
||||
},
|
||||
"get_status_led": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x3a 0x0b {}",
|
||||
"argument": [
|
||||
"0x3",
|
||||
"0x2"
|
||||
],
|
||||
"output_translator": {
|
||||
"00": "green",
|
||||
"01": "amber"
|
||||
},
|
||||
"default_output": "off"
|
||||
},
|
||||
"get_temperature": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sdr | grep {}",
|
||||
"argument": [
|
||||
"PSUR_Temp2",
|
||||
"PSUL_Temp2"
|
||||
],
|
||||
"output_translator": "float('{}'.split()[2])"
|
||||
},
|
||||
"get_temperature_high_threshold": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sensor list | grep {}",
|
||||
"argument": [
|
||||
"PSUR_Temp2",
|
||||
"PSUL_Temp2"
|
||||
],
|
||||
"output_translator": "float(0 if '{0}'.split()[-3]=='na' else '{0}'.split()[-3])"
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
{
|
||||
"port_num": 32,
|
||||
"eeprom_path": "/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom",
|
||||
"port_i2c_mapping": [
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33
|
||||
],
|
||||
"get_presence": {
|
||||
"output_source": "sysfs_value",
|
||||
"sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_modprs",
|
||||
"argument": "$ref:_port_name",
|
||||
"output_translator": "False if '{}' == '1' else True"
|
||||
},
|
||||
"get_lpmode": {
|
||||
"output_source": "sysfs_value",
|
||||
"sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_lpmode",
|
||||
"argument": "$ref:_port_name",
|
||||
"output_translator": "True if '{}' == '1' else False"
|
||||
},
|
||||
"get_reset_status": {
|
||||
"output_source": "sysfs_value",
|
||||
"sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_reset",
|
||||
"argument": "$ref:_port_name",
|
||||
"output_translator": "False if '{}' == '1' else True"
|
||||
},
|
||||
"reset": {
|
||||
"set_method": "sysfs_value",
|
||||
"write_offset": 0,
|
||||
"sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_reset",
|
||||
"argument": "$ref:_port_name"
|
||||
},
|
||||
"set_lpmode": {
|
||||
"set_method": "sysfs_value",
|
||||
"input_translator": {
|
||||
"True": "0x1",
|
||||
"False": "0x0"
|
||||
},
|
||||
"write_offset": 0,
|
||||
"sysfs_path": "/sys/devices/platform/switchboard/SFF/{}/qsfp_lpmode",
|
||||
"argument": "$ref:_port_name"
|
||||
},
|
||||
"_port_name": [
|
||||
"QSFP1",
|
||||
"QSFP2",
|
||||
"QSFP3",
|
||||
"QSFP4",
|
||||
"QSFP5",
|
||||
"QSFP6",
|
||||
"QSFP7",
|
||||
"QSFP8",
|
||||
"QSFP9",
|
||||
"QSFP10",
|
||||
"QSFP11",
|
||||
"QSFP12",
|
||||
"QSFP13",
|
||||
"QSFP14",
|
||||
"QSFP15",
|
||||
"QSFP16",
|
||||
"QSFP17",
|
||||
"QSFP18",
|
||||
"QSFP19",
|
||||
"QSFP20",
|
||||
"QSFP21",
|
||||
"QSFP22",
|
||||
"QSFP23",
|
||||
"QSFP24",
|
||||
"QSFP25",
|
||||
"QSFP26",
|
||||
"QSFP27",
|
||||
"QSFP28",
|
||||
"QSFP29",
|
||||
"QSFP30",
|
||||
"QSFP31",
|
||||
"QSFP32"
|
||||
]
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
{
|
||||
"thermal_num": 9,
|
||||
"get_name": {
|
||||
"output_source": "value_list",
|
||||
"value_list": [
|
||||
"Base_Temp_U5",
|
||||
"Base_Temp_U7",
|
||||
"CPU_Temp",
|
||||
"Switch_Temp_U1",
|
||||
"Switch_Temp_U18",
|
||||
"Switch_Temp_U28",
|
||||
"Switch_Temp_U29",
|
||||
"Switch_U21_Temp",
|
||||
"Switch_U33_Temp"
|
||||
]
|
||||
},
|
||||
"get_temperature": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x04 0x2D {}",
|
||||
"argument": [
|
||||
"0x1",
|
||||
"0x2",
|
||||
"0x7",
|
||||
"0x3",
|
||||
"0x4",
|
||||
"0x5",
|
||||
"0x6",
|
||||
"0x56",
|
||||
"0x4C"
|
||||
],
|
||||
"output_translator": "int('{}'.split()[0],16)"
|
||||
},
|
||||
"get_high_threshold": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x04 0x27 {}",
|
||||
"argument": [
|
||||
"0x1",
|
||||
"0x2",
|
||||
"0x7",
|
||||
"0x3",
|
||||
"0x4",
|
||||
"0x5",
|
||||
"0x6",
|
||||
"0x56",
|
||||
"0x4c"
|
||||
],
|
||||
"output_translator": "int('{}'.split()[4], 16)"
|
||||
},
|
||||
"get_low_threshold": {
|
||||
"output_source": "value",
|
||||
"value": "N/A"
|
||||
},
|
||||
"set_high_threshold": {
|
||||
"set_method": "ipmitool",
|
||||
"command": "ipmitool sensor thresh {}",
|
||||
"input_translator": "{}",
|
||||
"argument": [
|
||||
"Base_Temp_U5 unc {}",
|
||||
"Base_Temp_U7 unc {}",
|
||||
"CPU_Temp unc {}",
|
||||
"Switch_Temp_U1 unc {}",
|
||||
"Switch_Temp_U18 unc {}",
|
||||
"Switch_Temp_U28 unc {}",
|
||||
"Switch_Temp_U29 unc {}",
|
||||
"Switch_U21_Temp unc {}",
|
||||
"Switch_U33_Temp unc {}"
|
||||
]
|
||||
},
|
||||
"set_low_threshold": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool sensor thresh {}",
|
||||
"input_translator": "{}",
|
||||
"argument": [
|
||||
"Base_Temp_U5 lnc {}",
|
||||
"Base_Temp_U7 lnc {}",
|
||||
"CPU_Temp lnc {}",
|
||||
"Switch_Temp_U1 lnc {}",
|
||||
"Switch_Temp_U18 lnc {}",
|
||||
"Switch_Temp_U28 lnc {}",
|
||||
"Switch_Temp_U29 lnc {}",
|
||||
"Switch_U21_Temp lnc {}",
|
||||
"Switch_U33_Temp lnc {}"
|
||||
]
|
||||
},
|
||||
"get_high_critical_threshold": {
|
||||
"output_source": "ipmitool",
|
||||
"command": "ipmitool raw 0x04 0x27 {}",
|
||||
"argument": [
|
||||
"0x1",
|
||||
"0x2",
|
||||
"0x7",
|
||||
"0x3",
|
||||
"0x4",
|
||||
"0x5",
|
||||
"0x6",
|
||||
"0x56",
|
||||
"0x4c"
|
||||
],
|
||||
"output_translator": "int('{}'.split()[5], 16)"
|
||||
},
|
||||
"get_low_critical_threshold": {
|
||||
"output_source": "value",
|
||||
"value": "N/A"
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica Seastone2
|
||||
#
|
||||
# Watchdog contains an implementation of SONiC Platform Base API
|
||||
#
|
||||
#############################################################################
|
||||
import os
|
||||
import time
|
||||
|
||||
try:
|
||||
from sonic_platform_base.watchdog_base import WatchdogBase
|
||||
from sonic_platform.common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
PLATFORM_CPLD_PATH = '/sys/devices/platform/baseboard/'
|
||||
GETREG_FILE = 'getreg'
|
||||
SETREG_FILE = 'setreg'
|
||||
WDT_ENABLE_REG = '0xA181'
|
||||
WDT_TIMER_L_BIT_REG = '0xA182'
|
||||
WDT_TIMER_M_BIT_REG = '0xA183'
|
||||
WDT_TIMER_H_BIT_REG = '0xA184'
|
||||
WDT_KEEP_ALVIVE_REG = '0xA185'
|
||||
ENABLE_CMD = '0x1'
|
||||
DISABLE_CMD = '0x0'
|
||||
WDT_COMMON_ERROR = -1
|
||||
|
||||
|
||||
class Watchdog(WatchdogBase):
|
||||
|
||||
def __init__(self):
|
||||
self._api_common = Common()
|
||||
|
||||
# Init cpld reg path
|
||||
self.setreg_path = os.path.join(PLATFORM_CPLD_PATH, SETREG_FILE)
|
||||
self.getreg_path = os.path.join(PLATFORM_CPLD_PATH, GETREG_FILE)
|
||||
|
||||
# Set default value
|
||||
self._disable()
|
||||
self.armed = False
|
||||
self.timeout = self._gettimeout()
|
||||
|
||||
def _enable(self):
|
||||
"""
|
||||
Turn on the watchdog timer
|
||||
"""
|
||||
# echo 0xA181 0x1 > /sys/devices/platform/baseboard/setreg
|
||||
enable_val = '{} {}'.format(WDT_ENABLE_REG, ENABLE_CMD)
|
||||
return self._api_common.write_txt_file(self.setreg_path, enable_val)
|
||||
|
||||
def _disable(self):
|
||||
"""
|
||||
Turn off the watchdog timer
|
||||
"""
|
||||
# echo 0xA181 0x0 > /sys/devices/platform/baseboard/setreg
|
||||
disable_val = '{} {}'.format(WDT_ENABLE_REG, DISABLE_CMD)
|
||||
return self._api_common.write_txt_file(self.setreg_path, disable_val)
|
||||
|
||||
def _keepalive(self):
|
||||
"""
|
||||
Keep alive watchdog timer
|
||||
"""
|
||||
# echo 0xA185 0x1 > /sys/devices/platform/baseboard/setreg
|
||||
enable_val = '{} {}'.format(WDT_KEEP_ALVIVE_REG, ENABLE_CMD)
|
||||
return self._api_common.write_txt_file(self.setreg_path, enable_val)
|
||||
|
||||
def _get_level_hex(self, sub_hex):
|
||||
sub_hex_str = sub_hex.replace("x", "0")
|
||||
return hex(int(sub_hex_str, 16))
|
||||
|
||||
def _seconds_to_lmh_hex(self, seconds):
|
||||
ms = seconds*1000 # calculate timeout in ms format
|
||||
hex_str = hex(ms)
|
||||
l = self._get_level_hex(hex_str[-2:])
|
||||
m = self._get_level_hex(hex_str[-4:-2])
|
||||
h = self._get_level_hex(hex_str[-6:-4])
|
||||
return (l, m, h)
|
||||
|
||||
def _settimeout(self, seconds):
|
||||
"""
|
||||
Set watchdog timer timeout
|
||||
@param seconds - timeout in seconds
|
||||
@return is the actual set timeout
|
||||
"""
|
||||
# max = 0xffffff = 16777.215 seconds
|
||||
|
||||
(l, m, h) = self._seconds_to_lmh_hex(seconds)
|
||||
set_h_val = '{} {}'.format(WDT_TIMER_H_BIT_REG, h)
|
||||
set_m_val = '{} {}'.format(WDT_TIMER_M_BIT_REG, m)
|
||||
set_l_val = '{} {}'.format(WDT_TIMER_L_BIT_REG, l)
|
||||
|
||||
self._api_common.write_txt_file(
|
||||
self.setreg_path, set_h_val) # set high bit
|
||||
self._api_common.write_txt_file(
|
||||
self.setreg_path, set_m_val) # set med bit
|
||||
self._api_common.write_txt_file(
|
||||
self.setreg_path, set_l_val) # set low bit
|
||||
|
||||
return seconds
|
||||
|
||||
def _gettimeout(self):
|
||||
"""
|
||||
Get watchdog timeout
|
||||
@return watchdog timeout
|
||||
"""
|
||||
|
||||
h_bit = self._api_common.get_reg(
|
||||
self.getreg_path, WDT_TIMER_H_BIT_REG)
|
||||
m_bit = self._api_common.get_reg(
|
||||
self.getreg_path, WDT_TIMER_M_BIT_REG)
|
||||
l_bit = self._api_common.get_reg(
|
||||
self.getreg_path, WDT_TIMER_L_BIT_REG)
|
||||
|
||||
hex_time = '0x{}{}{}'.format(h_bit[2:], m_bit[2:], l_bit[2:])
|
||||
ms = int(hex_time, 16)
|
||||
return int(float(ms)/1000)
|
||||
|
||||
#################################################################
|
||||
|
||||
def arm(self, seconds):
|
||||
"""
|
||||
Arm the hardware watchdog with a timeout of <seconds> seconds.
|
||||
If the watchdog is currently armed, calling this function will
|
||||
simply reset the timer to the provided value. If the underlying
|
||||
hardware does not support the value provided in <seconds>, this
|
||||
method should arm the watchdog with the *next greater* available
|
||||
value.
|
||||
Returns:
|
||||
An integer specifying the *actual* number of seconds the watchdog
|
||||
was armed with. On failure returns -1.
|
||||
"""
|
||||
|
||||
ret = WDT_COMMON_ERROR
|
||||
if seconds < 0:
|
||||
return ret
|
||||
|
||||
try:
|
||||
if self.timeout != seconds:
|
||||
self.timeout = self._settimeout(seconds)
|
||||
|
||||
if self.armed:
|
||||
self._keepalive()
|
||||
else:
|
||||
self._enable()
|
||||
self.armed = True
|
||||
|
||||
ret = self.timeout
|
||||
self.arm_timestamp = time.time()
|
||||
except IOError as e:
|
||||
pass
|
||||
|
||||
return ret
|
||||
|
||||
def disarm(self):
|
||||
"""
|
||||
Disarm the hardware watchdog
|
||||
Returns:
|
||||
A boolean, True if watchdog is disarmed successfully, False if not
|
||||
"""
|
||||
disarmed = False
|
||||
if self.is_armed():
|
||||
try:
|
||||
self._disable()
|
||||
self.armed = False
|
||||
disarmed = True
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return disarmed
|
||||
|
||||
def is_armed(self):
|
||||
"""
|
||||
Retrieves the armed state of the hardware watchdog.
|
||||
Returns:
|
||||
A boolean, True if watchdog is armed, False if not
|
||||
"""
|
||||
|
||||
return self.armed
|
||||
|
||||
def get_remaining_time(self):
|
||||
"""
|
||||
If the watchdog is armed, retrieve the number of seconds remaining on
|
||||
the watchdog timer
|
||||
Returns:
|
||||
An integer specifying the number of seconds remaining on thei
|
||||
watchdog timer. If the watchdog is not armed, returns -1.
|
||||
"""
|
||||
|
||||
return int(self.timeout - (time.time() - self.arm_timestamp)) if self.armed else WDT_COMMON_ERROR
|
@ -1,2 +1,4 @@
|
||||
seastone2/cfg/seastone2-modules.conf etc/modules-load.d
|
||||
seastone2/systemd/platform-modules-seastone2.service lib/systemd/system
|
||||
seastone2/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_seastone_2-r0
|
||||
services/platform_api/platform_api_mgnt.sh usr/local/bin
|
@ -1,3 +1,5 @@
|
||||
depmod -a
|
||||
systemctl enable platform-modules-seastone2.service
|
||||
systemctl start platform-modules-seastone2.service
|
||||
|
||||
/usr/local/bin/platform_api_mgnt.sh install
|
||||
|
@ -16,6 +16,10 @@ override_dh_auto_build:
|
||||
cd $(MOD_SRC_DIR)/$${mod}; \
|
||||
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
|
||||
cd $(MOD_SRC_DIR); \
|
||||
if [ $$mod = "seastone2" ]; then \
|
||||
cd services/platform_api; \
|
||||
python2 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
|
||||
fi \
|
||||
done)
|
||||
|
||||
override_dh_auto_install:
|
||||
|
File diff suppressed because it is too large
Load Diff
30
platform/broadcom/sonic-platform-modules-cel/services/platform_api/setup.py
Executable file
30
platform/broadcom/sonic-platform-modules-cel/services/platform_api/setup.py
Executable file
@ -0,0 +1,30 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='sonic-platform',
|
||||
version='1.0',
|
||||
description='SONiC platform API implementation on Celestica Platforms',
|
||||
license='Apache 2.0',
|
||||
author='SONiC Team',
|
||||
author_email='linuxnetdev@microsoft.com',
|
||||
url='https://github.com/Azure/sonic-buildimage',
|
||||
maintainer='Wirut Getbamrung',
|
||||
maintainer_email='wgetbumr@celestica.com',
|
||||
packages=[
|
||||
'sonic_platform',
|
||||
],
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Plugins',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: Information Technology',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: POSIX :: Linux',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Utilities',
|
||||
],
|
||||
keywords='sonic SONiC platform PLATFORM',
|
||||
)
|
||||
|
@ -0,0 +1,2 @@
|
||||
import chassis
|
||||
import platform
|
@ -0,0 +1,325 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Module contains an implementation of SONiC Platform Base API and
|
||||
# provides the Chassis information which are available in the platform
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
import sys
|
||||
from sonic_platform_base.chassis_base import ChassisBase
|
||||
from common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Chassis(ChassisBase):
|
||||
"""Platform-specific Chassis class"""
|
||||
|
||||
CHASSIS_CONFIG = 'chassis.json'
|
||||
THERMAL_CONFIG = 'thermal.json'
|
||||
SFP_CONFIG = 'sfp.json'
|
||||
PSU_CONFIG = 'psu.json'
|
||||
FAN_CONFIG = 'fan.json'
|
||||
COMPONENT_CONFIG = 'component.json'
|
||||
|
||||
def __init__(self):
|
||||
ChassisBase.__init__(self)
|
||||
self._api_common = Common()
|
||||
config_path = self._api_common.get_config_path(self.CHASSIS_CONFIG)
|
||||
self._config = self._api_common.load_json_file(config_path)
|
||||
|
||||
self.sfp_module_initialized = False
|
||||
self.__initialize_eeprom()
|
||||
|
||||
if not self._api_common.is_host():
|
||||
self.__initialize_fan()
|
||||
self.__initialize_psu()
|
||||
self.__initialize_thermals()
|
||||
else:
|
||||
self.__initialize_components()
|
||||
|
||||
def __initialize_fan(self):
|
||||
from sonic_platform.fan import Fan
|
||||
from sonic_platform.fan_drawer import FanDrawer
|
||||
|
||||
fan_config_path = self._api_common.get_config_path(self.FAN_CONFIG)
|
||||
self._fan_config = self._api_common.load_json_file(fan_config_path)
|
||||
|
||||
if self._fan_config:
|
||||
fan_index = 0
|
||||
for drawer_index in range(0, self._fan_config['drawer_num']):
|
||||
drawer_fan_list = []
|
||||
for index in range(0, self._fan_config['fan_num_per_drawer']):
|
||||
fan = Fan(fan_index, conf=self._fan_config)
|
||||
fan_index += 1
|
||||
self._fan_list.append(fan)
|
||||
drawer_fan_list.append(fan)
|
||||
fan_drawer = FanDrawer(drawer_index, drawer_fan_list)
|
||||
self._fan_drawer_list.append(fan_drawer)
|
||||
|
||||
def __initialize_sfp(self):
|
||||
from sonic_platform.sfp import Sfp
|
||||
|
||||
sfp_config_path = self._api_common.get_config_path(self.SFP_CONFIG)
|
||||
sfp_config = self._api_common.load_json_file(sfp_config_path)
|
||||
|
||||
sfp_index = 0
|
||||
for index in range(0, sfp_config['port_num']):
|
||||
sfp = Sfp(sfp_index, conf=sfp_config)
|
||||
self._sfp_list.append(sfp)
|
||||
sfp_index += 1
|
||||
self.sfp_module_initialized = True
|
||||
|
||||
def __initialize_psu(self):
|
||||
from sonic_platform.psu import Psu
|
||||
|
||||
psu_config_path = self._api_common.get_config_path(self.PSU_CONFIG)
|
||||
psu_config = self._api_common.load_json_file(psu_config_path)
|
||||
|
||||
if psu_config:
|
||||
psu_index = 0
|
||||
for index in range(0, psu_config['psu_num']):
|
||||
psu = Psu(psu_index, conf=psu_config,
|
||||
fan_conf=self._fan_config)
|
||||
psu_index += 1
|
||||
self._psu_list.append(psu)
|
||||
|
||||
def __initialize_thermals(self):
|
||||
from sonic_platform.thermal import Thermal
|
||||
|
||||
thermal_config_path = self._api_common.get_config_path(
|
||||
self.THERMAL_CONFIG)
|
||||
thermal_config = self._api_common.load_json_file(thermal_config_path)
|
||||
|
||||
thermal_index = 0
|
||||
for index in range(0, thermal_config['thermal_num']):
|
||||
thermal = Thermal(thermal_index, conf=thermal_config)
|
||||
thermal_index += 1
|
||||
self._thermal_list.append(thermal)
|
||||
|
||||
def __initialize_eeprom(self):
|
||||
from sonic_platform.eeprom import Tlv
|
||||
self._eeprom = Tlv(self._config)
|
||||
|
||||
def __initialize_components(self):
|
||||
from component import Component
|
||||
|
||||
component_config_path = self._api_common.get_config_path(
|
||||
self.COMPONENT_CONFIG)
|
||||
component_config = self._api_common.load_json_file(
|
||||
component_config_path)
|
||||
|
||||
for index in range(0, component_config['component_num']):
|
||||
component = Component(index, conf=component_config)
|
||||
self._component_list.append(component)
|
||||
|
||||
def get_base_mac(self):
|
||||
"""
|
||||
Retrieves the base MAC address for the chassis
|
||||
Returns:
|
||||
A string containing the MAC address in the format
|
||||
'XX:XX:XX:XX:XX:XX'
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Retrieves the full content of system EEPROM information for the chassis
|
||||
Returns:
|
||||
A dictionary where keys are the type code defined in
|
||||
OCP ONIE TlvInfo EEPROM format and values are their corresponding
|
||||
values.
|
||||
"""
|
||||
return self._eeprom.get_eeprom()
|
||||
|
||||
def get_reboot_cause(self):
|
||||
"""
|
||||
Retrieves the cause of the previous reboot
|
||||
Returns:
|
||||
A tuple (string, string) where the first element is a string
|
||||
containing the cause of the previous reboot. This string must be
|
||||
one of the predefined strings in this class. If the first string
|
||||
is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used
|
||||
to pass a description of the reboot cause.
|
||||
|
||||
Avaliable reboot cause:
|
||||
REBOOT_CAUSE_POWER_LOSS = "Power Loss"
|
||||
REBOOT_CAUSE_THERMAL_OVERLOAD_CPU = "Thermal Overload: CPU"
|
||||
REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC = "Thermal Overload: ASIC"
|
||||
REBOOT_CAUSE_THERMAL_OVERLOAD_OTHER = "Thermal Overload: Other"
|
||||
REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED = "Insufficient Fan Speed"
|
||||
REBOOT_CAUSE_WATCHDOG = "Watchdog"
|
||||
REBOOT_CAUSE_HARDWARE_OTHER = "Hardware - Other"
|
||||
REBOOT_CAUSE_NON_HARDWARE = "Non-Hardware"
|
||||
|
||||
"""
|
||||
reboot_cause = self._api_common.get_output(
|
||||
0, self._config['get_reboot_cause'], self.REBOOT_CAUSE_HARDWARE_OTHER)
|
||||
description = self._api_common.get_output(
|
||||
0, self._config['get_reboot_description'], 'Unknown')
|
||||
|
||||
return (reboot_cause, description)
|
||||
|
||||
# ##############################################################
|
||||
# ######################## SFP methods #########################
|
||||
# ##############################################################
|
||||
|
||||
def get_num_sfps(self):
|
||||
"""
|
||||
Retrieves the number of sfps available on this chassis
|
||||
Returns:
|
||||
An integer, the number of sfps available on this chassis
|
||||
"""
|
||||
if not self.sfp_module_initialized:
|
||||
self.__initialize_sfp()
|
||||
|
||||
return len(self._sfp_list)
|
||||
|
||||
def get_all_sfps(self):
|
||||
"""
|
||||
Retrieves all sfps available on this chassis
|
||||
Returns:
|
||||
A list of objects derived from SfpBase representing all sfps
|
||||
available on this chassis
|
||||
"""
|
||||
if not self.sfp_module_initialized:
|
||||
self.__initialize_sfp()
|
||||
|
||||
return self._sfp_list
|
||||
|
||||
def get_sfp(self, index):
|
||||
"""
|
||||
Retrieves sfp represented by (1-based) index <index>
|
||||
Args:
|
||||
index: An integer, the index (1-based) of the sfp to retrieve.
|
||||
The index should be the sequence of a physical port in a chassis,
|
||||
starting from 1.
|
||||
For example, 1 for Ethernet0, 2 for Ethernet4 and so on.
|
||||
Returns:
|
||||
An object dervied from SfpBase representing the specified sfp
|
||||
"""
|
||||
sfp = None
|
||||
if not self.sfp_module_initialized:
|
||||
self.__initialize_sfp()
|
||||
|
||||
try:
|
||||
# The index will start from 1
|
||||
sfp = self._sfp_list[index-1]
|
||||
except IndexError:
|
||||
sys.stderr.write("SFP index {} out of range (1-{})\n".format(
|
||||
index, len(self._sfp_list)))
|
||||
return sfp
|
||||
|
||||
##############################################################
|
||||
###################### Event methods #########################
|
||||
##############################################################
|
||||
|
||||
def get_change_event(self, timeout=0):
|
||||
"""
|
||||
Returns a nested dictionary containing all devices which have
|
||||
experienced a change at chassis level
|
||||
Args:
|
||||
timeout: Timeout in milliseconds (optional). If timeout == 0,
|
||||
this method will block until a change is detected.
|
||||
Returns:
|
||||
(bool, dict):
|
||||
- True if call successful, False if not;
|
||||
- A nested dictionary where key is a device type,
|
||||
value is a dictionary with key:value pairs in the format of
|
||||
{'device_id':'device_event'},
|
||||
where device_id is the device ID for this device and
|
||||
device_event,
|
||||
status='1' represents device inserted,
|
||||
status='0' represents device removed.
|
||||
Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
|
||||
indicates that fan 0 has been removed, fan 2
|
||||
has been inserted and sfp 11 has been removed.
|
||||
Specifically for SFP event, besides SFP plug in and plug out,
|
||||
there are some other error event could be raised from SFP, when
|
||||
these error happened, SFP eeprom will not be avalaible, XCVRD shall
|
||||
stop to read eeprom before SFP recovered from error status.
|
||||
status='2' I2C bus stuck,
|
||||
status='3' Bad eeprom,
|
||||
status='4' Unsupported cable,
|
||||
status='5' High Temperature,
|
||||
status='6' Bad cable.
|
||||
"""
|
||||
|
||||
if not self.sfp_module_initialized:
|
||||
self.__initialize_sfp()
|
||||
|
||||
return self._api_common.get_event(timeout, self._config['get_change_event'], self._sfp_list)
|
||||
|
||||
# ##############################################################
|
||||
# ###################### Other methods ########################
|
||||
# ##############################################################
|
||||
|
||||
def get_watchdog(self):
|
||||
"""
|
||||
Retreives hardware watchdog device on this chassis
|
||||
Returns:
|
||||
An object derived from WatchdogBase representing the hardware
|
||||
watchdog device
|
||||
"""
|
||||
if self._watchdog is None:
|
||||
wdt = self._api_common.get_output(
|
||||
0, self._config['get_watchdog'], None)
|
||||
self._watchdog = wdt()
|
||||
|
||||
return self._watchdog
|
||||
|
||||
# ##############################################################
|
||||
# ###################### Device methods ########################
|
||||
# ##############################################################
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the name of the device
|
||||
Returns:
|
||||
string: The name of the device
|
||||
"""
|
||||
return self._api_common.hwsku
|
||||
|
||||
def get_presence(self):
|
||||
"""
|
||||
Retrieves the presence of the PSU
|
||||
Returns:
|
||||
bool: True if PSU is present, False if not
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_model(self):
|
||||
"""
|
||||
Retrieves the model number (or part number) of the device
|
||||
Returns:
|
||||
string: Model/part number of device
|
||||
"""
|
||||
return self._eeprom.get_pn()
|
||||
|
||||
def get_serial(self):
|
||||
"""
|
||||
Retrieves the serial number of the device
|
||||
Returns:
|
||||
string: Serial number of device
|
||||
"""
|
||||
return self._eeprom.get_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
|
||||
"""
|
||||
return True
|
@ -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)
|
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Component contains an implementation of SONiC Platform Base API and
|
||||
# provides the components firmware management function
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
from sonic_platform_base.component_base import ComponentBase
|
||||
from common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Component(ComponentBase):
|
||||
"""Platform-specific Component class"""
|
||||
|
||||
def __init__(self, component_index, conf):
|
||||
ComponentBase.__init__(self)
|
||||
|
||||
self._index = component_index
|
||||
self._config = conf
|
||||
self._api_common = Common(self._config)
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the name of the component
|
||||
Returns:
|
||||
A string containing the name of the component
|
||||
"""
|
||||
default = 'N/A'
|
||||
config = self._config["get_name"]
|
||||
return self._api_common.get_output(self._index, config, default)
|
||||
|
||||
def get_description(self):
|
||||
"""
|
||||
Retrieves the description of the component
|
||||
Returns:
|
||||
A string containing the description of the component
|
||||
"""
|
||||
default = 'N/A'
|
||||
config = self._config["get_description"]
|
||||
return self._api_common.get_output(self._index, config, default)
|
||||
|
||||
def get_firmware_version(self):
|
||||
"""
|
||||
Retrieves the firmware version of the component
|
||||
Note: the firmware version will be read from HW
|
||||
Returns:
|
||||
A string containing the firmware version of the component
|
||||
"""
|
||||
default = 'N/A'
|
||||
config = self._config["get_firmware_version"]
|
||||
return self._api_common.get_output(self._index, config, default)
|
@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Platform and model specific eeprom subclass, inherits from the base class,
|
||||
# and provides the followings:
|
||||
# - the eeprom format definition
|
||||
# - specific encoder/decoder if there is special need
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from cStringIO import StringIO
|
||||
from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo
|
||||
except ImportError, e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
CACHE_ROOT = '/var/cache/sonic/decode-syseeprom'
|
||||
CACHE_FILE = 'syseeprom_cache'
|
||||
|
||||
|
||||
class Tlv(eeprom_tlvinfo.TlvInfoDecoder):
|
||||
|
||||
EEPROM_DECODE_HEADLINES = 6
|
||||
DEFAULT_EEPROM_PATH = "/sys/class/i2c-adapter/i2c-12/12-0050/eeprom"
|
||||
|
||||
def __init__(self, conf=None):
|
||||
eeprom_path = conf.get('eeprom', self.DEFAULT_EEPROM_PATH)
|
||||
super(Tlv, self).__init__(eeprom_path, 0, '', True)
|
||||
|
||||
self._eeprom = self._load_eeprom()
|
||||
|
||||
def _parse_output(self, decode_output):
|
||||
decode_output.replace('\0', '')
|
||||
lines = decode_output.split('\n')
|
||||
lines = lines[self.EEPROM_DECODE_HEADLINES:]
|
||||
_eeprom_info_dict = dict()
|
||||
|
||||
for line in lines:
|
||||
try:
|
||||
match = re.search(
|
||||
'(0x[0-9a-fA-F]{2})([\s]+[\S]+[\s]+)([\S]+)', line)
|
||||
if match is not None:
|
||||
idx = match.group(1)
|
||||
value = match.group(3).rstrip('\0')
|
||||
|
||||
_eeprom_info_dict[idx] = value
|
||||
except Exception:
|
||||
pass
|
||||
return _eeprom_info_dict
|
||||
|
||||
def _load_eeprom(self):
|
||||
original_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
try:
|
||||
self.read_eeprom_db()
|
||||
except Exception:
|
||||
decode_output = sys.stdout.getvalue()
|
||||
sys.stdout = original_stdout
|
||||
return self._parse_output(decode_output)
|
||||
|
||||
status = self.check_status()
|
||||
if 'ok' not in status:
|
||||
return False
|
||||
|
||||
if not os.path.exists(CACHE_ROOT):
|
||||
try:
|
||||
os.makedirs(CACHE_ROOT)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
#
|
||||
# only the eeprom classes that inherit from eeprom_base
|
||||
# support caching. Others will work normally
|
||||
#
|
||||
try:
|
||||
self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
e = self.read_eeprom()
|
||||
if e is None:
|
||||
return 0
|
||||
|
||||
try:
|
||||
self.update_cache(e)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.decode_eeprom(e)
|
||||
decode_output = sys.stdout.getvalue()
|
||||
sys.stdout = original_stdout
|
||||
|
||||
# (is_valid, valid_crc) = self.is_checksum_valid(e)
|
||||
# if not is_valid:
|
||||
# return False
|
||||
|
||||
return self._parse_output(decode_output)
|
||||
|
||||
def get_eeprom(self):
|
||||
return self._eeprom
|
||||
|
||||
def get_serial(self):
|
||||
return self._eeprom.get('0x23', "Undefined.")
|
||||
|
||||
def get_mac(self):
|
||||
return self._eeprom.get('0x24', "Undefined.")
|
@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Module contains an implementation of SONiC Platform Base API and
|
||||
# provides the fan status which are available in the platform
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
from sonic_platform_base.fan_base import FanBase
|
||||
from common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Fan(FanBase):
|
||||
"""Platform-specific Fan class"""
|
||||
|
||||
def __init__(self, index, is_psu_fan=False, psu_index=0, conf=None):
|
||||
FanBase.__init__(self)
|
||||
|
||||
self._fan_index = index
|
||||
self._config = conf
|
||||
self._api_common = Common()
|
||||
|
||||
self._is_psu_fan = is_psu_fan
|
||||
if self._is_psu_fan:
|
||||
self._initialize_psu_fan(psu_index)
|
||||
|
||||
self._name = self.get_name()
|
||||
|
||||
def _initialize_psu_fan(self, psu_index):
|
||||
self._psu_index = psu_index
|
||||
self._psu_fan_config = self._config['psu_fan'][self._psu_index]
|
||||
|
||||
def get_direction(self):
|
||||
"""
|
||||
Retrieves the direction of fan
|
||||
Returns:
|
||||
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
|
||||
depending on fan direction
|
||||
"""
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_direction'], self.FAN_DIRECTION_NOT_APPLICABLE)
|
||||
|
||||
def get_speed(self):
|
||||
"""
|
||||
Retrieves the speed of fan as a percentage of full speed
|
||||
Returns:
|
||||
An integer, the percentage of full fan speed, in the range 0 (off)
|
||||
to 100 (full speed)
|
||||
|
||||
Note:
|
||||
speed = pwm_in/255*100
|
||||
"""
|
||||
config = self._config['get_speed'] if not self._is_psu_fan else self._psu_fan_config['get_speed']
|
||||
max_rpm = config['max_rear'] if 'R' in self._name else config['max_front']
|
||||
raw_speed = self._api_common.get_output(
|
||||
self._fan_index, config, 0)
|
||||
|
||||
return int(float(raw_speed) / max_rpm * 100.0)
|
||||
|
||||
def get_target_speed(self):
|
||||
"""
|
||||
Retrieves the target (expected) speed of the fan
|
||||
Returns:
|
||||
An integer, the percentage of full fan speed, in the range 0 (off)
|
||||
to 100 (full speed)
|
||||
|
||||
Note:
|
||||
0 : when PWM mode is not in use
|
||||
pwm : when pwm mode is not use
|
||||
"""
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_target_speed'], Common.NULL_VAL)
|
||||
|
||||
def get_speed_tolerance(self):
|
||||
"""
|
||||
Retrieves the speed tolerance of the fan
|
||||
Returns:
|
||||
An integer, the percentage of variance from target speed which is
|
||||
considered tolerable
|
||||
"""
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_speed_tolerance'], Common.NULL_VAL)
|
||||
|
||||
def set_speed(self, speed):
|
||||
"""
|
||||
Sets the fan speed
|
||||
Args:
|
||||
speed: An integer, the percentage of full fan speed to set fan to,
|
||||
in the range 0 (off) to 100 (full speed)
|
||||
Returns:
|
||||
A boolean, True if speed is set successfully, False if not
|
||||
|
||||
Note:
|
||||
Depends on pwm or target mode is selected:
|
||||
1) pwm = speed_pc * 255 <-- Currently use this mode.
|
||||
2) target_pwm = speed_pc * 100 / 255
|
||||
2.1) set pwm{}_enable to 3
|
||||
|
||||
ipmitool raw 0x3a 0x0e 0x00 > enable auto fcs
|
||||
ipmitool raw 0x3a 0x0e 0x01 > disable auto fcs
|
||||
"""
|
||||
if speed not in range(1, 101) or self._is_psu_fan:
|
||||
return False
|
||||
|
||||
return self._api_common.set_output(self._fan_index, speed, self._config['set_speed'])
|
||||
|
||||
def set_status_led(self, color):
|
||||
"""
|
||||
Sets the state of the fan module status LED
|
||||
Args:
|
||||
color: A string representing the color with which to set the
|
||||
fan module status LED
|
||||
Returns:
|
||||
bool: True if status LED state is set successfully, False if not
|
||||
|
||||
|
||||
Note: Required Manual Control LED mode ('ipmitool raw 0x3a 0x0f 0x2 0x0')
|
||||
"""
|
||||
config = self._config['set_status_led']
|
||||
avaliable_input = config.get('avaliable_input')
|
||||
if (avaliable_input and color not in avaliable_input) or self._is_psu_fan:
|
||||
return False
|
||||
|
||||
return self._api_common.set_output(self._fan_index, color, config)
|
||||
|
||||
def get_status_led(self):
|
||||
"""
|
||||
Gets the state of the fan status LED
|
||||
Returns:
|
||||
A string, one of the predefined STATUS_LED_COLOR_* strings above
|
||||
|
||||
Note:
|
||||
STATUS_LED_COLOR_GREEN = "green"
|
||||
STATUS_LED_COLOR_AMBER = "amber"
|
||||
STATUS_LED_COLOR_RED = "red"
|
||||
STATUS_LED_COLOR_OFF = "off"
|
||||
"""
|
||||
|
||||
default = self.STATUS_LED_COLOR_OFF
|
||||
|
||||
if self._is_psu_fan:
|
||||
return default
|
||||
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_status_led'], default)
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the name of the device
|
||||
Returns:
|
||||
string: The name of the device
|
||||
"""
|
||||
config = self._config['get_name'] if not self._is_psu_fan else self._psu_fan_config['get_name']
|
||||
return self._api_common.get_output(self._fan_index, config, Common.NULL_VAL)
|
||||
|
||||
def get_presence(self):
|
||||
"""
|
||||
Retrieves the presence of the FAN
|
||||
Returns:
|
||||
bool: True if FAN is present, False if not
|
||||
"""
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_presence'], False)
|
||||
|
||||
def get_model(self):
|
||||
"""
|
||||
Retrieves the model number (or part number) of the device
|
||||
Returns:
|
||||
string: Model/part number of device
|
||||
"""
|
||||
default = Common.NULL_VAL
|
||||
if self._is_psu_fan:
|
||||
return default
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_model'], default)
|
||||
|
||||
def get_serial(self):
|
||||
"""
|
||||
Retrieves the serial number of the device
|
||||
Returns:
|
||||
string: Serial number of device
|
||||
"""
|
||||
default = Common.NULL_VAL
|
||||
if self._is_psu_fan:
|
||||
return default
|
||||
return self._api_common.get_output(self._fan_index, self._config['get_serial'], default)
|
||||
|
||||
def get_status(self):
|
||||
"""
|
||||
Retrieves the operational status of the device
|
||||
Returns:
|
||||
A boolean value, True if device is operating properly, False if not
|
||||
"""
|
||||
return self.get_presence() and self.get_speed() > 10
|
@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Module contains an implementation of SONiC Platform Base API and
|
||||
# provides the fan status which are available in the platform
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
from sonic_platform_base.fan_drawer_base import FanDrawerBase
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class FanDrawer(FanDrawerBase):
|
||||
|
||||
def __init__(self, index, fan_list):
|
||||
FanDrawerBase.__init__(self)
|
||||
|
||||
self._fan_list = fan_list
|
||||
self._index = index + 1
|
||||
|
||||
def set_status_led(self, color):
|
||||
"""
|
||||
Sets the state of the fan drawer status LED
|
||||
Args:
|
||||
color: A string representing the color with which to set the
|
||||
fan drawer status LED
|
||||
Returns:
|
||||
bool: True if status LED state is set successfully, False if not
|
||||
"""
|
||||
return self._fan_list[0].set_status_led(color)
|
||||
|
||||
def get_status_led(self, color):
|
||||
"""
|
||||
Gets the state of the fan drawer LED
|
||||
Returns:
|
||||
A string, one of the predefined STATUS_LED_COLOR_* strings above
|
||||
"""
|
||||
return self._fan_list[0].get_status_led()
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the name of the device
|
||||
Returns:
|
||||
string: The name of the device
|
||||
"""
|
||||
return 'Fan {}'.format(self._index)
|
||||
|
||||
def get_presence(self):
|
||||
"""
|
||||
Retrieves the presence of the FAN
|
||||
Returns:
|
||||
bool: True if FAN is present, False if not
|
||||
"""
|
||||
return self._fan_list[0].get_presence()
|
||||
|
||||
def get_model(self):
|
||||
"""
|
||||
Retrieves the model number (or part number) of the device
|
||||
Returns:
|
||||
string: Model/part number of device
|
||||
"""
|
||||
return self._fan_list[0].get_model()
|
||||
|
||||
def get_serial(self):
|
||||
"""
|
||||
Retrieves the serial number of the device
|
||||
Returns:
|
||||
string: Serial number of device
|
||||
"""
|
||||
return self._fan_list[0].get_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
|
||||
"""
|
||||
return self._fan_list[0].get_status()
|
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Module contains an implementation of SONiC Platform Base API and
|
||||
# provides the platform information
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
from sonic_platform_base.platform_base import PlatformBase
|
||||
from sonic_platform.chassis import Chassis
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Platform(PlatformBase):
|
||||
"""Platform-specific Platform class"""
|
||||
|
||||
def __init__(self):
|
||||
PlatformBase.__init__(self)
|
||||
self._chassis = Chassis()
|
@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Module contains an implementation of SONiC Platform Base API and
|
||||
# provides the psu status which are available in the platform
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
from sonic_platform_base.psu_base import PsuBase
|
||||
from common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Psu(PsuBase):
|
||||
"""Platform-specific Fan class"""
|
||||
|
||||
def __init__(self, index, conf=None, fan_conf=None):
|
||||
PsuBase.__init__(self)
|
||||
|
||||
self._psu_index = index
|
||||
self._config = conf
|
||||
self._api_common = Common(self._config)
|
||||
self._fan_conf = fan_conf
|
||||
self._initialize_psu_fan()
|
||||
|
||||
def _initialize_psu_fan(self):
|
||||
from sonic_platform.fan import Fan
|
||||
|
||||
num_fan = self._fan_conf['psu_fan'][self._psu_index]["num_of_fan"]
|
||||
for fan_index in range(0, num_fan):
|
||||
fan = Fan(fan_index, is_psu_fan=True,
|
||||
psu_index=self._psu_index, conf=self._fan_conf)
|
||||
self._fan_list.append(fan)
|
||||
|
||||
def get_voltage(self):
|
||||
"""
|
||||
Retrieves current PSU voltage output
|
||||
|
||||
Returns:
|
||||
A float number, the output voltage in volts,
|
||||
e.g. 12.1
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_voltage'], 0.0)
|
||||
|
||||
def get_current(self):
|
||||
"""
|
||||
Retrieves present electric current supplied by PSU
|
||||
|
||||
Returns:
|
||||
A float number, the electric current in amperes, e.g 15.4
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_current'], 0.0)
|
||||
|
||||
def get_power(self):
|
||||
"""
|
||||
Retrieves current energy supplied by PSU
|
||||
|
||||
Returns:
|
||||
A float number, the power in watts, e.g. 302.6
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_current'], 0.0)
|
||||
|
||||
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._api_common.get_output(self._psu_index, self._config['get_powergood_status'], False)
|
||||
|
||||
def set_status_led(self, color):
|
||||
"""
|
||||
Sets the state of the PSU status LED
|
||||
Note:
|
||||
Seastone2 CPLD able to set only AMBER color.
|
||||
This function should be disable auto mode before execute
|
||||
command: ipmitool raw 0x3a 0x0f 0x02 0x00
|
||||
Args:
|
||||
color: A string representing the color with which to set the
|
||||
PSU status LED
|
||||
|
||||
Returns:
|
||||
bool: True if status LED state is set successfully, False if not
|
||||
"""
|
||||
return self._api_common.set_output(self._psu_index, color, self._config['set_status_led'])
|
||||
|
||||
def get_status_led(self):
|
||||
"""
|
||||
Gets the state of the PSU status LED
|
||||
Note:
|
||||
Seastone2 PSU LED got only 2 mode, AMBER and Hardware control mode.
|
||||
Returns:
|
||||
A string, one of the predefined STATUS_LED_COLOR_* strings above
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_status_led'], Common.NULL_VAL)
|
||||
|
||||
def get_temperature(self):
|
||||
"""
|
||||
Retrieves current temperature reading from PSU
|
||||
|
||||
Returns:
|
||||
A float number of current temperature in Celsius up to nearest thousandth
|
||||
of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_temperature'], 0.0)
|
||||
|
||||
def get_temperature_high_threshold(self):
|
||||
"""
|
||||
Retrieves the high threshold temperature of PSU
|
||||
Returns:
|
||||
A float number, the high threshold temperature of PSU in Celsius
|
||||
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_temperature_high_threshold'], 0.0)
|
||||
|
||||
def get_voltage_high_threshold(self):
|
||||
"""
|
||||
Retrieves the high threshold PSU voltage output
|
||||
|
||||
Returns:
|
||||
ucr as float number and return 0 if the BMC output is na.
|
||||
A float number, the high threshold output voltage in volts,
|
||||
e.g. 12.1
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_voltage_high_threshold'], 0.0)
|
||||
|
||||
def get_voltage_low_threshold(self):
|
||||
"""
|
||||
Retrieves the low threshold PSU voltage output
|
||||
|
||||
Returns:
|
||||
lcr as float number and return 0 if the BMC output is na.
|
||||
A float number, the low threshold output voltage in volts,
|
||||
e.g. 12.1
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_voltage_low_threshold'], 0.0)
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the name of the device
|
||||
Returns:
|
||||
string: The name of the device
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_name'], Common.NULL_VAL)
|
||||
|
||||
def get_presence(self):
|
||||
"""
|
||||
Retrieves the presence of the PSU
|
||||
Returns:
|
||||
bool: True if PSU is present, False if not
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_name'], False)
|
||||
|
||||
def get_model(self):
|
||||
"""
|
||||
Retrieves the model number (or part number) of the device
|
||||
Returns:
|
||||
string: Model/part number of device
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_model'], Common.NULL_VAL)
|
||||
|
||||
def get_serial(self):
|
||||
"""
|
||||
Retrieves the serial number of the device
|
||||
Returns:
|
||||
string: Serial number of device
|
||||
"""
|
||||
return self._api_common.get_output(self._psu_index, self._config['get_serial'], Common.NULL_VAL)
|
||||
|
||||
def get_status(self):
|
||||
"""
|
||||
Retrieves the operational status of the device
|
||||
Returns:
|
||||
A boolean value, True if device is operating properly, False if not
|
||||
"""
|
||||
return self.get_powergood_status()
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Celestica
|
||||
#
|
||||
# Module contains an implementation of SONiC Platform Base API and
|
||||
# provides the thermal status which are available in the platform
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
from sonic_platform_base.thermal_base import ThermalBase
|
||||
from common import Common
|
||||
except ImportError as e:
|
||||
raise ImportError(str(e) + "- required module not found")
|
||||
|
||||
|
||||
class Thermal(ThermalBase):
|
||||
"""Platform-specific Thermal class"""
|
||||
|
||||
def __init__(self, index, _thermal_index=0, conf=None):
|
||||
ThermalBase.__init__(self)
|
||||
|
||||
self._thermal_index = index
|
||||
self._config = conf
|
||||
self._api_common = Common(self._config)
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Retrieves the human-readable name of a thermal sensor by 1-based index
|
||||
|
||||
Returns:
|
||||
:param index: An integer, 1-based index of the thermal sensor of which to query status
|
||||
:return: String,
|
||||
A string representing the name of the thermal sensor.
|
||||
"""
|
||||
return self._api_common.get_output(self._thermal_index, self._config['get_name'], Common.NULL_VAL)
|
||||
|
||||
def get_temperature(self):
|
||||
"""
|
||||
Retrieves current temperature reading from thermal
|
||||
|
||||
by using command ipmitool raw 0x04 0x2D [address]
|
||||
|
||||
Returns:
|
||||
A float number of current temperature in Celsius up to nearest thousandth
|
||||
of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['get_temperature'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def get_high_threshold(self):
|
||||
"""
|
||||
Retrieves the high threshold temperature of thermal
|
||||
Returns:
|
||||
A float number, the high threshold temperature of thermal in Celsius
|
||||
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['get_high_threshold'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def get_low_threshold(self):
|
||||
"""
|
||||
Retrieves the low threshold temperature of thermal
|
||||
|
||||
Returns:
|
||||
lnc as float number and return 0 if the BMC output is na.
|
||||
A float number, the low threshold temperature of thermal in Celsius
|
||||
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['get_low_threshold'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def set_high_threshold(self, temperature):
|
||||
"""
|
||||
Sets the high threshold temperature of thermal
|
||||
|
||||
For AMI BMC device :
|
||||
use ipmitool command
|
||||
ipmitool sensor thresh [sensor name] unc [0>= temp_value <=62]
|
||||
if the current value of unc is 'na' ipmitool can't be set the value
|
||||
|
||||
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
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['set_high_threshold'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def set_low_threshold(self, temperature):
|
||||
"""
|
||||
Sets the low threshold temperature of thermal
|
||||
|
||||
For AMI BMC device :
|
||||
use ipmitool command
|
||||
ipmitool sensor thresh [sensor name] lnc [temp_value]
|
||||
if the current value of lnc is 'na' ipmitool can't be set the value
|
||||
|
||||
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
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['set_low_threshold'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def get_high_critical_threshold(self):
|
||||
"""
|
||||
Retrieves the high critical threshold temperature of thermal
|
||||
|
||||
Returns:
|
||||
ucr as float number and return 0 if the BMC output is na.
|
||||
A float number, the high critical threshold temperature of thermal in Celsius
|
||||
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['get_high_critical_threshold'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def get_low_critical_threshold(self):
|
||||
"""
|
||||
Retrieves the low critical threshold temperature of thermal
|
||||
|
||||
Returns:
|
||||
lnr as float number and return 0 if the BMC output is na.
|
||||
A float number, the low critical threshold temperature of thermal in Celsius
|
||||
up to nearest thousandth of one degree Celsius, e.g. 30.125
|
||||
"""
|
||||
output = self._api_common.get_output(
|
||||
self._thermal_index, self._config['get_low_critical_threshold'], Common.NULL_VAL)
|
||||
return float(output) if output != Common.NULL_VAL else output
|
||||
|
||||
def get_presence(self):
|
||||
"""
|
||||
Retrieves the presence of the device
|
||||
Returns:
|
||||
bool: True if device is present, False if not
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_model(self):
|
||||
"""
|
||||
Retrieves the model number (or part number) of the device
|
||||
|
||||
Returns:
|
||||
string: Model/part number of device
|
||||
"""
|
||||
return Common.NULL_VAL
|
||||
|
||||
def get_serial(self):
|
||||
"""
|
||||
Retrieves the serial number of the device
|
||||
|
||||
Returns:
|
||||
string: Serial number of device
|
||||
"""
|
||||
return Common.NULL_VAL
|
||||
|
||||
def get_status(self):
|
||||
"""
|
||||
Retrieves the operational status of the device
|
||||
|
||||
Returns:
|
||||
A boolean value, True if device is operating properly, False if not
|
||||
"""
|
||||
return True
|
Loading…
Reference in New Issue
Block a user