diff --git a/platform/mellanox/docker-syncd-mlnx-rpc.mk b/platform/mellanox/docker-syncd-mlnx-rpc.mk index f27242ce17..d6011884c4 100644 --- a/platform/mellanox/docker-syncd-mlnx-rpc.mk +++ b/platform/mellanox/docker-syncd-mlnx-rpc.mk @@ -2,7 +2,7 @@ DOCKER_SYNCD_MLNX_RPC = docker-syncd-mlnx-rpc.gz $(DOCKER_SYNCD_MLNX_RPC)_PATH = $(PLATFORM_PATH)/docker-syncd-mlnx-rpc -$(DOCKER_SYNCD_MLNX_RPC)_DEPENDS += $(SYNCD_RPC) $(LIBTHRIFT) +$(DOCKER_SYNCD_MLNX_RPC)_DEPENDS += $(SYNCD_RPC) $(LIBTHRIFT) $(MLNX_SFPD) $(DOCKER_SYNCD_MLNX_RPC)_LOAD_DOCKERS += $(DOCKER_SYNCD_MLNX) SONIC_DOCKER_IMAGES += $(DOCKER_SYNCD_MLNX_RPC) ifeq ($(ENABLE_SYNCD_RPC),y) diff --git a/platform/mellanox/docker-syncd-mlnx.mk b/platform/mellanox/docker-syncd-mlnx.mk index 2da5961fc0..6ac895d2eb 100644 --- a/platform/mellanox/docker-syncd-mlnx.mk +++ b/platform/mellanox/docker-syncd-mlnx.mk @@ -2,7 +2,7 @@ DOCKER_SYNCD_MLNX = docker-syncd-mlnx.gz $(DOCKER_SYNCD_MLNX)_PATH = $(PLATFORM_PATH)/docker-syncd-mlnx -$(DOCKER_SYNCD_MLNX)_DEPENDS += $(SYNCD) $(PYTHON_SDK_API) +$(DOCKER_SYNCD_MLNX)_DEPENDS += $(SYNCD) $(PYTHON_SDK_API) $(MLNX_SFPD) $(DOCKER_SYNCD_MLNX)_LOAD_DOCKERS += $(DOCKER_CONFIG_ENGINE) SONIC_DOCKER_IMAGES += $(DOCKER_SYNCD_MLNX) ifneq ($(ENABLE_SYNCD_RPC),y) diff --git a/platform/mellanox/docker-syncd-mlnx/start.sh b/platform/mellanox/docker-syncd-mlnx/start.sh index 6233160504..3adbd04d53 100755 --- a/platform/mellanox/docker-syncd-mlnx/start.sh +++ b/platform/mellanox/docker-syncd-mlnx/start.sh @@ -5,3 +5,5 @@ rm -f /var/run/rsyslogd.pid supervisorctl start rsyslogd supervisorctl start syncd + +supervisorctl start mlnx-sfpd diff --git a/platform/mellanox/docker-syncd-mlnx/supervisord.conf b/platform/mellanox/docker-syncd-mlnx/supervisord.conf index 1af5d70a1d..8860bd6c02 100644 --- a/platform/mellanox/docker-syncd-mlnx/supervisord.conf +++ b/platform/mellanox/docker-syncd-mlnx/supervisord.conf @@ -26,3 +26,11 @@ autostart=false autorestart=false stdout_logfile=syslog stderr_logfile=syslog + +[program:mlnx-sfpd] +command=/usr/bin/mlnx-sfpd +priority=4 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog diff --git a/platform/mellanox/mlnx-sfpd.mk b/platform/mellanox/mlnx-sfpd.mk new file mode 100644 index 0000000000..416dd7d0f2 --- /dev/null +++ b/platform/mellanox/mlnx-sfpd.mk @@ -0,0 +1,5 @@ +# mlnx-sfpd (SONiC MLNX platform sfp event monitoring daemon) Debian package + +MLNX_SFPD = python-mlnx-sfpd_1.0-1_all.deb +$(MLNX_SFPD)_SRC_PATH = $(PLATFORM_PATH)/mlnx-sfpd +SONIC_PYTHON_STDEB_DEBS += $(MLNX_SFPD) diff --git a/platform/mellanox/mlnx-sfpd/scripts/mlnx-sfpd b/platform/mellanox/mlnx-sfpd/scripts/mlnx-sfpd new file mode 100644 index 0000000000..e91dbb7a8a --- /dev/null +++ b/platform/mellanox/mlnx-sfpd/scripts/mlnx-sfpd @@ -0,0 +1,199 @@ +#!/usr/bin/env python +''' +This code is for a mlnx platform specific daemon, mlnx-sfpd. +Which listen to the SDK for the SFP change event and post the event to DB. +''' + +from __future__ import print_function +import sys, errno +import os +import time +import syslog +import signal +import json +from python_sdk_api.sx_api import * +from swsssdk import SonicV2Connector + +VERSION = '1.0' + +SYSLOG_IDENTIFIER = "mlnx-sfpd" + +REDIS_HOSTIP = "127.0.0.1" + +SDK_SFP_STATE_IN = 0x1 +SDK_SFP_STATE_OUT = 0x2 +STATUS_PLUGIN = '1' +STATUS_PLUGOUT = '0' +STATUS_UNKNOWN = '2' + +sfp_value_status_dict = {SDK_SFP_STATE_IN:STATUS_PLUGIN, SDK_SFP_STATE_OUT:STATUS_PLUGOUT} + +#========================== Syslog wrappers ========================== + +def log_info(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_INFO, msg) + syslog.closelog() + + if also_print_to_console: + print(msg) + +def log_warning(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + + if also_print_to_console: + print(msg) + +def log_error(msg, also_print_to_console=False): + syslog.openlog(SYSLOG_IDENTIFIER) + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + if also_print_to_console: + print(msg) + +#========================== Signal Handling ========================== + +def signal_handler(sig, frame): + if sig == signal.SIGHUP: + log_info("Caught SIGHUP - ignoring...") + return + elif sig == signal.SIGINT: + log_info("Caught SIGINT - exiting...") + sys.exit(128 + sig) + elif sig == signal.SIGTERM: + log_info("Caught SIGTERM - exiting...") + sys.exit(128 + sig) + else: + log_warning("Caught unhandled signal '" + sig + "'") + + +def sx_recv(fd_p, handle): + # recv parameters + pkt_size = 2000 + pkt_size_p = new_uint32_t_p() + uint32_t_p_assign(pkt_size_p, pkt_size) + pkt = new_uint8_t_arr(pkt_size) + recv_info_p = new_sx_receive_info_t_p() + pmpe_t = sx_event_pmpe_t() + logical_port_list = new_sx_port_log_id_t_arr(4) + port_attributes_list = new_sx_port_attributes_t_arr(64) + port_cnt_p = new_uint32_t_p() + uint32_t_p_assign(port_cnt_p,64) + label_port_list = [] + + rc = sx_lib_host_ifc_recv(fd_p, pkt, pkt_size_p, recv_info_p) + if rc != 0: + log_error("event receive exit with error, rc %d" % rc) + exit(rc) + + pmpe_t = recv_info_p.event_info.pmpe + port_list_size = pmpe_t.list_size + logical_port_list = pmpe_t.log_port_list + module_state = pmpe_t.module_state + + for i in range(0, port_list_size): + logical_port = sx_port_log_id_t_arr_getitem(logical_port_list, i) + rc = sx_api_port_device_get(handle, 1 , 0, port_attributes_list, port_cnt_p) + port_cnt = uint32_t_p_value(port_cnt_p) + + for i in range(0,port_cnt): + port_attributes = sx_port_attributes_t_arr_getitem(port_attributes_list,i) + if port_attributes.log_port == logical_port: + lable_port = port_attributes.port_mapping.module_port + break + label_port_list.append(lable_port) + + return label_port_list, module_state + +def send_sfp_notification(db, interface, state): + sfp_notify = [interface,state] + msg = json.dumps(sfp_notify,separators=(',',':')) + db.publish('STATE_DB','TRANSCEIVER_NOTIFY', msg) + return + +# main start +def main(): + # Register our signal handlers + signal.signal(signal.SIGHUP, signal_handler) + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + #open SDK handler + log_info("starting mlnx-sfpd...") + rc, handle = sx_api_open(None) + retry_time = 1 + while rc != SX_STATUS_SUCCESS: + time.sleep(2**retry_time) + retry_time += 1 + rc, handle = sx_api_open(None) + if retry_time > 20: + log_error("Failed to open api handle.\nPlease check that SDK is running.") + sys.exit(errno.EACCES) + + #open recv fd + rx_fd_p = new_sx_fd_t_p() + rc = sx_api_host_ifc_open(handle, rx_fd_p) + if rc != 0: + log_error("sx_api_host_ifc_open exit with error, rc %d" % rc) + exit(rc) + + # set up general host ifc parameters + swid = 0 + cmd = SX_ACCESS_CMD_REGISTER + uc_p = new_sx_user_channel_t_p() + uc_p.type = SX_USER_CHANNEL_TYPE_FD + uc_p.channel.fd = rx_fd_p + trap_id = SX_TRAP_ID_PMPE + + rc = sx_api_host_ifc_trap_id_register_set(handle, cmd, swid, trap_id, uc_p) + if rc != 0: + log_error("sx_api_host_ifc_trap_id_register_set exit with error, rc %d" % rc) + exit(rc) + + #connect to state db for notification sending + state_db = SonicV2Connector(host=REDIS_HOSTIP) + state_db.connect(state_db.STATE_DB) + + #main loop for sfp event listening + log_info("mlnx-sfpd started") + while True: + state = STATUS_UNKNOWN + port_list, module_state = sx_recv(rx_fd_p, handle) + if module_state in sfp_value_status_dict: state = sfp_value_status_dict[module_state] + + if state != STATUS_UNKNOWN: + for port in port_list: + log_info("SFP on port %d state %s" % (port, state)) + send_sfp_notification(state_db, str(port), state) + + log_info("sfp change event handling done") + + ''' + # TODO: clean open handlers before exit, need find out which errors can be raised by SDK in this case. + # unregister trap id + cmd = SX_ACCESS_CMD_DEREGISTER + rc = sx_api_host_ifc_trap_id_register_set(handle, cmd, swid, trap_id, uc_p) + if rc != 0: + log_error("sx_api_host_ifc_trap_id_register_set exit with error, rc %d" % rc) + exit(rc) + + # close read fp + rc = sx_api_host_ifc_close(handle, rx_fd_p) + if rc != 0: + log_error("sx_api_host_ifc_close exit with error, rc %d" % rc) + exit(rc) + + # close sdk handler + rc = sx_api_close(handle) + if rc != 0: + log_error("exit with error, rc %d" % rc) + exit(rc) + + log_info("mlnx-sfpd exited") + ''' + +if __name__ == '__main__': + main() diff --git a/platform/mellanox/mlnx-sfpd/setup.py b/platform/mellanox/mlnx-sfpd/setup.py new file mode 100644 index 0000000000..ea9b895cb1 --- /dev/null +++ b/platform/mellanox/mlnx-sfpd/setup.py @@ -0,0 +1,28 @@ +from setuptools import setup + +setup( + name='mlnx-sfpd', + version='1.0', + description='SFP event mmonitoring daemon for SONiC on mellanox platform', + license='Apache 2.0', + author='SONiC Community', + url='https://github.com/Azure/sonic-buildimage/', + maintainer='Kebo Liu', + maintainer_email='kebol@mellanox.com', + scripts=[ + 'scripts/mlnx-sfpd', + ], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: No Input/Output (Daemon)', + '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 :: System :: Hardware', + ], + keywords='sonic SONiC SFP sfp MELLANOX mellanox daemon SFPD sfpd', +) diff --git a/platform/mellanox/rules.mk b/platform/mellanox/rules.mk index 1651f4a668..ff5c96fe00 100644 --- a/platform/mellanox/rules.mk +++ b/platform/mellanox/rules.mk @@ -10,6 +10,7 @@ include $(PLATFORM_PATH)/one-image.mk include $(PLATFORM_PATH)/libsaithrift-dev.mk include $(PLATFORM_PATH)/python-saithrift.mk include $(PLATFORM_PATH)/docker-ptf-mlnx.mk +include $(PLATFORM_PATH)/mlnx-sfpd.mk SONIC_ALL += $(SONIC_ONE_IMAGE) \ $(DOCKER_FPM)