diff --git a/sonic-psud/scripts/psud b/sonic-psud/scripts/psud new file mode 100644 index 000000000000..4c50a7dc2fb3 --- /dev/null +++ b/sonic-psud/scripts/psud @@ -0,0 +1,190 @@ +#!/usr/bin/env python2 + +""" + 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 + includes three things: number of PSU, PSU presence and PSU status which is supported by old plugins. + The loop interval is PSU_INFO_UPDATE_PERIOD_SECS in seconds. +""" + +try: + import getopt + import os + import imp + import signal + import subprocess + import sys + import syslog + import time + from swsscommon import swsscommon +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): + 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 ================================ + +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 + +if __name__ == '__main__': + main() diff --git a/sonic-psud/setup.py b/sonic-psud/setup.py new file mode 100644 index 000000000000..51771a676349 --- /dev/null +++ b/sonic-psud/setup.py @@ -0,0 +1,29 @@ +from setuptools import setup + +setup( + name='sonic-psud', + version='1.0', + description='PSU daemon for SONiC', + license='Apache 2.0', + author='SONiC Team', + author_email='linuxnetdev@microsoft.com', + url='https://github.com/Azure/sonic-platform-daemons', + maintainer='Kevin Wang', + maintainer_email='kevinw@mellanox.com', + scripts=[ + 'scripts/psud', + ], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: No Input/Output (Daemon)', + 'Intended Audience :: Developers', + 'Intended Audience :: Information Technology', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Natural Language :: English', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python :: 2.7', + 'Topic :: System :: Hardware', + ], + keywords='sonic SONiC psu PSU daemon psud PSUD', +)