From fa84c4226b76336aed7f865c9e935bf55b5f7118 Mon Sep 17 00:00:00 2001 From: "Kevin(Shengkai) Wang" Date: Wed, 6 Mar 2019 14:55:43 +0800 Subject: [PATCH] [sonic-daemon-base] Create DaemonBase class for all daemons (#2570) DaemonBase is to wrap the common function of daemons, such as logging, signal handling, db connector, load specific platform plugins. Signed-off-by: Kevin Wang --- rules/docker-platform-monitor.mk | 1 + rules/sonic-daemon-base.mk | 7 + src/sonic-daemon-base/setup.py | 30 ++++ .../sonic_daemon_base/__init__.py | 0 .../sonic_daemon_base/daemon_base.py | 136 ++++++++++++++++++ 5 files changed, 174 insertions(+) create mode 100644 rules/sonic-daemon-base.mk create mode 100644 src/sonic-daemon-base/setup.py create mode 100644 src/sonic-daemon-base/sonic_daemon_base/__init__.py create mode 100644 src/sonic-daemon-base/sonic_daemon_base/daemon_base.py diff --git a/rules/docker-platform-monitor.mk b/rules/docker-platform-monitor.mk index 317bc9b46c5c..a1000849bc76 100644 --- a/rules/docker-platform-monitor.mk +++ b/rules/docker-platform-monitor.mk @@ -7,6 +7,7 @@ $(DOCKER_PLATFORM_MONITOR)_PYTHON_DEBS += $(SONIC_LEDD) $(SONIC_XCVRD) $(SONIC_P $(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PLATFORM_COMMON_PY2) $(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SWSSSDK_PY2) $(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PLATFORM_API_PY2) +$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_DAEMON_BASE_PY2) $(DOCKER_PLATFORM_MONITOR)_LOAD_DOCKERS = $(DOCKER_CONFIG_ENGINE) SONIC_DOCKER_IMAGES += $(DOCKER_PLATFORM_MONITOR) diff --git a/rules/sonic-daemon-base.mk b/rules/sonic-daemon-base.mk new file mode 100644 index 000000000000..dad0900e75ca --- /dev/null +++ b/rules/sonic-daemon-base.mk @@ -0,0 +1,7 @@ +# SONIC_DAEMON_BASE_PY2 package + +SONIC_DAEMON_BASE_PY2 = sonic_daemon_base-1.0-py2-none-any.whl +$(SONIC_DAEMON_BASE_PY2)_SRC_PATH = $(SRC_PATH)/sonic-daemon-base +$(SONIC_DAEMON_BASE_PY2)_PYTHON_VERSION = 2 +SONIC_PYTHON_WHEELS += $(SONIC_DAEMON_BASE_PY2) + diff --git a/src/sonic-daemon-base/setup.py b/src/sonic-daemon-base/setup.py new file mode 100644 index 000000000000..397af04b87dc --- /dev/null +++ b/src/sonic-daemon-base/setup.py @@ -0,0 +1,30 @@ +from setuptools import setup + +setup( + name='sonic-daemon-base', + version='1.0', + description='Sonic daemon base package', + 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', + packages=[ + 'sonic_daemon_base', + ], + 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 PLATFORM platform DAEMON daemon', +) + diff --git a/src/sonic-daemon-base/sonic_daemon_base/__init__.py b/src/sonic-daemon-base/sonic_daemon_base/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/sonic-daemon-base/sonic_daemon_base/daemon_base.py b/src/sonic-daemon-base/sonic_daemon_base/daemon_base.py new file mode 100644 index 000000000000..7fc14bf55c1f --- /dev/null +++ b/src/sonic-daemon-base/sonic_daemon_base/daemon_base.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python2 + +try: + import imp + import signal + import subprocess + import sys + import syslog + from swsscommon import swsscommon +except ImportError, e: + raise ImportError (str(e) + " - required module not found") + +#============================= Constants ============================= + +# 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' + +class DaemonBase(object): + # Redis DB information + redis_hostname = "localhost" + redis_port = 6379 + redis_timeout_msecs = 0 + + def __init__(self): + self.log_info("Starting up...") + # Register our signal handlers + signal.signal(signal.SIGHUP, self.signal_handler) + signal.signal(signal.SIGINT, self.signal_handler) + signal.signal(signal.SIGTERM, self.signal_handler) + + def __del__(self): + self.log_error("Return from daemon, exiting...") + + def run(self): + raise NotImplementedError() + + # ========================== Connect to DB ============================ + def db_connect(self, db): + return swsscommon.DBConnector(db, + self.redis_hostname, + self.redis_port, + self.redis_timeout_msecs) + + # ========================== Syslog wrappers ========================== + def log_info(self, msg): + syslog.openlog() + syslog.syslog(syslog.LOG_INFO, msg) + syslog.closelog() + + def log_warning(self, msg): + syslog.openlog() + syslog.syslog(syslog.LOG_WARNING, msg) + syslog.closelog() + + def log_error(self, msg): + syslog.openlog() + syslog.syslog(syslog.LOG_ERR, msg) + syslog.closelog() + + #========================== Signal Handling ========================== + def signal_handler(self, sig, frame): + if sig == signal.SIGHUP: + self.log_info("Caught SIGHUP - ignoring...") + return + elif sig == signal.SIGINT: + self.log_info("Caught SIGINT - exiting...") + sys.exit(128 + sig) + elif sig == signal.SIGTERM: + self.log_info("Caught SIGTERM - exiting...") + sys.exit(128 + sig) + else: + self.log_warning("Caught unhandled signal '" + sig + "'") + return + + #============ Functions to load platform-specific classes ============ + # Returns platform and HW SKU + def get_platform_and_hwsku(self): + 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: + self.log_error("Cannot to detect platform") + raise OSError("Cannot detect platform") + + return (platform, hwsku) + + # Returns path to hwsku + def get_path_to_platform_and_hwsku(self): + # Get platform and hwsku + (platform, hwsku) = self.get_platform_and_hwsku() + + # Load platform module from source + platform_path = PLATFORM_ROOT_DOCKER + hwsku_path = "/".join([platform_path, hwsku]) + + return (platform_path, hwsku_path) + + # Loads platform specific psuutil module from source + def load_platform_util(self, module_name, class_name): + platform_util = None + + # Get path to platform and hwsku + (platform_path, hwsku_path) = self.get_path_to_platform_and_hwsku() + + try: + module_file = "/".join([platform_path, "plugins", module_name + ".py"]) + module = imp.load_source(module_name, module_file) + except IOError, e: + self.log_error("Failed to load platform module '%s': %s" % (module_name, str(e))) + return None + + try: + platform_util_class = getattr(module, class_name) + platform_util = platform_util_class() + except AttributeError, e: + self.log_error("Failed to instantiate '%s' class: %s" % (class_name, str(e))) + return None + + return platform_util +