[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:
Wirut Getbamrung 2020-09-10 00:32:56 +07:00 committed by GitHub
parent e9918bac07
commit aa601f4b0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 5532 additions and 789 deletions

View File

@ -2,5 +2,6 @@
"skip_ledd": true,
"skip_xcvrd": false,
"skip_psud": false,
"skip_syseepromd": false
"skip_syseepromd": false,
"skip_fancontrol": true
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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

View File

@ -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
}
}
]
}

View File

@ -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])"
}
}

View File

@ -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"
]
}

View File

@ -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"
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View 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',
)

View File

@ -0,0 +1,2 @@
import chassis
import platform

View File

@ -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

View 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)

View File

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

View File

@ -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.")

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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