[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:
parent
13467e5853
commit
6ae2cb5607
@ -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
|
||||
|
62
device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py
Normal file
62
device/celestica/x86_64-cel_e1031-r0/sonic_platform/event.py
Normal 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
|
@ -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
|
133
device/celestica/x86_64-cel_e1031-r0/sonic_platform/helper.py
Normal file
133
device/celestica/x86_64-cel_e1031-r0/sonic_platform/helper.py
Normal 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
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user