[device/celestica]: Add xcvrd event support for Haliburton (#6517)

#### Why I did it
- The xcvrd service requires an event detection function, unplug or plug in the transceiver.

#### How I did it
- Add sysfs interrupt to notify userspace app of external interrupt
- Implement get_change_event() in chassis api.
- Also begin installing Python 3 sonic-platform package for Celestica platforms
This commit is contained in:
Wirut Getbamrung 2021-03-09 01:10:11 +07:00 committed by GitHub
parent 13467e5853
commit 6ae2cb5607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 393 additions and 48 deletions

View File

@ -13,13 +13,11 @@ import subprocess
import json
try:
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.fan import Fan
from sonic_platform.psu import Psu
from sonic_platform.component import Component
from sonic_platform.thermal import Thermal
from sonic_platform.sfp import Sfp
from sonic_platform.eeprom import Tlv
from sonic_py_common import device_info
from .event import SfpEvent
from .helper import APIHelper
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
@ -40,29 +38,62 @@ class Chassis(ChassisBase):
def __init__(self):
ChassisBase.__init__(self)
self.config_data = {}
self._api_helper = APIHelper()
self.sfp_module_initialized = False
self.__initialize_eeprom()
self.is_host = self._api_helper.is_host()
if not self.is_host:
self.__initialize_fan()
self.__initialize_psu()
self.__initialize_thermals()
else:
self.__initialize_components()
self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host(
) else PMON_REBOOT_CAUSE_PATH
def __initialize_sfp(self):
sfputil_helper = SfpUtilHelper()
port_config_file_path = device_info.get_path_to_port_config_file()
sfputil_helper.read_porttab_mappings(port_config_file_path, 0)
from sonic_platform.sfp import Sfp
for index in range(0, NUM_SFP):
name_idx = 0 if index+1 == NUM_SFP else index+1
sfp = Sfp(index, sfputil_helper.logical[name_idx])
self._sfp_list.append(sfp)
self.sfp_module_initialized = True
def __initialize_psu(self):
from sonic_platform.psu import Psu
for index in range(0, NUM_PSU):
psu = Psu(index)
self._psu_list.append(psu)
def __initialize_fan(self):
from sonic_platform.fan import Fan
for fant_index in range(0, NUM_FAN_TRAY):
for fan_index in range(0, NUM_FAN):
fan = Fan(fant_index, fan_index)
self._fan_list.append(fan)
for index in range(0, NUM_PSU):
psu = Psu(index)
self._psu_list.append(psu)
def __initialize_thermals(self):
from sonic_platform.thermal import Thermal
for index in range(0, NUM_THERMAL):
thermal = Thermal(index)
self._thermal_list.append(thermal)
# sfp index start from 1
self._sfp_list.append(None)
for index in range(1, NUM_SFP+1):
sfp = Sfp(index)
self._sfp_list.append(sfp)
def __initialize_eeprom(self):
from sonic_platform.eeprom import Tlv
self._eeprom = Tlv()
def __initialize_components(self):
from sonic_platform.component import Component
for index in range(0, NUM_COMPONENT):
component = Component(index)
self._component_list.append(component)
self._reboot_cause_path = HOST_REBOOT_CAUSE_PATH if self.__is_host(
) else PMON_REBOOT_CAUSE_PATH
self._eeprom = Tlv()
def __is_host(self):
return os.system(HOST_CHK_CMD) == 0
@ -85,14 +116,6 @@ class Chassis(ChassisBase):
"""
return self._eeprom.get_mac()
def get_serial(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
@ -116,7 +139,8 @@ class Chassis(ChassisBase):
"""
description = 'None'
reboot_cause = self.REBOOT_CAUSE_HARDWARE_OTHER
hw_reboot_cause = self._component_list[0].get_register_value(RESET_REGISTER)
hw_reboot_cause = self._component_list[0].get_register_value(
RESET_REGISTER)
sw_reboot_cause = self.__read_txt_file(
self._reboot_cause_path) or "Unknown"
@ -145,3 +169,128 @@ class Chassis(ChassisBase):
self._watchdog = Watchdog()
return self._watchdog
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.
"""
# SFP event
if not self.sfp_module_initialized:
self.__initialize_sfp()
sfp_event = SfpEvent(self._sfp_list).get_sfp_event(timeout)
if sfp_event:
return True, {'sfp': sfp_event}
return False, {'sfp': {}}
##############################################################
######################## 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
##############################################################
###################### Device methods ########################
##############################################################
def get_name(self):
"""
Retrieves the name of the device
Returns:
string: The name of the device
"""
return self._api_helper.hwsku
def get_presence(self):
"""
Retrieves the presence of the Chassis
Returns:
bool: True if Chassis 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,62 @@
try:
import time
import select
from .helper import APIHelper
from sonic_py_common.logger import Logger
except ImportError as e:
raise ImportError(repr(e) + " - required module not found")
class SfpEvent:
''' Listen to insert/remove sfp events '''
SFP_NUM_START = 49
DELAY = 0.05
INT_PATH = '/sys/devices/platform/e1031.smc/SFP/modabs_int'
GPIO_SUS7 = '/sys/devices/platform/hlx-ich.0/sci_int_gpio_sus7'
def __init__(self, sfp_list):
self._api_helper = APIHelper()
self._sfp_list = sfp_list
self._logger = Logger()
# clear interrupt
self._api_helper.read_one_line_file(self.INT_PATH)
def get_sfp_event(self, timeout):
epoll = select.epoll()
port_dict = {}
timeout_sec = timeout/1000
try:
# We get notified when there is an SCI interrupt from GPIO SUS7
fd = open(self.GPIO_SUS7, "r")
fd.read()
epoll.register(fd.fileno(), select.EPOLLIN & select.EPOLLET)
events = epoll.poll(timeout=timeout_sec if timeout != 0 else -1)
if events:
# Read the QSFP ABS interrupt & status registers
port_changes = self._api_helper.read_one_line_file(
self.INT_PATH)
changes = int(port_changes, 16)
for sfp in self._sfp_list:
if sfp.port_num < self.SFP_NUM_START:
continue
change = (changes >> sfp.port_num-self.SFP_NUM_START) & 1
if change == 1:
time.sleep(self.DELAY)
port_status = sfp.get_presence()
port_dict[str(sfp.port_num)] = '1' if port_status else '0'
return port_dict
except Exception as e:
self._logger.log_error("Failed to detect SfpEvent - " + repr(e))
return False
finally:
fd.close()
epoll.close()
return False

View File

@ -247,3 +247,11 @@ class Fan(FanBase):
present_str = self.__read_txt_file(fan_direction_file) or '1'
return int(present_str) == 0 if not self.is_psu_fan else True
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() > 0

View File

@ -0,0 +1,133 @@
import os
import struct
import subprocess
from mmap import *
from sonic_py_common import device_info
HOST_CHK_CMD = "docker > /dev/null 2>&1"
EMPTY_STRING = ""
class APIHelper():
def __init__(self):
(self.platform, self.hwsku) = device_info.get_platform_and_hwsku()
def is_host(self):
return os.system(HOST_CHK_CMD) == 0
def pci_get_value(self, resource, offset):
status = True
result = ""
try:
fd = os.open(resource, os.O_RDWR)
mm = mmap(fd, 0)
mm.seek(int(offset))
read_data_stream = mm.read(4)
result = struct.unpack('I', read_data_stream)
except Exception:
status = False
return status, result
def run_command(self, cmd):
status = True
result = ""
try:
p = subprocess.Popen(
cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
raw_data, err = p.communicate()
if err == '':
result = raw_data.strip()
except Exception:
status = False
return status, result
def run_interactive_command(self, cmd):
try:
os.system(cmd)
except Exception:
return False
return True
def read_txt_file(self, file_path):
try:
with open(file_path, 'r') as fd:
data = fd.read()
return data.strip()
except IOError:
pass
return None
def read_one_line_file(self, file_path):
try:
with open(file_path, 'r') as fd:
data = fd.readline()
return data.strip()
except IOError:
pass
return None
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 get_cpld_reg_value(self, getreg_path, register):
cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register)
status, result = self.run_command(cmd)
return result if status else None
def ipmi_raw(self, netfn, cmd):
status = True
result = ""
try:
cmd = "ipmitool raw {} {}".format(str(netfn), str(cmd))
p = subprocess.Popen(
cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
raw_data, err = p.communicate()
if err == '':
result = raw_data.strip()
else:
status = False
except Exception:
status = False
return status, result
def ipmi_fru_id(self, id, key=None):
status = True
result = ""
try:
cmd = "ipmitool fru print {}".format(str(
id)) if not key else "ipmitool fru print {0} | grep '{1}' ".format(str(id), str(key))
p = subprocess.Popen(
cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
raw_data, err = p.communicate()
if err == '':
result = raw_data.strip()
else:
status = False
except Exception:
status = False
return status, result
def ipmi_set_ss_thres(self, id, threshold_key, value):
status = True
result = ""
try:
cmd = "ipmitool sensor thresh '{}' {} {}".format(
str(id), str(threshold_key), str(value))
p = subprocess.Popen(
cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
raw_data, err = p.communicate()
if err == '':
result = raw_data.strip()
else:
status = False
except Exception:
status = False
return status, result

View File

@ -16,7 +16,6 @@ try:
from sonic_platform_base.sonic_sfp.sff8472 import sff8472Dom
from sonic_platform_base.sonic_sfp.sff8472 import sff8472InterfaceId
from sonic_platform_base.sonic_sfp.sff8472 import sffbase
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
@ -88,10 +87,10 @@ class Sfp(SfpBase):
PLATFORM = "x86_64-cel_e1031-r0"
HWSKU = "Celestica-E1031-T48S4"
def __init__(self, sfp_index):
def __init__(self, sfp_index, sfp_name):
# Init index
self.index = sfp_index
self.port_num = self.index
self.port_num = self.index + 1
# Init eeprom path
eeprom_path = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom'
@ -111,6 +110,7 @@ class Sfp(SfpBase):
self.threshold_dict_keys = ['temphighalarm', 'temphighwarning', 'templowalarm', 'templowwarning', 'vcchighalarm', 'vcchighwarning', 'vcclowalarm', 'vcclowwarning', 'rxpowerhighalarm', 'rxpowerhighwarning',
'rxpowerlowalarm', 'rxpowerlowwarning', 'txpowerhighalarm', 'txpowerhighwarning', 'txpowerlowalarm', 'txpowerlowwarning', 'txbiashighalarm', 'txbiashighwarning', 'txbiaslowalarm', 'txbiaslowwarning']
self.name = sfp_name
SfpBase.__init__(self)
def _convert_string_to_num(self, value_str):
@ -145,12 +145,6 @@ class Sfp(SfpBase):
def __is_host(self):
return os.system(self.HOST_CHK_CMD) == 0
def __get_path_to_port_config_file(self):
platform_path = "/".join([self.PLATFORM_ROOT_PATH, self.PLATFORM])
hwsku_path = "/".join([platform_path, self.HWSKU]
) if self.__is_host() else self.PMON_HWSKU_PATH
return "/".join([hwsku_path, "port_config.ini"])
def __read_eeprom_specific_bytes(self, offset, num_bytes):
sysfsfile_eeprom = None
eeprom_raw = []
@ -670,11 +664,7 @@ class Sfp(SfpBase):
Returns:
string: The name of the device
"""
sfputil_helper = SfpUtilHelper()
sfputil_helper.read_porttab_mappings(
self.__get_path_to_port_config_file())
name = sfputil_helper.logical[self.index] or "Unknown"
return name
return self.name
def get_presence(self):
"""

View File

@ -4,6 +4,7 @@ haliburton/script/fancontrol.sh etc/init.d
haliburton/script/fancontrol.service lib/systemd/system
services/fancontrol/fancontrol usr/local/bin
haliburton/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-cel_e1031-r0
haliburton/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-cel_e1031-r0
services/platform_api/platform_api_mgnt.sh usr/local/bin
haliburton/script/popmsg.sh usr/local/bin
haliburton/script/udev_prefix.sh usr/local/bin

View File

@ -13,13 +13,15 @@ MODULE_DIRS:= dx010 haliburton silverstone seastone2
override_dh_auto_build:
(for mod in $(MODULE_DIRS); do \
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
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 \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
continue; \
fi; \
cd $(MOD_SRC_DIR)/$${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
done)
override_dh_auto_install:
@ -37,4 +39,3 @@ override_dh_clean:
(for mod in $(MODULE_DIRS); do \
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \
done)

View File

@ -9,6 +9,7 @@ install() {
# Install sonic-platform package
if [ -e $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl ]; then
pip install $DEVICE/$PLATFORM/sonic_platform-1.0-py2-none-any.whl
pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl
fi
}