diff --git a/sonic-ledd/scripts/ledd b/sonic-ledd/scripts/ledd index 75a1d7e69..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,9 @@ 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,199 +37,82 @@ Options: LED_MODULE_NAME = "led_control" LED_CLASS_NAME = "LedControl" -SONIC_CFGGEN = "/usr/local/bin/sonic-cfggen" -MINIGRAPH_FILE = "/etc/sonic/minigraph.xml" -HWSKU_KEY = "minigraph_hwsku" -PLATFORM_KEY = "platform" +SELECT_TIMEOUT = 1000 -# platform directory in base image -PLATFORM_ROOT = "/usr/share/sonic/device" +LEDUTIL_LOAD_ERROR = 1 -# platform root directory inside docker -PLATFORM_ROOT_DOCKER = "/usr/share/sonic/platform" +logger = Logger(SYSLOG_IDENTIFIER) -REDIS_HOSTNAME = "localhost" -REDIS_PORT = 6379 -REDIS_TIMEOUT_USECS = 0 +class DaemonLedd(DaemonBase): + def __init__(self): + DaemonBase.__init__(self) -SELECT_TIMEOUT = 1000 + # Run daemon + 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) -#========================== 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 ================================ + 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) -def main(): - log_info("Starting up...") + # Load platform-specific LedControl module + 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) - 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) + # Open a handle to the Application database + appl_db = daemon_base.db_connect(swsscommon.APPL_DB) - # 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"]) + # 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: + logger.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(): + ledd = DaemonLedd() + ledd.run() if __name__ == '__main__': main() diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud index 4c50a7dc2..3f6e540d2 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,120 @@ """ try: - import getopt - import os - import imp - import signal - import subprocess import sys - import syslog 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 ==================================================================== +# -VERSION = '1.0' +SYSLOG_IDENTIFIER = "psud" -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 +CHASSIS_INFO_TABLE = 'CHASSIS_INFO' +CHASSIS_INFO_KEY_TEMPLATE = 'chassis {}' +CHASSIS_INFO_PSU_NUM_FIELD = 'psu_num' -REDIS_HOSTNAME = "localhost" -REDIS_PORT = 6379 -REDIS_TIMEOUT_MSECS = 0 +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 -#========================== 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): - for psu_index in range(1, num_psus + 1): - fvs = swsscommon.FieldValuePairs([('presence', - 'true' if platform_psuutil.get_psu_presence(psu_index) else 'false'), - ('status', - 'true' if platform_psuutil.get_psu_status(psu_index) else 'false')]) - psu_tbl.set("PSU {}".format(psu_index), fvs) - -#=============================== Main ================================ +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) + + 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 + 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) + + # 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) + + 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(): - 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") - 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 + psud = DaemonPsud() + psud.run() if __name__ == '__main__': main() diff --git a/sonic-xcvrd/scripts/xcvrd b/sonic-xcvrd/scripts/xcvrd index 777719011..7b146a59d 100644 --- a/sonic-xcvrd/scripts/xcvrd +++ b/sonic-xcvrd/scripts/xcvrd @@ -1,50 +1,36 @@ #!/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 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 ============================= - -VERSION = '1.0' +# +# Constants ==================================================================== +# -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' +SYSLOG_IDENTIFIER = "xcvrd" PLATFORM_SPECIFIC_MODULE_NAME = "sfputil" PLATFORM_SPECIFIC_CLASS_NAME = "SfpUtil" -# Global platform-specific sfputil class instance -platform_sfputil = None +TRANSCEIVER_INFO_TABLE = 'TRANSCEIVER_INFO' +TRANSCEIVER_DOM_SENSOR_TABLE = 'TRANSCEIVER_DOM_SENSOR' -# 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 @@ -56,135 +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' -#========================== 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() +# Global platform specific sfputil class instance +platform_sfputil = None - if also_print_to_console: - print msg +# Global logger class instance +logger = Logger(SYSLOG_IDENTIFIER) -#========================== Signal Handling ========================== +# +# Helper functions ============================================================= +# -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 +# 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 + 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 @@ -193,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) @@ -218,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 = 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) + 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 @@ -245,29 +137,42 @@ 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 except NotImplementedError: - 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 = 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) + 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 @@ -297,17 +202,33 @@ 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") - sys.exit(3) + 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) -# del sfp and dom info from db -def del_port_sfp_dom_info_to_db(logical_port_name, int_tbl, dom_tbl): + # 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 = 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) + 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: @@ -322,133 +243,231 @@ 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") - sys.exit(3) + logger.log_error("This functionality is currently not implemented for this platform") + sys.exit(NOT_IMPLEMENTED_ERROR) -# Timer thread wrapper class to update dom info to DB periodically +# +# Helper classes =============================================================== +# + +# 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(): - log_error("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_timer = threading.Timer(DOM_INFO_UPDATE_PERIOD_SECS, self.task_run) - self.task_timer.start() + self.task_thread = threading.Thread(target=self.task_worker) + self.task_thread.start() def task_stop(self): self.task_stopping_event.set() - self.task_timer.join() + 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() + + 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 -#=============================== Main ================================ + logger.log_info("Stop SFP monitoring loop") -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 sfputil class - err = load_platform_sfputil() - if err != 0: - log_error("failed to load sfputil") - 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 + def task_run(self): + if self.task_stopping_event.is_set(): + return - (key, op, fvp) = sst.pop() - if key in ["PortConfigDone", "PortInitDone"]: - break + self.task_process = multiprocessing.Process(target=self.task_worker,args=(self.task_stopping_event,)) + self.task_process.start() - # 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 + def task_stop(self): + self.task_stopping_event.set() + os.kill(self.task_process.pid, signal.SIGKILL) + +# +# 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: - # 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 + logger.log_warning("Caught unhandled signal '" + sig + "'") - # Stop the dom info update timer - dom_info_update.task_stop() + # 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) - # 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 + 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) + + # Deinitialize daemon + def deinit(self): + logger.log_info("Start daemon deinit...") + + # 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()