From 6c2270e7c438aaab0d1c7794945a5b5d3d99fece Mon Sep 17 00:00:00 2001 From: "Kevin(Shengkai) Wang" Date: Thu, 7 Mar 2019 02:50:31 +0800 Subject: [PATCH 1/3] [sonic-platform-daemons] Refactor daemons based on DaemonBase class (#22) Signed-off-by: Kevin Wang --- sonic-ledd/scripts/ledd | 252 +++++++------------------ sonic-psud/scripts/psud | 197 +++++--------------- sonic-xcvrd/scripts/xcvrd | 375 +++++++++++++------------------------- 3 files changed, 242 insertions(+), 582 deletions(-) diff --git a/sonic-ledd/scripts/ledd b/sonic-ledd/scripts/ledd index 75a1d7e69..66cde42ba 100644 --- a/sonic-ledd/scripts/ledd +++ b/sonic-ledd/scripts/ledd @@ -15,6 +15,7 @@ try: import sys import syslog from swsscommon import swsscommon + from sonic_daemon_base.daemon_base import DaemonBase except ImportError, e: raise ImportError (str(e) + " - required module not found") @@ -34,200 +35,87 @@ Options: LED_MODULE_NAME = "led_control" LED_CLASS_NAME = "LedControl" +SELECT_TIMEOUT = 1000 -SONIC_CFGGEN = "/usr/local/bin/sonic-cfggen" -MINIGRAPH_FILE = "/etc/sonic/minigraph.xml" -HWSKU_KEY = "minigraph_hwsku" -PLATFORM_KEY = "platform" +class DaemonLedd(DaemonBase): + def __init__(self): + DaemonBase.__init__(self) + + def __exit__(self): + DaemonBase.__exit__(self) + + def run(self): + # Parse options if provided + if (len(sys.argv) > 1): + try: + (options, remainder) = getopt.getopt(sys.argv[1:], + 'hv', + ['help', 'version']) + except getopt.GetoptError, e: + print e + print USAGE_HELP + sys.exit(2) -# platform directory in base image -PLATFORM_ROOT = "/usr/share/sonic/device" + for opt, arg in options: + if opt == '--help' or opt == '-h': + print USAGE_HELP + sys.exit(0) + elif opt == '--version' or opt == '-v': + print 'ledd version ' + VERSION + sys.exit(0) -# platform root directory inside docker -PLATFORM_ROOT_DOCKER = "/usr/share/sonic/platform" + # Load platform-specific LedControl module + led_control = self.load_platform_util(LED_MODULE_NAME, LED_CLASS_NAME) + if not led_control: + self.log_error("failed to load ledutil") + sys.exit(1) -REDIS_HOSTNAME = "localhost" -REDIS_PORT = 6379 -REDIS_TIMEOUT_USECS = 0 + # Open a handle to the Application database + appl_db = self.db_connect(swsscommon.APPL_DB) -SELECT_TIMEOUT = 1000 + # Subscribe to PORT table notifications in the Application DB + sel = swsscommon.Select() + sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) + sel.addSelectable(sst) -#========================== Syslog wrappers ========================== - -def log_info(msg): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_INFO, msg) - syslog.closelog() - -def log_warning(msg): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_WARNING, msg) - syslog.closelog() - -def log_error(msg): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_ERR, msg) - syslog.closelog() - -#========================== 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 + "'") - - -#============ Functions to load platform-specific classes ============ - -# Returns platform and HW SKU -def get_platform_and_hwsku(): - try: - proc = subprocess.Popen([SONIC_CFGGEN, '-v', PLATFORM_KEY], - stdout=subprocess.PIPE, - shell=False, - stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] - proc.wait() - platform = stdout.rstrip('\n') - - proc = subprocess.Popen([SONIC_CFGGEN, '-m', MINIGRAPH_FILE, '-v', HWSKU_KEY], - stdout=subprocess.PIPE, - shell=False, - stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] - proc.wait() - hwsku = stdout.rstrip('\n') - except OSError, e: - log_error("Cannot detect platform") - raise OSError("Cannot detect platform") - - return (platform, hwsku) - - -# Loads platform-specific LED control module from source -def load_platform_led_control_module(): - # Get platform and hwsku - (platform, hwsku) = get_platform_and_hwsku() - - # Load platform module from source - platform_path = '/'.join([PLATFORM_ROOT, platform]) - hwsku_path = '/'.join([platform_path, hwsku]) - - module_file_base = '/'.join([platform_path, 'plugins', LED_MODULE_NAME + '.py']) - - module_file_docker = '/'.join([PLATFORM_ROOT_DOCKER, 'plugins', LED_MODULE_NAME + '.py']) - - # If we can't locate a platform-specific module, exit gracefully, assuming this - # platform utilizes a hardware-based LED control solution - if os.path.isfile(module_file_base): - module_file = module_file_base - elif os.path.isfile(module_file_docker): - module_file = module_file_docker - else: - log_info("Failed to locate platform-specific %s module." % LED_MODULE_NAME) - return None - - try: - module = imp.load_source(LED_MODULE_NAME, module_file) - except IOError, e: - log_error("Failed to load platform module '%s': %s" % (LED_MODULE_NAME, str(e))) - return None - - log_info("Loaded module '%s'." % LED_MODULE_NAME) - - try: - led_control_class = getattr(module, LED_CLASS_NAME) - led_control = led_control_class() - except AttributeError, e: - log_error("Failed to instantiate '%s' class: %s" % (LED_CLASS_NAME, str(e))) - return None - - log_info("Instantiated class '%s.%s'." % (LED_MODULE_NAME, LED_CLASS_NAME)) - - return led_control - -#=============================== Main ================================ + # Listen indefinitely for changes to the PORT table in the Application DB + while True: + # Use timeout to prevent ignoring the signals we want to handle + # in signal_handler() (e.g. SIGTERM for graceful shutdown) + (state, c) = sel.select(SELECT_TIMEOUT) -def main(): - log_info("Starting up...") + if state == swsscommon.Select.TIMEOUT: + # Do not flood log when select times out + continue + if state != swsscommon.Select.OBJECT: + self.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + continue + + (key, op, fvp) = sst.pop() + + # TODO: Once these flag entries have been removed from the DB, + # we can remove this check + if key in ["PortConfigDone", "PortInitDone"]: + continue + + fvp_dict = dict(fvp) + if op == "SET" and "oper_status" in fvp_dict: + led_control.port_link_state_change(key, fvp_dict["oper_status"]) + + return 1 + +def main(): if not os.geteuid() == 0: - log_error("Must be root to run this daemon") print "Error: Must be root to run this daemon" sys.exit(1) - # Parse options if provided - if (len(sys.argv) > 1): - try: - (options, remainder) = getopt.getopt(sys.argv[1:], - 'hv', - ['help', 'version']) - except getopt.GetoptError, e: - print e - print USAGE_HELP - sys.exit(2) - - for opt, arg in options: - if opt == '--help' or opt == '-h': - print USAGE_HELP - sys.exit(0) - elif opt == '--version' or opt == '-v': - print 'ledd version ' + VERSION - sys.exit(0) - - # Register our signal handlers - signal.signal(signal.SIGHUP, signal_handler) - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - # Load platform-specific LedControl module - led_control = load_platform_led_control_module() - if led_control is None: - sys.exit(0) - - # Open a handle to the Application database - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, - REDIS_HOSTNAME, - REDIS_PORT, - REDIS_TIMEOUT_USECS) - - # Subscribe to PORT table notifications in the Application DB - sel = swsscommon.Select() - sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) - sel.addSelectable(sst) - - # Listen indefinitely for changes to the PORT table in the Application DB - while True: - # Use timeout to prevent ignoring the signals we want to handle - # in signal_handler() (e.g. SIGTERM for graceful shutdown) - (state, c) = sel.select(SELECT_TIMEOUT) - - if state == swsscommon.Select.TIMEOUT: - # Do not flood log when select times out - continue - if state != swsscommon.Select.OBJECT: - log_warning("sel.select() did not return swsscommon.Select.OBJECT") - continue - - (key, op, fvp) = sst.pop() - - # TODO: Once these flag entries have been removed from the DB, - # we can remove this check - if key in ["PortConfigDone", "PortInitDone"]: - continue - - fvp_dict = dict(fvp) - - if op == "SET" and "oper_status" in fvp_dict: - led_control.port_link_state_change(key, fvp_dict["oper_status"]) + daemon_ledd = DaemonLedd() + if not daemon_ledd: + print "Failed to instantiate LED daemon" + sys.exit(1) + + daemon_ledd.run() if __name__ == '__main__': main() diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud index 4c50a7dc2..922f608c2 100644 --- a/sonic-psud/scripts/psud +++ b/sonic-psud/scripts/psud @@ -1,7 +1,7 @@ #!/usr/bin/env python2 """ - Psud + psud PSU information update daemon for SONiC This daemon will loop to collect PSU related information and then write the information to state DB. Currently it is implemented based on old plugins rather than new platform APIs. So the PSU information just @@ -10,181 +10,68 @@ """ try: - import getopt - import os - import imp - import signal - import subprocess import sys - import syslog import time from swsscommon import swsscommon + from sonic_daemon_base.daemon_base import DaemonBase except ImportError, e: raise ImportError (str(e) + " - required module not found") #============================= Constants ============================= -VERSION = '1.0' - -SYSLOG_IDENTIFIER = os.path.basename(__file__) PLATFORM_SPECIFIC_MODULE_NAME = "psuutil" PLATFORM_SPECIFIC_CLASS_NAME = "PsuUtil" -# Platform root directory inside docker -PLATFORM_ROOT_DOCKER = "/usr/share/sonic/platform" -SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' -HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku' -PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform' - -# Global platform-specific psuutil class instance -platform_psuutil = None - -REDIS_HOSTNAME = "localhost" -REDIS_PORT = 6379 -REDIS_TIMEOUT_MSECS = 0 - PSU_INFO_UPDATE_PERIOD_SECS = 3 -#========================== 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 + "'") - return - -#============ Functions to load platform-specific classes ============ - -# Returns platform and HW SKU -def get_platform_and_hwsku(): - try: - proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], - stdout=subprocess.PIPE, - shell=False, - stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] - proc.wait() - platform = stdout.rstrip('\n') - - proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], - stdout=subprocess.PIPE, - shell=False, - stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] - proc.wait() - hwsku = stdout.rstrip('\n') - except OSError, e: - raise OSError("Cannot detect platform") - - return (platform, hwsku) - -# Loads platform specific psuutil module from source -def load_platform_psuutil(): - global platform_psuutil - - # Get platform and hwsku - (platform, hwsku) = get_platform_and_hwsku() - - # Load platform module from source - platform_path = PLATFORM_ROOT_DOCKER - hwsku_path = "/".join([platform_path, hwsku]) - - try: - module_file = "/".join([platform_path, "plugins", PLATFORM_SPECIFIC_MODULE_NAME + ".py"]) - module = imp.load_source(PLATFORM_SPECIFIC_MODULE_NAME, module_file) - except IOError, e: - log_error("Failed to load platform module '%s': %s" % (PLATFORM_SPECIFIC_MODULE_NAME, str(e)), True) - return -1 - - try: - platform_psuutil_class = getattr(module, PLATFORM_SPECIFIC_CLASS_NAME) - platform_psuutil = platform_psuutil_class() - except AttributeError, e: - log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True) - return -2 - - return 0 - -def psu_db_update(psu_tbl, num_psus): +class DaemonPsud(DaemonBase): + def __init__(self): + DaemonBase.__init__(self) + + def __exit__(self): + DaemonBase.__exit__(self) + + def run(self): + # Load platform-specific psuutil class + platform_psuutil = self.load_platform_util(PLATFORM_SPECIFIC_MODULE_NAME, PLATFORM_SPECIFIC_CLASS_NAME) + if not platform_psuutil: + self.log_error("failed to load psuutil") + sys.exit(1) + + state_db = self.db_connect(swsscommon.STATE_DB) + psu_tbl = swsscommon.Table(state_db, "PSU_INFO") + chassis_tbl = swsscommon.Table(state_db, "CHASSIS_INFO") + num_psus = platform_psuutil.get_num_psus() + fvs = swsscommon.FieldValuePairs([('num_psus', str(num_psus))]) + chassis_tbl.set('chassis 1', fvs) + + # Start main loop to listen to the PSU change event. + self.log_info("Start main loop") + while True: + psu_db_update(platform_psuutil, psu_tbl, num_psus) + time.sleep(PSU_INFO_UPDATE_PERIOD_SECS) + + # Clean all the information from DB and then exit + for psu_index in range(1, num_psus + 1): + psu_tbl._del("PSU {}".format(psu_index)) + chassis_tbl._del('chassis 1') + return 1 + +def psu_db_update(psuutil, psu_tbl, num_psus): for psu_index in range(1, num_psus + 1): fvs = swsscommon.FieldValuePairs([('presence', - 'true' if platform_psuutil.get_psu_presence(psu_index) else 'false'), + 'true' if psuutil.get_psu_presence(psu_index) else 'false'), ('status', - 'true' if platform_psuutil.get_psu_status(psu_index) else 'false')]) + 'true' if psuutil.get_psu_status(psu_index) else 'false')]) psu_tbl.set("PSU {}".format(psu_index), fvs) -#=============================== Main ================================ - def main(): - log_info("Starting up...") - - # Register our signal handlers - signal.signal(signal.SIGHUP, signal_handler) - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - # Load platform-specific psuutil class - err = load_platform_psuutil() - if err != 0: - log_error("failed to load psuutil") + daemon_psud = DaemonPsud() + if not daemon_psud: + print "Failed to load psu daemon utilities" sys.exit(1) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, - REDIS_HOSTNAME, - REDIS_PORT, - REDIS_TIMEOUT_MSECS) - psu_tbl = swsscommon.Table(state_db, "PSU_INFO") - chassis_tbl = swsscommon.Table(state_db, "CHASSIS_INFO") - num_psus = platform_psuutil.get_num_psus() - fvs = swsscommon.FieldValuePairs([('num_psus', str(num_psus))]) - chassis_tbl.set('chassis 1', fvs) - - # Start main loop to listen to the PSU change event. - log_info("Start main loop") - while True: - psu_db_update(psu_tbl, num_psus) - time.sleep(PSU_INFO_UPDATE_PERIOD_SECS) - - # Clean all the information from DB and then exit - for psu_index in range(1, num_psus + 1): - psu_tbl._del("PSU {}".format(psu_index)) - chassis_tbl._del('chassis 1') - log_error("Error: return error from psu daemon, exiting...") - return 1 + daemon_psud.run() if __name__ == '__main__': main() diff --git a/sonic-xcvrd/scripts/xcvrd b/sonic-xcvrd/scripts/xcvrd index 777719011..7d71d01a3 100644 --- a/sonic-xcvrd/scripts/xcvrd +++ b/sonic-xcvrd/scripts/xcvrd @@ -1,50 +1,30 @@ #!/usr/bin/env python2 """ - Xcvrd + xcvrd Transceiver information update daemon for SONiC """ try: - import getopt import os - import imp - import signal - import subprocess import sys - import syslog import time import threading from swsscommon import swsscommon + from sonic_daemon_base.daemon_base import DaemonBase except ImportError, e: raise ImportError (str(e) + " - required module not found") #============================= Constants ============================= -VERSION = '1.0' - -SYSLOG_IDENTIFIER = os.path.basename(__file__) - -PLATFORM_ROOT_PATH = '/usr/share/sonic/device' -SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' -HWSKU_KEY = 'DEVICE_METADATA.localhost.hwsku' -PLATFORM_KEY = 'DEVICE_METADATA.localhost.platform' - PLATFORM_SPECIFIC_MODULE_NAME = "sfputil" PLATFORM_SPECIFIC_CLASS_NAME = "SfpUtil" # Global platform-specific sfputil class instance platform_sfputil = None +# Global xcvr dameon util class instance +daemon_xcvrd = None -# platform directory in base image -PLATFORM_ROOT = "/usr/share/sonic/device" - -# platform root directory inside docker -PLATFORM_ROOT_DOCKER = "/usr/share/sonic/platform" - -REDIS_HOSTNAME = "localhost" -REDIS_PORT = 6379 -REDIS_TIMEOUT_MSECS = 0 SELECT_TIMEOUT_MSECS = 1000 DOM_INFO_UPDATE_PERIOD_SECS = 60 @@ -61,125 +41,122 @@ VOLT_UNIT = 'Volts' POWER_UNIT = 'dBm' BIAS_UNIT = 'mA' -#========================== 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() +class DaemonXcvrd(DaemonBase): + def __init__(self): + DaemonBase.__init__(self) + + def __exit__(self): + DaemonBase.__exit__(self) + + # Returns path to port config file + def get_path_to_port_config_file(self): + (platform_path, hwsku_path) = self.get_path_to_platform_and_hwsku() + # First check for the presence of the new 'port_config.ini' file + port_config_file_path = "/".join([hwsku_path, "port_config.ini"]) + if not os.path.isfile(port_config_file_path): + # port_config.ini doesn't exist. Try loading the legacy 'portmap.ini' file + port_config_file_path = "/".join([hwsku_path, "portmap.ini"]) + + return port_config_file_path + + # find out the underneath physical port list by logical name + def logical_port_name_to_physical_port_list(self, port_name): + if port_name.startswith("Ethernet"): + if platform_sfputil.is_logical_port(port_name): + return platform_sfputil.get_logical_to_physical(port_name) + else: + self.log_error("Invalid port '%s'" % port_name) + return None + else: + return [int(port_name)] - if also_print_to_console: - print msg + def run(self): + global platform_sfputil -def log_error(msg, also_print_to_console=False): - syslog.openlog(SYSLOG_IDENTIFIER) - syslog.syslog(syslog.LOG_ERR, msg) - syslog.closelog() + # Load platform-specific sfputil class + platform_sfputil = self.load_platform_util(PLATFORM_SPECIFIC_MODULE_NAME, PLATFORM_SPECIFIC_CLASS_NAME) + if not platform_sfputil: + self.log_error("failed to load sfputil") + sys.exit(1) - if also_print_to_console: - print msg + # Load port info + try: + port_config_file_path = self.get_path_to_port_config_file() + platform_sfputil.read_porttab_mappings(port_config_file_path) + # platform_sfputil.read_port_mappings() + except Exception, e: + self.log_error("failed to reading port info (%s)" % str(e)) + sys.exit(2) + + # Connect to STATE_DB and create transceiver info/dom info table + state_db = self.db_connect(swsscommon.STATE_DB) + int_tbl = swsscommon.Table(state_db, "TRANSCEIVER_INFO") + dom_tbl = swsscommon.Table(state_db, "TRANSCEIVER_DOM_SENSOR") + + # Connect to APPL_DB abd subscribe to PORT table notifications + appl_db = self.db_connect(swsscommon.APPL_DB) + + sel = swsscommon.Select() + sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) + sel.addSelectable(sst) + + # Make sure this daemon started after all port configured. + while True: + (state, c) = sel.select(SELECT_TIMEOUT_MSECS) + if state == swsscommon.Select.TIMEOUT: + continue + if state != swsscommon.Select.OBJECT: + self.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + continue + + (key, op, fvp) = sst.pop() + if key in ["PortConfigDone", "PortInitDone"]: + break + + # Post all the current interface SFP info to STATE_DB + logical_port_list = platform_sfputil.logical + for logical_port_name in logical_port_list: + post_port_sfp_info_to_db(logical_port_name, int_tbl) + post_port_dom_info_to_db(logical_port_name, dom_tbl) + + # Start the dom sensor info update timer thread + dom_info_update = dom_info_update_task(dom_tbl) + dom_info_update.task_run() + + # Start main loop to listen to the SFP change event. + self.log_info("Start main loop") + while True: + status, port_dict = platform_sfputil.get_transceiver_change_event() + if status: + for key, value in port_dict.iteritems(): + logical_port_list = platform_sfputil.get_physical_to_logical(int(key)) + for logical_port in logical_port_list: + if value == SFP_STATUS_INSERTED: + rc = post_port_sfp_info_to_db(logical_port, int_tbl) + # If we didn't get the sfp info, assuming the eeprom is not ready, give a try again. + if rc == SFP_EEPROM_NOT_READY: + time.sleep(TIME_FOR_SFP_READY_SECS) + post_port_sfp_info_to_db(logical_port, int_tbl) + post_port_dom_info_to_db(logical_port, dom_tbl) + + elif value == SFP_STATUS_REMOVED: + del_port_sfp_dom_info_to_db(logical_port, int_tbl, dom_tbl) + else: + # TODO, SFP return error code, need handle accordingly. + continue + else: + # If get_transceiver_change_event() return error, will clean up the DB and then exit + # TODO: next step need to define more error types to handle accordingly. + break -#========================== Signal Handling ========================== + # Stop the dom info update timer + dom_info_update.task_stop() -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 + "'") - return - - -#============ Functions to load platform-specific classes ============ - -# Returns platform and HW SKU -def get_platform_and_hwsku(): - proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-H', '-v', PLATFORM_KEY], - stdout=subprocess.PIPE, - shell=False, - stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] - proc.wait() - platform = stdout.rstrip('\n') - - proc = subprocess.Popen([SONIC_CFGGEN_PATH, '-d', '-v', HWSKU_KEY], - stdout=subprocess.PIPE, - shell=False, - stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] - proc.wait() - hwsku = stdout.rstrip('\n') - - return (platform, hwsku) - -# Loads platform specific sfputil module from source -def load_platform_sfputil(): - global platform_sfputil - - # Get platform and hwsku - (platform, hwsku) = get_platform_and_hwsku() - - # Load platform module from source - platform_path = PLATFORM_ROOT_DOCKER - hwsku_path = "/".join([platform_path, hwsku]) - - try: - module_file = "/".join([platform_path, "plugins", PLATFORM_SPECIFIC_MODULE_NAME + ".py"]) - module = imp.load_source(PLATFORM_SPECIFIC_MODULE_NAME, module_file) - except IOError, e: - log_error("Failed to load platform module '%s': %s" % (PLATFORM_SPECIFIC_MODULE_NAME, str(e)), True) - return -1 - - try: - platform_sfputil_class = getattr(module, PLATFORM_SPECIFIC_CLASS_NAME) - platform_sfputil = platform_sfputil_class() - except AttributeError, e: - log_error("Failed to instantiate '%s' class: %s" % (PLATFORM_SPECIFIC_CLASS_NAME, str(e)), True) - return -2 - - return 0 - -# Returns path to port config file -def get_path_to_port_config_file(): - # Get platform and hwsku - (platform, hwsku) = get_platform_and_hwsku() - - # Load platform module from source - platform_path = PLATFORM_ROOT_DOCKER - hwsku_path = "/".join([platform_path, hwsku]) - - # First check for the presence of the new 'port_config.ini' file - port_config_file_path = "/".join([hwsku_path, "port_config.ini"]) - if not os.path.isfile(port_config_file_path): - # port_config.ini doesn't exist. Try loading the legacy 'portmap.ini' file - port_config_file_path = "/".join([hwsku_path, "portmap.ini"]) - - return port_config_file_path - -# find out the underneath physical port list by logical name -def logical_port_name_to_physical_port_list(port_name): - if port_name.startswith("Ethernet"): - if platform_sfputil.is_logical_port(port_name): - return platform_sfputil.get_logical_to_physical(port_name) - else: - print "Error: Invalid port '%s'" % port_name - return None - else: - return [int(port_name)] + # Clean all the information from DB and then exit + logical_port_list = platform_sfputil.logical + for logical_port_name in logical_port_list: + del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl) + return 1 # Returns, # port_num if physical @@ -223,9 +200,9 @@ def post_port_sfp_info_to_db(logical_port_name, table): ganged_port = False ganged_member_num = 1 - physical_port_list = logical_port_name_to_physical_port_list(logical_port_name) + physical_port_list = daemon_xcvrd.logical_port_name_to_physical_port_list(logical_port_name) if physical_port_list is None: - log_error("Error: No physical ports found for logical port '%s'" % logical_port_name) + daemon_xcvrd.log_error("No physical ports found for logical port '%s'" % logical_port_name) return PHYSICAL_PORT_NOT_EXIST if len(physical_port_list) > 1: @@ -251,7 +228,7 @@ def post_port_sfp_info_to_db(logical_port_name, table): return SFP_EEPROM_NOT_READY except NotImplementedError: - log_error("This functionality is currently not implemented for this platform") + daemon_xcvrd.log_error("This functionality is currently not implemented for this platform") sys.exit(3) # update dom sensor info to db @@ -259,9 +236,9 @@ def post_port_dom_info_to_db(logical_port_name, table): ganged_port = False ganged_member_num = 1 - physical_port_list = logical_port_name_to_physical_port_list(logical_port_name) + physical_port_list = daemon_xcvrd.logical_port_name_to_physical_port_list(logical_port_name) if physical_port_list is None: - log_error("Error: No physical ports found for logical port '%s'" % logical_port_name) + daemon_xcvrd.log_error("No physical ports found for logical port '%s'" % logical_port_name) return PHYSICAL_PORT_NOT_EXIST if len(physical_port_list) > 1: @@ -297,7 +274,7 @@ def post_port_dom_info_to_db(logical_port_name, table): return SFP_EEPROM_NOT_READY except NotImplementedError: - log_error("This functionality is currently not implemented for this platform") + daemon_xcvrd.log_error("This functionality is currently not implemented for this platform") sys.exit(3) # del sfp and dom info from db @@ -305,9 +282,9 @@ def del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl): ganged_port = False ganged_member_num = 1 - physical_port_list = logical_port_name_to_physical_port_list(logical_port_name) + physical_port_list = daemon_xcvrd.logical_port_name_to_physical_port_list(logical_port_name) if physical_port_list is None: - log_error("Error: No physical ports found for logical port '%s'" % logical_port_name) + daemon_xcvrd.log_error("No physical ports found for logical port '%s'" % logical_port_name) return PHYSICAL_PORT_NOT_EXIST if len(physical_port_list) > 1: @@ -322,7 +299,7 @@ def del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl): dom_tbl._del(port_name) except NotImplementedError: - log_error("This functionality is currently not implemented for this platform") + daemon_xcvrd.log_error("This functionality is currently not implemented for this platform") sys.exit(3) # Timer thread wrapper class to update dom info to DB periodically @@ -334,7 +311,7 @@ class dom_info_update_task: def task_run(self): if self.task_stopping_event.isSet(): - log_error("Error: dom info update thread received stop event, exiting...") + daemon_xcvrd.log_error("dom info update thread received stop event, exiting...") return logical_port_list = platform_sfputil.logical @@ -348,107 +325,15 @@ class dom_info_update_task: self.task_stopping_event.set() self.task_timer.join() -#=============================== Main ================================ - def main(): - log_info("Starting up...") - - # Register our signal handlers - signal.signal(signal.SIGHUP, signal_handler) - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) + global daemon_xcvrd - # Load platform-specific sfputil class - err = load_platform_sfputil() - if err != 0: - log_error("failed to load sfputil") + daemon_xcvrd = DaemonXcvrd() + if not daemon_xcvrd: + print "Failed to load xcvrd daemon utilities" sys.exit(1) - # Load port info - try: - port_config_file_path = get_path_to_port_config_file() - platform_sfputil.read_porttab_mappings(port_config_file_path) - # platform_sfputil.read_port_mappings() - except Exception, e: - log_error("Error reading port info (%s)" % str(e), True) - sys.exit(2) - - # Connect to STATE_DB and create transceiver info/dom info table - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, - REDIS_HOSTNAME, - REDIS_PORT, - REDIS_TIMEOUT_MSECS) - int_tbl = swsscommon.Table(state_db, "TRANSCEIVER_INFO") - dom_tbl = swsscommon.Table(state_db, "TRANSCEIVER_DOM_SENSOR") - - # Connect to APPL_DB abd subscribe to PORT table notifications - appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, - REDIS_HOSTNAME, - REDIS_PORT, - REDIS_TIMEOUT_MSECS) - - sel = swsscommon.Select() - sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) - sel.addSelectable(sst) - - # Make sure this daemon started after all port configured. - while True: - (state, c) = sel.select(SELECT_TIMEOUT_MSECS) - if state == swsscommon.Select.TIMEOUT: - continue - if state != swsscommon.Select.OBJECT: - log_warning("sel.select() did not return swsscommon.Select.OBJECT") - continue - - (key, op, fvp) = sst.pop() - if key in ["PortConfigDone", "PortInitDone"]: - break - - # Post all the current interface SFP info to STATE_DB - logical_port_list = platform_sfputil.logical - for logical_port_name in logical_port_list: - post_port_sfp_info_to_db(logical_port_name, int_tbl) - post_port_dom_info_to_db(logical_port_name, dom_tbl) - - # Start the dom sensor info update timer thread - dom_info_update = dom_info_update_task(dom_tbl) - dom_info_update.task_run() - - # Start main loop to listen to the SFP change event. - log_info("Start main loop") - while True: - status, port_dict = platform_sfputil.get_transceiver_change_event() - if status: - for key, value in port_dict.iteritems(): - logical_port_list = platform_sfputil.get_physical_to_logical(int(key)) - for logical_port in logical_port_list: - if value == SFP_STATUS_INSERTED: - rc = post_port_sfp_info_to_db(logical_port, int_tbl) - # If we didn't get the sfp info, assuming the eeprom is not ready, give a try again. - if rc == SFP_EEPROM_NOT_READY: - time.sleep(TIME_FOR_SFP_READY_SECS) - post_port_sfp_info_to_db(logical_port, int_tbl) - post_port_dom_info_to_db(logical_port, dom_tbl) - - elif value == SFP_STATUS_REMOVED: - del_port_sfp_dom_info_to_db(logical_port, int_tbl, dom_tbl) - else: - # TODO, SFP return error code, need handle accordingly. - continue - else: - # If get_transceiver_change_event() return error, will clean up the DB and then exit - # TODO: next step need to define more error types to handle accordingly. - break - - # Stop the dom info update timer - dom_info_update.task_stop() - - # Clean all the information from DB and then exit - logical_port_list = platform_sfputil.logical - for logical_port_name in logical_port_list: - del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl) - log_error("Error: return error from get_transceiver_change_event(), exiting...") - return 1 + daemon_xcvrd.run() if __name__ == '__main__': main() From 3c6a57ac733566185e78f58369f028e970490ecc Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Thu, 7 Mar 2019 02:52:05 +0800 Subject: [PATCH 2/3] extend xcvrd to post more sfp info to state DB (#24) --- sonic-xcvrd/scripts/xcvrd | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sonic-xcvrd/scripts/xcvrd b/sonic-xcvrd/scripts/xcvrd index 7d71d01a3..b9fc6b4db 100644 --- a/sonic-xcvrd/scripts/xcvrd +++ b/sonic-xcvrd/scripts/xcvrd @@ -222,7 +222,18 @@ def post_port_sfp_info_to_db(logical_port_name, table): ('hardwarerev', port_info_dict['hardwarerev']), ('serialnum', port_info_dict['serialnum']), ('manufacturename', port_info_dict['manufacturename']), - ('modelname', port_info_dict['modelname'])]) + ('modelname', port_info_dict['modelname']), + ('vendor_oui',port_info_dict['vendor_oui']), + ('vendor_date',port_info_dict['vendor_date']), + ('Connector',port_info_dict['Connector']), + ('encoding',port_info_dict['encoding']), + ('ext_identifier',port_info_dict['ext_identifier']), + ('ext_rateselect_compliance',port_info_dict['ext_rateselect_compliance']), + ('cable_type',port_info_dict['cable_type']), + ('cable_length',port_info_dict['cable_length']), + ('specification_compliance',port_info_dict['specification_compliance']), + ('nominal_bit_rate',port_info_dict['nominal_bit_rate'])]) + table.set(port_name, fvs) else: return SFP_EEPROM_NOT_READY From 13c1f225f584b11b9dbf30c290eeba08066e13b5 Mon Sep 17 00:00:00 2001 From: Nazarii Hnydyn Date: Sun, 17 Feb 2019 17:37:00 +0200 Subject: [PATCH 3/3] Fixed xcvrd shutdown flow. Signed-off-by: Nazarii Hnydyn --- sonic-ledd/scripts/ledd | 37 ++-- sonic-psud/scripts/psud | 132 +++++++---- sonic-xcvrd/scripts/xcvrd | 445 ++++++++++++++++++++++++-------------- 3 files changed, 393 insertions(+), 221 deletions(-) diff --git a/sonic-ledd/scripts/ledd b/sonic-ledd/scripts/ledd index 66cde42ba..d69f59a26 100644 --- a/sonic-ledd/scripts/ledd +++ b/sonic-ledd/scripts/ledd @@ -2,7 +2,6 @@ """ ledd - Front-panel LED control daemon for SONiC """ @@ -15,6 +14,8 @@ try: import sys import syslog from swsscommon import swsscommon + from sonic_daemon_base import daemon_base + from sonic_daemon_base.daemon_base import Logger from sonic_daemon_base.daemon_base import DaemonBase except ImportError, e: raise ImportError (str(e) + " - required module not found") @@ -35,15 +36,18 @@ Options: LED_MODULE_NAME = "led_control" LED_CLASS_NAME = "LedControl" + SELECT_TIMEOUT = 1000 +LEDUTIL_LOAD_ERROR = 1 + +logger = Logger(SYSLOG_IDENTIFIER) + class DaemonLedd(DaemonBase): def __init__(self): DaemonBase.__init__(self) - def __exit__(self): - DaemonBase.__exit__(self) - + # Run daemon def run(self): # Parse options if provided if (len(sys.argv) > 1): @@ -65,13 +69,14 @@ class DaemonLedd(DaemonBase): sys.exit(0) # Load platform-specific LedControl module - led_control = self.load_platform_util(LED_MODULE_NAME, LED_CLASS_NAME) - if not led_control: - self.log_error("failed to load ledutil") - sys.exit(1) + try: + led_control = self.load_platform_util(LED_MODULE_NAME, LED_CLASS_NAME) + except Exception as e: + logger.log_error("Failed to load ledutil: %s" % (str(e)), True) + sys.exit(LEDUTIL_LOAD_ERROR) # Open a handle to the Application database - appl_db = self.db_connect(swsscommon.APPL_DB) + appl_db = daemon_base.db_connect(swsscommon.APPL_DB) # Subscribe to PORT table notifications in the Application DB sel = swsscommon.Select() @@ -88,7 +93,7 @@ class DaemonLedd(DaemonBase): # Do not flood log when select times out continue if state != swsscommon.Select.OBJECT: - self.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT") continue (key, op, fvp) = sst.pop() @@ -106,16 +111,8 @@ class DaemonLedd(DaemonBase): return 1 def main(): - if not os.geteuid() == 0: - print "Error: Must be root to run this daemon" - sys.exit(1) - - daemon_ledd = DaemonLedd() - if not daemon_ledd: - print "Failed to instantiate LED daemon" - sys.exit(1) - - daemon_ledd.run() + ledd = DaemonLedd() + ledd.run() if __name__ == '__main__': main() diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud index 922f608c2..3f6e540d2 100644 --- a/sonic-psud/scripts/psud +++ b/sonic-psud/scripts/psud @@ -12,66 +12,118 @@ try: import sys import time + import signal + import threading from swsscommon import swsscommon + from sonic_daemon_base import daemon_base + from sonic_daemon_base.daemon_base import Logger from sonic_daemon_base.daemon_base import DaemonBase except ImportError, e: raise ImportError (str(e) + " - required module not found") -#============================= Constants ============================= +# +# Constants ==================================================================== +# + +SYSLOG_IDENTIFIER = "psud" PLATFORM_SPECIFIC_MODULE_NAME = "psuutil" PLATFORM_SPECIFIC_CLASS_NAME = "PsuUtil" +CHASSIS_INFO_TABLE = 'CHASSIS_INFO' +CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}' +CHASSIS_INFO_PSU_NUM_FIELD = 'psu_num' + +PSU_INFO_TABLE = 'PSU_INFO' +PSU_INFO_KEY_TEMPLATE = 'PSU {}' +PSU_INFO_PRESENCE_FIELD = 'presence' +PSU_INFO_STATUS_FIELD = 'status' + PSU_INFO_UPDATE_PERIOD_SECS = 3 +PSUUTIL_LOAD_ERROR = 1 + +logger = Logger(SYSLOG_IDENTIFIER) + +# +# Helper functions ============================================================= +# + +def psu_db_update(psuutil, psu_tbl, psu_num): + for psu_index in range(1, psu_num + 1): + fvs = swsscommon.FieldValuePairs([(PSU_INFO_PRESENCE_FIELD, + 'true' if psuutil.get_psu_presence(psu_index) else 'false'), + (PSU_INFO_STATUS_FIELD, + 'true' if psuutil.get_psu_status(psu_index) else 'false')]) + psu_tbl.set(PSU_INFO_KEY_TEMPLATE.format(psu_index), fvs) + +# +# Daemon ======================================================================= +# + class DaemonPsud(DaemonBase): def __init__(self): DaemonBase.__init__(self) - def __exit__(self): - DaemonBase.__exit__(self) + self.stop = threading.Event() + + # Signal handler + def signal_handler(self, sig, frame): + if sig == signal.SIGHUP: + logger.log_info("Caught SIGHUP - ignoring...") + elif sig == signal.SIGINT: + logger.log_info("Caught SIGINT - exiting...") + self.stop.set() + elif sig == signal.SIGTERM: + logger.log_info("Caught SIGTERM - exiting...") + self.stop.set() + else: + logger.log_warning("Caught unhandled signal '" + sig + "'") + # Run daemon def run(self): + logger.log_info("Starting up...") + # Load platform-specific psuutil class - platform_psuutil = self.load_platform_util(PLATFORM_SPECIFIC_MODULE_NAME, PLATFORM_SPECIFIC_CLASS_NAME) - if not platform_psuutil: - self.log_error("failed to load psuutil") - sys.exit(1) - - state_db = self.db_connect(swsscommon.STATE_DB) - psu_tbl = swsscommon.Table(state_db, "PSU_INFO") - chassis_tbl = swsscommon.Table(state_db, "CHASSIS_INFO") - num_psus = platform_psuutil.get_num_psus() - fvs = swsscommon.FieldValuePairs([('num_psus', str(num_psus))]) - chassis_tbl.set('chassis 1', fvs) - - # Start main loop to listen to the PSU change event. - self.log_info("Start main loop") - while True: - psu_db_update(platform_psuutil, psu_tbl, num_psus) - time.sleep(PSU_INFO_UPDATE_PERIOD_SECS) - - # Clean all the information from DB and then exit - for psu_index in range(1, num_psus + 1): - psu_tbl._del("PSU {}".format(psu_index)) - chassis_tbl._del('chassis 1') - return 1 - -def psu_db_update(psuutil, psu_tbl, num_psus): - for psu_index in range(1, num_psus + 1): - fvs = swsscommon.FieldValuePairs([('presence', - 'true' if psuutil.get_psu_presence(psu_index) else 'false'), - ('status', - 'true' if psuutil.get_psu_status(psu_index) else 'false')]) - psu_tbl.set("PSU {}".format(psu_index), fvs) + try: + platform_psuutil = self.load_platform_util(PLATFORM_SPECIFIC_MODULE_NAME, PLATFORM_SPECIFIC_CLASS_NAME) + except Exception as e: + logger.log_error("Failed to load psuutil: %s" % (str(e)), True) + sys.exit(PSUUTIL_LOAD_ERROR) -def main(): - daemon_psud = DaemonPsud() - if not daemon_psud: - print "Failed to load psu daemon utilities" - sys.exit(1) + # Connect to STATE_DB and create psu/chassis info tables + state_db = daemon_base.db_connect(swsscommon.STATE_DB) + chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) + psu_tbl = swsscommon.Table(state_db, PSU_INFO_TABLE) + + # Post psu number info to STATE_DB + psu_num = platform_psuutil.get_num_psus() + fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_PSU_NUM_FIELD, str(psu_num))]) + chassis_tbl.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) + + # Start main loop + logger.log_info("Start daemon main loop") + + while not self.stop.wait(PSU_INFO_UPDATE_PERIOD_SECS): + psu_db_update(platform_psuutil, psu_tbl, psu_num) - daemon_psud.run() + logger.log_info("Stop daemon main loop") + + # Delete all the information from DB and then exit + for psu_index in range(1, psu_num + 1): + psu_tbl._del(PSU_INFO_KEY_TEMPLATE.format(psu_index)) + + chassis_tbl._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + + logger.log_info("Shutting down...") + +# +# Main ========================================================================= +# + +def main(): + psud = DaemonPsud() + psud.run() if __name__ == '__main__': main() diff --git a/sonic-xcvrd/scripts/xcvrd b/sonic-xcvrd/scripts/xcvrd index b9fc6b4db..7b146a59d 100644 --- a/sonic-xcvrd/scripts/xcvrd +++ b/sonic-xcvrd/scripts/xcvrd @@ -9,21 +9,27 @@ try: import os import sys import time + import signal import threading + import multiprocessing from swsscommon import swsscommon + from sonic_daemon_base import daemon_base + from sonic_daemon_base.daemon_base import Logger from sonic_daemon_base.daemon_base import DaemonBase except ImportError, e: raise ImportError (str(e) + " - required module not found") -#============================= Constants ============================= +# +# Constants ==================================================================== +# + +SYSLOG_IDENTIFIER = "xcvrd" PLATFORM_SPECIFIC_MODULE_NAME = "sfputil" PLATFORM_SPECIFIC_CLASS_NAME = "SfpUtil" -# Global platform-specific sfputil class instance -platform_sfputil = None -# Global xcvr dameon util class instance -daemon_xcvrd = None +TRANSCEIVER_INFO_TABLE = 'TRANSCEIVER_INFO' +TRANSCEIVER_DOM_SENSOR_TABLE = 'TRANSCEIVER_DOM_SENSOR' SELECT_TIMEOUT_MSECS = 1000 @@ -36,132 +42,37 @@ SFP_STATUS_REMOVED = '0' PHYSICAL_PORT_NOT_EXIST = -1 SFP_EEPROM_NOT_READY = -2 +SFPUTIL_LOAD_ERROR = 1 +PORT_CONFIG_LOAD_ERROR = 2 +NOT_IMPLEMENTED_ERROR = 3 + TEMP_UNIT = 'C' VOLT_UNIT = 'Volts' POWER_UNIT = 'dBm' BIAS_UNIT = 'mA' -class DaemonXcvrd(DaemonBase): - def __init__(self): - DaemonBase.__init__(self) - - def __exit__(self): - DaemonBase.__exit__(self) - - # Returns path to port config file - def get_path_to_port_config_file(self): - (platform_path, hwsku_path) = self.get_path_to_platform_and_hwsku() - # First check for the presence of the new 'port_config.ini' file - port_config_file_path = "/".join([hwsku_path, "port_config.ini"]) - if not os.path.isfile(port_config_file_path): - # port_config.ini doesn't exist. Try loading the legacy 'portmap.ini' file - port_config_file_path = "/".join([hwsku_path, "portmap.ini"]) - - return port_config_file_path - - # find out the underneath physical port list by logical name - def logical_port_name_to_physical_port_list(self, port_name): - if port_name.startswith("Ethernet"): - if platform_sfputil.is_logical_port(port_name): - return platform_sfputil.get_logical_to_physical(port_name) - else: - self.log_error("Invalid port '%s'" % port_name) - return None - else: - return [int(port_name)] - - def run(self): - global platform_sfputil - - # Load platform-specific sfputil class - platform_sfputil = self.load_platform_util(PLATFORM_SPECIFIC_MODULE_NAME, PLATFORM_SPECIFIC_CLASS_NAME) - if not platform_sfputil: - self.log_error("failed to load sfputil") - sys.exit(1) - - # Load port info - try: - port_config_file_path = self.get_path_to_port_config_file() - platform_sfputil.read_porttab_mappings(port_config_file_path) - # platform_sfputil.read_port_mappings() - except Exception, e: - self.log_error("failed to reading port info (%s)" % str(e)) - sys.exit(2) - - # Connect to STATE_DB and create transceiver info/dom info table - state_db = self.db_connect(swsscommon.STATE_DB) - int_tbl = swsscommon.Table(state_db, "TRANSCEIVER_INFO") - dom_tbl = swsscommon.Table(state_db, "TRANSCEIVER_DOM_SENSOR") - - # Connect to APPL_DB abd subscribe to PORT table notifications - appl_db = self.db_connect(swsscommon.APPL_DB) - - sel = swsscommon.Select() - sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) - sel.addSelectable(sst) - - # Make sure this daemon started after all port configured. - while True: - (state, c) = sel.select(SELECT_TIMEOUT_MSECS) - if state == swsscommon.Select.TIMEOUT: - continue - if state != swsscommon.Select.OBJECT: - self.log_warning("sel.select() did not return swsscommon.Select.OBJECT") - continue - - (key, op, fvp) = sst.pop() - if key in ["PortConfigDone", "PortInitDone"]: - break - - # Post all the current interface SFP info to STATE_DB - logical_port_list = platform_sfputil.logical - for logical_port_name in logical_port_list: - post_port_sfp_info_to_db(logical_port_name, int_tbl) - post_port_dom_info_to_db(logical_port_name, dom_tbl) - - # Start the dom sensor info update timer thread - dom_info_update = dom_info_update_task(dom_tbl) - dom_info_update.task_run() - - # Start main loop to listen to the SFP change event. - self.log_info("Start main loop") - while True: - status, port_dict = platform_sfputil.get_transceiver_change_event() - if status: - for key, value in port_dict.iteritems(): - logical_port_list = platform_sfputil.get_physical_to_logical(int(key)) - for logical_port in logical_port_list: - if value == SFP_STATUS_INSERTED: - rc = post_port_sfp_info_to_db(logical_port, int_tbl) - # If we didn't get the sfp info, assuming the eeprom is not ready, give a try again. - if rc == SFP_EEPROM_NOT_READY: - time.sleep(TIME_FOR_SFP_READY_SECS) - post_port_sfp_info_to_db(logical_port, int_tbl) - post_port_dom_info_to_db(logical_port, dom_tbl) +# Global platform specific sfputil class instance +platform_sfputil = None - elif value == SFP_STATUS_REMOVED: - del_port_sfp_dom_info_to_db(logical_port, int_tbl, dom_tbl) - else: - # TODO, SFP return error code, need handle accordingly. - continue - else: - # If get_transceiver_change_event() return error, will clean up the DB and then exit - # TODO: next step need to define more error types to handle accordingly. - break +# Global logger class instance +logger = Logger(SYSLOG_IDENTIFIER) - # Stop the dom info update timer - dom_info_update.task_stop() +# +# Helper functions ============================================================= +# - # Clean all the information from DB and then exit - logical_port_list = platform_sfputil.logical - for logical_port_name in logical_port_list: - del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl) - return 1 +# Find out the underneath physical port list by logical name +def logical_port_name_to_physical_port_list(port_name): + if port_name.startswith("Ethernet"): + if platform_sfputil.is_logical_port(port_name): + return platform_sfputil.get_logical_to_physical(port_name) + else: + logger.log_error("Invalid port '%s'" % port_name) + return None + else: + return [int(port_name)] -# Returns, -# port_num if physical -# logical_port:port_num if logical port and is a ganged port -# logical_port if logical and not ganged +# Get physical port name def get_physical_port_name(logical_port, physical_port, ganged): if logical_port == physical_port: return logical_port @@ -170,6 +81,7 @@ def get_physical_port_name(logical_port, physical_port, ganged): else: return logical_port +# Strip units and beautify def strip_unit_and_beautify(value, unit): # Strip unit from raw data width = len(unit) @@ -195,20 +107,23 @@ def beautify_dom_info_dict(dom_info_dict): dom_info_dict['tx3power'] = strip_unit_and_beautify(dom_info_dict['tx3power'], POWER_UNIT) dom_info_dict['tx4power'] = strip_unit_and_beautify(dom_info_dict['tx4power'], POWER_UNIT) -# update all the sfp info to db -def post_port_sfp_info_to_db(logical_port_name, table): +# Update port sfp info in db +def post_port_sfp_info_to_db(logical_port_name, table, stop=threading.Event()): ganged_port = False ganged_member_num = 1 - physical_port_list = daemon_xcvrd.logical_port_name_to_physical_port_list(logical_port_name) + physical_port_list = logical_port_name_to_physical_port_list(logical_port_name) if physical_port_list is None: - daemon_xcvrd.log_error("No physical ports found for logical port '%s'" % logical_port_name) + logger.log_error("No physical ports found for logical port '%s'" % logical_port_name) return PHYSICAL_PORT_NOT_EXIST if len(physical_port_list) > 1: ganged_port = True for physical_port in physical_port_list: + if stop.is_set(): + break + if not platform_sfputil.get_presence(physical_port): continue @@ -233,29 +148,31 @@ def post_port_sfp_info_to_db(logical_port_name, table): ('cable_length',port_info_dict['cable_length']), ('specification_compliance',port_info_dict['specification_compliance']), ('nominal_bit_rate',port_info_dict['nominal_bit_rate'])]) - table.set(port_name, fvs) else: return SFP_EEPROM_NOT_READY except NotImplementedError: - daemon_xcvrd.log_error("This functionality is currently not implemented for this platform") - sys.exit(3) + logger.log_error("This functionality is currently not implemented for this platform") + sys.exit(NOT_IMPLEMENTED_ERROR) -# update dom sensor info to db -def post_port_dom_info_to_db(logical_port_name, table): +# Update port dom sensor info in db +def post_port_dom_info_to_db(logical_port_name, table, stop=threading.Event()): ganged_port = False ganged_member_num = 1 - physical_port_list = daemon_xcvrd.logical_port_name_to_physical_port_list(logical_port_name) + physical_port_list = logical_port_name_to_physical_port_list(logical_port_name) if physical_port_list is None: - daemon_xcvrd.log_error("No physical ports found for logical port '%s'" % logical_port_name) + logger.log_error("No physical ports found for logical port '%s'" % logical_port_name) return PHYSICAL_PORT_NOT_EXIST if len(physical_port_list) > 1: ganged_port = True for physical_port in physical_port_list: + if stop.is_set(): + break + if not platform_sfputil.get_presence(physical_port): continue @@ -285,17 +202,33 @@ def post_port_dom_info_to_db(logical_port_name, table): return SFP_EEPROM_NOT_READY except NotImplementedError: - daemon_xcvrd.log_error("This functionality is currently not implemented for this platform") - sys.exit(3) - -# del sfp and dom info from db -def del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl): + logger.log_error("This functionality is currently not implemented for this platform") + sys.exit(NOT_IMPLEMENTED_ERROR) + +# Update port dom/sfp info in db +def post_port_sfp_dom_info_to_db(stop=threading.Event()): + # Connect to STATE_DB and create transceiver dom/sfp info tables + state_db = daemon_base.db_connect(swsscommon.STATE_DB) + int_tbl = swsscommon.Table(state_db, TRANSCEIVER_INFO_TABLE) + dom_tbl = swsscommon.Table(state_db, TRANSCEIVER_DOM_SENSOR_TABLE) + + # Post all the current interface dom/sfp info to STATE_DB + logical_port_list = platform_sfputil.logical + for logical_port_name in logical_port_list: + if stop.is_set(): + break + + post_port_sfp_info_to_db(logical_port_name, int_tbl, stop) + post_port_dom_info_to_db(logical_port_name, dom_tbl, stop) + +# Delete port dom/sfp info from db +def del_port_sfp_dom_info_from_db(logical_port_name, int_tbl, dom_tbl): ganged_port = False ganged_member_num = 1 - physical_port_list = daemon_xcvrd.logical_port_name_to_physical_port_list(logical_port_name) + physical_port_list = logical_port_name_to_physical_port_list(logical_port_name) if physical_port_list is None: - daemon_xcvrd.log_error("No physical ports found for logical port '%s'" % logical_port_name) + logger.log_error("No physical ports found for logical port '%s'" % logical_port_name) return PHYSICAL_PORT_NOT_EXIST if len(physical_port_list) > 1: @@ -310,41 +243,231 @@ def del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl): dom_tbl._del(port_name) except NotImplementedError: - daemon_xcvrd.log_error("This functionality is currently not implemented for this platform") - sys.exit(3) + logger.log_error("This functionality is currently not implemented for this platform") + sys.exit(NOT_IMPLEMENTED_ERROR) + +# +# Helper classes =============================================================== +# -# Timer thread wrapper class to update dom info to DB periodically +# Thread wrapper class to update dom info periodically class dom_info_update_task: - def __init__(self, table): + def __init__(self): + self.task_thread = None self.task_stopping_event = threading.Event() - self.task_timer = None - self.dom_table = table + + def task_worker(self): + logger.log_info("Start DOM monitoring loop") + + # Connect to STATE_DB and create transceiver dom info table + state_db = daemon_base.db_connect(swsscommon.STATE_DB) + dom_tbl = swsscommon.Table(state_db, TRANSCEIVER_DOM_SENSOR_TABLE) + + # Start loop to update dom info in DB periodically + while not self.task_stopping_event.wait(DOM_INFO_UPDATE_PERIOD_SECS): + logical_port_list = platform_sfputil.logical + for logical_port_name in logical_port_list: + post_port_dom_info_to_db(logical_port_name, dom_tbl, self.task_stopping_event) + + logger.log_info("Stop DOM monitoring loop") def task_run(self): - if self.task_stopping_event.isSet(): - daemon_xcvrd.log_error("dom info update thread received stop event, exiting...") + if self.task_stopping_event.is_set(): return - logical_port_list = platform_sfputil.logical - for logical_port_name in logical_port_list: - post_port_dom_info_to_db(logical_port_name, self.dom_table) + self.task_thread = threading.Thread(target=self.task_worker) + self.task_thread.start() + + def task_stop(self): + self.task_stopping_event.set() + self.task_thread.join() + +# Process wrapper class to update sfp state info periodically +class sfp_state_update_task: + def __init__(self): + self.task_process = None + self.task_stopping_event = multiprocessing.Event() - self.task_timer = threading.Timer(DOM_INFO_UPDATE_PERIOD_SECS, self.task_run) - self.task_timer.start() + def task_worker(self, stopping_event): + logger.log_info("Start SFP monitoring loop") + + # Connect to STATE_DB and create transceiver dom/sfp info tables + state_db = daemon_base.db_connect(swsscommon.STATE_DB) + int_tbl = swsscommon.Table(state_db, TRANSCEIVER_INFO_TABLE) + dom_tbl = swsscommon.Table(state_db, TRANSCEIVER_DOM_SENSOR_TABLE) + + # Start loop to listen to the sfp change event + while not stopping_event.is_set(): + status, port_dict = platform_sfputil.get_transceiver_change_event() + if status: + for key, value in port_dict.iteritems(): + logical_port_list = platform_sfputil.get_physical_to_logical(int(key)) + for logical_port in logical_port_list: + if value == SFP_STATUS_INSERTED: + logger.log_info("Got SFP inserted event") + rc = post_port_sfp_info_to_db(logical_port, int_tbl) + # If we didn't get the sfp info, assuming the eeprom is not ready, give a try again. + if rc == SFP_EEPROM_NOT_READY: + logger.log_warning("SFP EEPROM is not ready. One more try...") + time.sleep(TIME_FOR_SFP_READY_SECS) + post_port_sfp_info_to_db(logical_port, int_tbl) + post_port_dom_info_to_db(logical_port, dom_tbl) + elif value == SFP_STATUS_REMOVED: + logger.log_info("Got SFP removed event") + del_port_sfp_dom_info_from_db(logical_port, int_tbl, dom_tbl) + else: + # TODO, SFP return error code, need handle accordingly. + continue + else: + # If get_transceiver_change_event() return error, will clean up the DB and then exit + # TODO: next step need to define more error types to handle accordingly. + logger.log_error("Failed to get transceiver change event. Exiting...") + os.kill(os.getppid(), signal.SIGTERM) + break + + logger.log_info("Stop SFP monitoring loop") + + def task_run(self): + if self.task_stopping_event.is_set(): + return + + self.task_process = multiprocessing.Process(target=self.task_worker,args=(self.task_stopping_event,)) + self.task_process.start() def task_stop(self): self.task_stopping_event.set() - self.task_timer.join() + os.kill(self.task_process.pid, signal.SIGKILL) -def main(): - global daemon_xcvrd +# +# Daemon ======================================================================= +# + +class DaemonXcvrd(DaemonBase): + def __init__(self): + DaemonBase.__init__(self) + + self.timeout = 1 + self.stop = threading.Event() + + # Signal handler + def signal_handler(self, sig, frame): + if sig == signal.SIGHUP: + logger.log_info("Caught SIGHUP - ignoring...") + elif sig == signal.SIGINT: + logger.log_info("Caught SIGINT - exiting...") + self.stop.set() + elif sig == signal.SIGTERM: + logger.log_info("Caught SIGTERM - exiting...") + self.stop.set() + else: + logger.log_warning("Caught unhandled signal '" + sig + "'") + + # Wait for port config is done + def wait_for_port_config_done(self): + # Connect to APPL_DB and subscribe to PORT table notifications + appl_db = daemon_base.db_connect(swsscommon.APPL_DB) + + sel = swsscommon.Select() + sst = swsscommon.SubscriberStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) + sel.addSelectable(sst) + + # Make sure this daemon started after all port configured + while not self.stop.is_set(): + (state, c) = sel.select(SELECT_TIMEOUT_MSECS) + if state == swsscommon.Select.TIMEOUT: + continue + if state != swsscommon.Select.OBJECT: + logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + continue + + (key, op, fvp) = sst.pop() + if key in ["PortConfigDone", "PortInitDone"]: + break + + # Initialize daemon + def init(self): + global platform_sfputil + + logger.log_info("Start daemon init...") + + # Load platform specific sfputil class + try: + platform_sfputil = self.load_platform_util(PLATFORM_SPECIFIC_MODULE_NAME, PLATFORM_SPECIFIC_CLASS_NAME) + except Exception as e: + logger.log_error("Failed to load sfputil: %s" % (str(e)), True) + sys.exit(SFPUTIL_LOAD_ERROR) + + # Load port info + try: + port_config_file_path = self.get_path_to_port_config_file() + platform_sfputil.read_porttab_mappings(port_config_file_path) + except Exception, e: + logger.log_error("Failed to read port info: %s" % (str(e)), True) + sys.exit(PORT_CONFIG_LOAD_ERROR) + + # Make sure this daemon started after all port configured + logger.log_info("Wait for port config is done") + self.wait_for_port_config_done() + + # Post all the current interface dom/sfp info to STATE_DB + logger.log_info("Post all port DOM/SFP info to DB") + post_port_sfp_dom_info_to_db(self.stop) - daemon_xcvrd = DaemonXcvrd() - if not daemon_xcvrd: - print "Failed to load xcvrd daemon utilities" - sys.exit(1) + # Deinitialize daemon + def deinit(self): + logger.log_info("Start daemon deinit...") - daemon_xcvrd.run() + # Connect to STATE_DB and create transceiver dom/sfp info tables + state_db = daemon_base.db_connect(swsscommon.STATE_DB) + int_tbl = swsscommon.Table(state_db, TRANSCEIVER_INFO_TABLE) + dom_tbl = swsscommon.Table(state_db, TRANSCEIVER_DOM_SENSOR_TABLE) + + # Delete all the information from DB and then exit + logical_port_list = platform_sfputil.logical + for logical_port_name in logical_port_list: + del_port_sfp_dom_info_from_db(logical_port_name, int_tbl, dom_tbl) + + # Run daemon + def run(self): + logger.log_info("Starting up...") + + # Start daemon initialization sequence + self.init() + + # Start the dom sensor info update thread + dom_info_update = dom_info_update_task() + dom_info_update.task_run() + + # Start the sfp state info update process + sfp_state_update = sfp_state_update_task() + sfp_state_update.task_run() + + # Start main loop + logger.log_info("Start daemon main loop") + + while not self.stop.wait(self.timeout): + pass + + logger.log_info("Stop daemon main loop") + + # Stop the dom sensor info update thread + dom_info_update.task_stop() + + # Stop the sfp state info update process + sfp_state_update.task_stop() + + # Start daemon deinitialization sequence + self.deinit() + + logger.log_info("Shutting down...") + +# +# Main ========================================================================= +# + +def main(): + xcvrd = DaemonXcvrd() + xcvrd.run() if __name__ == '__main__': main()