Introduce sonic-py-common package (#5003)
Consolidate common SONiC Python-language functionality into one shared package (sonic-py-common) and eliminate duplicate code. The package currently includes three modules: - daemon_base - device_info - logger
This commit is contained in:
parent
89184038fd
commit
c0d1616f89
6
.gitignore
vendored
6
.gitignore
vendored
@ -32,10 +32,16 @@ installer/x86_64/platforms/
|
|||||||
src/sonic-config-engine/**/*.pyc
|
src/sonic-config-engine/**/*.pyc
|
||||||
src/sonic-config-engine/build
|
src/sonic-config-engine/build
|
||||||
src/sonic-config-engine/sonic_config_engine.egg-info
|
src/sonic-config-engine/sonic_config_engine.egg-info
|
||||||
|
|
||||||
src/sonic-daemon-base/**/*.pyc
|
src/sonic-daemon-base/**/*.pyc
|
||||||
src/sonic-daemon-base/build
|
src/sonic-daemon-base/build
|
||||||
src/sonic-daemon-base/sonic_daemon_base.egg-info
|
src/sonic-daemon-base/sonic_daemon_base.egg-info
|
||||||
|
|
||||||
|
src/sonic-py-common/**/*.pyc
|
||||||
|
src/sonic-py-common/build
|
||||||
|
src/sonic-py-common/dist
|
||||||
|
src/sonic-py-common/sonic_py_common.egg-info
|
||||||
|
|
||||||
# Misc. files
|
# Misc. files
|
||||||
asic_config_checksum
|
asic_config_checksum
|
||||||
files/Aboot/boot0
|
files/Aboot/boot0
|
||||||
|
@ -270,6 +270,7 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y in
|
|||||||
openssh-server \
|
openssh-server \
|
||||||
python \
|
python \
|
||||||
python-setuptools \
|
python-setuptools \
|
||||||
|
python3-setuptools \
|
||||||
python-apt \
|
python-apt \
|
||||||
traceroute \
|
traceroute \
|
||||||
iputils-ping \
|
iputils-ping \
|
||||||
|
@ -163,6 +163,18 @@ sudo cp {{daemon_base_py2_wheel_path}} $FILESYSTEM_ROOT/$DAEMON_BASE_PY2_WHEEL_N
|
|||||||
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install $DAEMON_BASE_PY2_WHEEL_NAME
|
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install $DAEMON_BASE_PY2_WHEEL_NAME
|
||||||
sudo rm -rf $FILESYSTEM_ROOT/$DAEMON_BASE_PY2_WHEEL_NAME
|
sudo rm -rf $FILESYSTEM_ROOT/$DAEMON_BASE_PY2_WHEEL_NAME
|
||||||
|
|
||||||
|
# Install sonic-py-common Python 2 package
|
||||||
|
SONIC_PY_COMMON_PY2_WHEEL_NAME=$(basename {{sonic_py_common_py2_wheel_path}})
|
||||||
|
sudo cp {{sonic_py_common_py2_wheel_path}} $FILESYSTEM_ROOT/$SONIC_PY_COMMON_PY2_WHEEL_NAME
|
||||||
|
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install $SONIC_PY_COMMON_PY2_WHEEL_NAME
|
||||||
|
sudo rm -rf $FILESYSTEM_ROOT/$SONIC_PY_COMMON_PY2_WHEEL_NAME
|
||||||
|
|
||||||
|
# Install sonic-py-common Python 3 package
|
||||||
|
SONIC_PY_COMMON_PY3_WHEEL_NAME=$(basename {{sonic_py_common_py3_wheel_path}})
|
||||||
|
sudo cp {{sonic_py_common_py3_wheel_path}} $FILESYSTEM_ROOT/$SONIC_PY_COMMON_PY3_WHEEL_NAME
|
||||||
|
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip3 install $SONIC_PY_COMMON_PY3_WHEEL_NAME
|
||||||
|
sudo rm -rf $FILESYSTEM_ROOT/$SONIC_PY_COMMON_PY3_WHEEL_NAME
|
||||||
|
|
||||||
# Install built Python Click package (and its dependencies via 'apt-get -y install -f')
|
# Install built Python Click package (and its dependencies via 'apt-get -y install -f')
|
||||||
# Do this before installing sonic-utilities so that it doesn't attempt to install
|
# Do this before installing sonic-utilities so that it doesn't attempt to install
|
||||||
# an older version as part of its dependencies
|
# an older version as part of its dependencies
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
SONIC_PLATFORM_API_PY2 = mlnx_platform_api-1.0-py2-none-any.whl
|
SONIC_PLATFORM_API_PY2 = mlnx_platform_api-1.0-py2-none-any.whl
|
||||||
$(SONIC_PLATFORM_API_PY2)_SRC_PATH = $(PLATFORM_PATH)/mlnx-platform-api
|
$(SONIC_PLATFORM_API_PY2)_SRC_PATH = $(PLATFORM_PATH)/mlnx-platform-api
|
||||||
$(SONIC_PLATFORM_API_PY2)_PYTHON_VERSION = 2
|
$(SONIC_PLATFORM_API_PY2)_PYTHON_VERSION = 2
|
||||||
$(SONIC_PLATFORM_API_PY2)_DEPENDS = $(SONIC_PLATFORM_COMMON_PY2) $(SONIC_DAEMON_BASE_PY2) $(SONIC_CONFIG_ENGINE)
|
$(SONIC_PLATFORM_API_PY2)_DEPENDS = $(SONIC_PY_COMMON_PY2) $(SONIC_PLATFORM_COMMON_PY2) $(SONIC_DAEMON_BASE_PY2) $(SONIC_CONFIG_ENGINE)
|
||||||
SONIC_PYTHON_WHEELS += $(SONIC_PLATFORM_API_PY2)
|
SONIC_PYTHON_WHEELS += $(SONIC_PLATFORM_API_PY2)
|
||||||
|
|
||||||
export mlnx_platform_api_py2_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PLATFORM_API_PY2))"
|
export mlnx_platform_api_py2_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PLATFORM_API_PY2))"
|
||||||
|
@ -13,6 +13,7 @@ endif
|
|||||||
$(DOCKER_PLATFORM_MONITOR)_PYTHON_DEBS += $(SONIC_LEDD) $(SONIC_XCVRD) $(SONIC_PSUD) $(SONIC_SYSEEPROMD) $(SONIC_THERMALCTLD)
|
$(DOCKER_PLATFORM_MONITOR)_PYTHON_DEBS += $(SONIC_LEDD) $(SONIC_XCVRD) $(SONIC_PSUD) $(SONIC_SYSEEPROMD) $(SONIC_THERMALCTLD)
|
||||||
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PLATFORM_COMMON_PY2)
|
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PLATFORM_COMMON_PY2)
|
||||||
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SWSSSDK_PY2)
|
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SWSSSDK_PY2)
|
||||||
|
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY2)
|
||||||
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PLATFORM_API_PY2)
|
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_PLATFORM_API_PY2)
|
||||||
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_DAEMON_BASE_PY2)
|
$(DOCKER_PLATFORM_MONITOR)_PYTHON_WHEELS += $(SONIC_DAEMON_BASE_PY2)
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ $(DOCKER_SNMP)_DBG_DEPENDS += $(SNMP_DBG) $(SNMPD_DBG) $(LIBSNMP_DBG)
|
|||||||
|
|
||||||
$(DOCKER_SNMP)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES)
|
$(DOCKER_SNMP)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES)
|
||||||
|
|
||||||
$(DOCKER_SNMP)_PYTHON_WHEELS += $(SONIC_PLATFORM_COMMON_PY3) $(SWSSSDK_PY3) $(ASYNCSNMP_PY3)
|
$(DOCKER_SNMP)_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY3) $(SONIC_PLATFORM_COMMON_PY3) $(SWSSSDK_PY3) $(ASYNCSNMP_PY3)
|
||||||
$(DOCKER_SNMP)_LOAD_DOCKERS += $(DOCKER_CONFIG_ENGINE_BUSTER)
|
$(DOCKER_SNMP)_LOAD_DOCKERS += $(DOCKER_CONFIG_ENGINE_BUSTER)
|
||||||
|
|
||||||
SONIC_DOCKER_IMAGES += $(DOCKER_SNMP)
|
SONIC_DOCKER_IMAGES += $(DOCKER_SNMP)
|
||||||
|
12
rules/sonic-py-common.dep
Normal file
12
rules/sonic-py-common.dep
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
SPATH := $($(SONIC_PY_COMMON_PY2)_SRC_PATH)
|
||||||
|
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/sonic-py-common.mk rules/sonic-py-common.dep
|
||||||
|
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
|
||||||
|
DEP_FILES += $(shell git ls-files $(SPATH))
|
||||||
|
|
||||||
|
$(SONIC_PY_COMMON_PY2)_CACHE_MODE := GIT_CONTENT_SHA
|
||||||
|
$(SONIC_PY_COMMON_PY2)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
|
||||||
|
$(SONIC_PY_COMMON_PY2)_DEP_FILES := $(DEP_FILES)
|
||||||
|
|
||||||
|
$(SONIC_PY_COMMON_PY3)_CACHE_MODE := GIT_CONTENT_SHA
|
||||||
|
$(SONIC_PY_COMMON_PY3)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
|
||||||
|
$(SONIC_PY_COMMON_PY3)_DEP_FILES := $(DEP_FILES)
|
15
rules/sonic-py-common.mk
Normal file
15
rules/sonic-py-common.mk
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# SONIC_PY_COMMON_PY2 package
|
||||||
|
|
||||||
|
SONIC_PY_COMMON_PY2 = sonic_py_common-1.0-py2-none-any.whl
|
||||||
|
$(SONIC_PY_COMMON_PY2)_SRC_PATH = $(SRC_PATH)/sonic-py-common
|
||||||
|
$(SONIC_PY_COMMON_PY2)_DEPENDS += $(SWSSSDK_PY2)
|
||||||
|
$(SONIC_PY_COMMON_PY2)_PYTHON_VERSION = 2
|
||||||
|
SONIC_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY2)
|
||||||
|
|
||||||
|
# SONIC_PY_COMMON_PY3 package
|
||||||
|
|
||||||
|
SONIC_PY_COMMON_PY3 = sonic_py_common-1.0-py3-none-any.whl
|
||||||
|
$(SONIC_PY_COMMON_PY3)_SRC_PATH = $(SRC_PATH)/sonic-py-common
|
||||||
|
$(SONIC_PY_COMMON_PY3)_DEPENDS += $(SWSSSDK_PY3)
|
||||||
|
$(SONIC_PY_COMMON_PY3)_PYTHON_VERSION = 3
|
||||||
|
SONIC_PYTHON_WHEELS += $(SONIC_PY_COMMON_PY3)
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
SONIC_THERMALCTLD = python-sonic-thermalctld_1.0-1_all.deb
|
SONIC_THERMALCTLD = python-sonic-thermalctld_1.0-1_all.deb
|
||||||
$(SONIC_THERMALCTLD)_SRC_PATH = $(SRC_PATH)/sonic-platform-daemons/sonic-thermalctld
|
$(SONIC_THERMALCTLD)_SRC_PATH = $(SRC_PATH)/sonic-platform-daemons/sonic-thermalctld
|
||||||
$(SONIC_THERMALCTLD)_WHEEL_DEPENDS = $(SONIC_DAEMON_BASE_PY2)
|
$(SONIC_THERMALCTLD)_WHEEL_DEPENDS = $(SONIC_PY_COMMON_PY2) $(SONIC_DAEMON_BASE_PY2)
|
||||||
SONIC_PYTHON_STDEB_DEBS += $(SONIC_THERMALCTLD)
|
SONIC_PYTHON_STDEB_DEBS += $(SONIC_THERMALCTLD)
|
||||||
|
4
slave.mk
4
slave.mk
@ -805,6 +805,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \
|
|||||||
$$(addprefix $(FILES_PATH)/,$$($$*_FILES)) \
|
$$(addprefix $(FILES_PATH)/,$$($$*_FILES)) \
|
||||||
$(if $(findstring y,$(ENABLE_ZTP)),$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(SONIC_ZTP))) \
|
$(if $(findstring y,$(ENABLE_ZTP)),$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$(SONIC_ZTP))) \
|
||||||
$(addprefix $(PYTHON_DEBS_PATH)/,$(SONIC_UTILS)) \
|
$(addprefix $(PYTHON_DEBS_PATH)/,$(SONIC_UTILS)) \
|
||||||
|
$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PY_COMMON_PY2)) \
|
||||||
|
$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PY_COMMON_PY3)) \
|
||||||
$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_CONFIG_ENGINE)) \
|
$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_CONFIG_ENGINE)) \
|
||||||
$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PLATFORM_COMMON_PY2)) \
|
$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PLATFORM_COMMON_PY2)) \
|
||||||
$(addprefix $(PYTHON_WHEELS_PATH)/,$(REDIS_DUMP_LOAD_PY2)) \
|
$(addprefix $(PYTHON_WHEELS_PATH)/,$(REDIS_DUMP_LOAD_PY2)) \
|
||||||
@ -835,6 +837,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \
|
|||||||
export installer_debs="$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$($*_INSTALLS))"
|
export installer_debs="$(addprefix $(IMAGE_DISTRO_DEBS_PATH)/,$($*_INSTALLS))"
|
||||||
export lazy_installer_debs="$(foreach deb, $($*_LAZY_INSTALLS),$(foreach device, $($(deb)_PLATFORM),$(addprefix $(device)@, $(IMAGE_DISTRO_DEBS_PATH)/$(deb))))"
|
export lazy_installer_debs="$(foreach deb, $($*_LAZY_INSTALLS),$(foreach device, $($(deb)_PLATFORM),$(addprefix $(device)@, $(IMAGE_DISTRO_DEBS_PATH)/$(deb))))"
|
||||||
export installer_images="$(addprefix $(TARGET_PATH)/,$($*_DOCKERS))"
|
export installer_images="$(addprefix $(TARGET_PATH)/,$($*_DOCKERS))"
|
||||||
|
export sonic_py_common_py2_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PY_COMMON_PY2))"
|
||||||
|
export sonic_py_common_py3_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_PY_COMMON_PY3))"
|
||||||
export config_engine_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_CONFIG_ENGINE))"
|
export config_engine_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_CONFIG_ENGINE))"
|
||||||
export swsssdk_py2_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SWSSSDK_PY2))"
|
export swsssdk_py2_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SWSSSDK_PY2))"
|
||||||
export swsssdk_py3_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SWSSSDK_PY3))"
|
export swsssdk_py3_wheel_path="$(addprefix $(PYTHON_WHEELS_PATH)/,$(SWSSSDK_PY3))"
|
||||||
|
39
src/sonic-py-common/setup.py
Normal file
39
src/sonic-py-common/setup.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
'natsort',
|
||||||
|
'pyyaml',
|
||||||
|
'swsssdk>=2.0.1',
|
||||||
|
]
|
||||||
|
|
||||||
|
high_performance_deps = [
|
||||||
|
'swsssdk[high_perf]>=2.0.1',
|
||||||
|
]
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='sonic-py-common',
|
||||||
|
version='1.0',
|
||||||
|
description='Common Python libraries for SONiC',
|
||||||
|
license='Apache 2.0',
|
||||||
|
author='SONiC Team',
|
||||||
|
author_email='linuxnetdev@microsoft.com',
|
||||||
|
url='https://github.com/Azure/SONiC',
|
||||||
|
maintainer='Joe LeVeque',
|
||||||
|
maintainer_email='jolevequ@microsoft.com',
|
||||||
|
install_requires=dependencies,
|
||||||
|
extras_require={
|
||||||
|
'high_perf': high_performance_deps,
|
||||||
|
},
|
||||||
|
packages=[
|
||||||
|
'sonic_py_common',
|
||||||
|
],
|
||||||
|
classifiers=[
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Operating System :: Linux',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
],
|
||||||
|
keywords='SONiC sonic PYTHON python COMMON common',
|
||||||
|
)
|
||||||
|
|
0
src/sonic-py-common/sonic_py_common/__init__.py
Normal file
0
src/sonic-py-common/sonic_py_common/__init__.py
Normal file
81
src/sonic-py-common/sonic_py_common/daemon_base.py
Normal file
81
src/sonic-py-common/sonic_py_common/daemon_base.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import imp
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from . import device_info
|
||||||
|
from .logger import Logger
|
||||||
|
|
||||||
|
#
|
||||||
|
# Constants ====================================================================
|
||||||
|
#
|
||||||
|
REDIS_TIMEOUT_MSECS = 0
|
||||||
|
|
||||||
|
EEPROM_MODULE_NAME = 'eeprom'
|
||||||
|
EEPROM_CLASS_NAME = 'board'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Helper functions =============================================================
|
||||||
|
#
|
||||||
|
|
||||||
|
def db_connect(db_name):
|
||||||
|
from swsscommon import swsscommon
|
||||||
|
return swsscommon.DBConnector(db_name, REDIS_TIMEOUT_MSECS, True)
|
||||||
|
|
||||||
|
#
|
||||||
|
# DaemonBase ===================================================================
|
||||||
|
#
|
||||||
|
|
||||||
|
class DaemonBase(Logger):
|
||||||
|
def __init__(self, log_identifier):
|
||||||
|
super(DaemonBase, self).__init__(log_identifier, Logger.LOG_FACILITY_DAEMON)
|
||||||
|
|
||||||
|
# Register our default signal handlers, unless the signal already has a
|
||||||
|
# handler registered, most likely from a subclass implementation
|
||||||
|
if not signal.getsignal(signal.SIGHUP):
|
||||||
|
signal.signal(signal.SIGHUP, self.signal_handler)
|
||||||
|
if not signal.getsignal(signal.SIGINT):
|
||||||
|
signal.signal(signal.SIGINT, self.signal_handler)
|
||||||
|
if not signal.getsignal(signal.SIGTERM):
|
||||||
|
signal.signal(signal.SIGTERM, self.signal_handler)
|
||||||
|
|
||||||
|
# Default signal handler; can be overridden by subclass
|
||||||
|
def signal_handler(self, sig, frame):
|
||||||
|
if sig == signal.SIGHUP:
|
||||||
|
log_info("DaemonBase: Caught SIGHUP - ignoring...")
|
||||||
|
elif sig == signal.SIGINT:
|
||||||
|
log_info("DaemonBase: Caught SIGINT - exiting...")
|
||||||
|
sys.exit(128 + sig)
|
||||||
|
elif sig == signal.SIGTERM:
|
||||||
|
log_info("DaemonBase: Caught SIGTERM - exiting...")
|
||||||
|
sys.exit(128 + sig)
|
||||||
|
else:
|
||||||
|
log_warning("DaemonBase: Caught unhandled signal '{}'".format(sig))
|
||||||
|
|
||||||
|
# Loads platform specific platform 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) = device_info.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 as e:
|
||||||
|
raise IOError("Failed to load platform module '%s': %s" % (module_name, str(e)))
|
||||||
|
|
||||||
|
try:
|
||||||
|
platform_util_class = getattr(module, class_name)
|
||||||
|
# board class of eeprom requires 4 paramerters, need special treatment here.
|
||||||
|
if module_name == EEPROM_MODULE_NAME and class_name == EEPROM_CLASS_NAME:
|
||||||
|
platform_util = platform_util_class('','','','')
|
||||||
|
else:
|
||||||
|
platform_util = platform_util_class()
|
||||||
|
except AttributeError as e:
|
||||||
|
raise AttributeError("Failed to instantiate '%s' class: %s" % (class_name, str(e)))
|
||||||
|
|
||||||
|
return platform_util
|
||||||
|
|
||||||
|
# Runs daemon
|
||||||
|
def run(self):
|
||||||
|
raise NotImplementedError()
|
344
src/sonic-py-common/sonic_py_common/device_info.py
Normal file
344
src/sonic-py-common/sonic_py_common/device_info.py
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from natsort import natsorted
|
||||||
|
|
||||||
|
# TODD: Replace with swsscommon
|
||||||
|
from swsssdk import ConfigDBConnector, SonicDBConfig
|
||||||
|
|
||||||
|
USR_SHARE_SONIC_PATH = "/usr/share/sonic"
|
||||||
|
HOST_DEVICE_PATH = USR_SHARE_SONIC_PATH + "/device"
|
||||||
|
CONTAINER_PLATFORM_PATH = USR_SHARE_SONIC_PATH + "/platform"
|
||||||
|
|
||||||
|
MACHINE_CONF_PATH = "/host/machine.conf"
|
||||||
|
SONIC_VERSION_YAML_PATH = "/etc/sonic/sonic_version.yml"
|
||||||
|
|
||||||
|
# Port configuration file names
|
||||||
|
PORT_CONFIG_FILE = "port_config.ini"
|
||||||
|
PLATFORM_JSON_FILE = "platform.json"
|
||||||
|
|
||||||
|
# Multi-NPU constants
|
||||||
|
# TODO: Move Multi-ASIC-related functions and constants to a "multi_asic.py" module
|
||||||
|
NPU_NAME_PREFIX = "asic"
|
||||||
|
NAMESPACE_PATH_GLOB = "/run/netns/*"
|
||||||
|
ASIC_CONF_FILENAME = "asic.conf"
|
||||||
|
FRONTEND_ASIC_SUB_ROLE = "FrontEnd"
|
||||||
|
BACKEND_ASIC_SUB_ROLE = "BackEnd"
|
||||||
|
|
||||||
|
|
||||||
|
def get_machine_info():
|
||||||
|
"""
|
||||||
|
Retreives data from the machine configuration file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dictionary containing the key/value pairs as found in the machine
|
||||||
|
configuration file
|
||||||
|
"""
|
||||||
|
if not os.path.isfile(MACHINE_CONF_PATH):
|
||||||
|
return None
|
||||||
|
|
||||||
|
machine_vars = {}
|
||||||
|
with open(MACHINE_CONF_PATH) as machine_conf_file:
|
||||||
|
for line in machine_conf_file:
|
||||||
|
tokens = line.split('=')
|
||||||
|
if len(tokens) < 2:
|
||||||
|
continue
|
||||||
|
machine_vars[tokens[0]] = tokens[1].strip()
|
||||||
|
|
||||||
|
return machine_vars
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform():
|
||||||
|
"""
|
||||||
|
Retrieve the device's platform identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the device's platform identifier
|
||||||
|
"""
|
||||||
|
# First, attempt to retrieve the platform string from Config DB
|
||||||
|
config_db = ConfigDBConnector()
|
||||||
|
config_db.connect()
|
||||||
|
|
||||||
|
metadata = config_db.get_table('DEVICE_METADATA')
|
||||||
|
|
||||||
|
if 'localhost' in metadata and 'platform' in metadata['localhost']:
|
||||||
|
return metadata['localhost']['platform']
|
||||||
|
|
||||||
|
# If we were unable to retrieve the platform string from Config DB, attempt
|
||||||
|
# to retrieve it from the machine configuration file
|
||||||
|
machine_info = get_machine_info()
|
||||||
|
if machine_info:
|
||||||
|
if machine_info.has_key('onie_platform'):
|
||||||
|
return machine_info['onie_platform']
|
||||||
|
elif machine_info.has_key('aboot_platform'):
|
||||||
|
return machine_info['aboot_platform']
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_hwsku():
|
||||||
|
"""
|
||||||
|
Retrieve the device's hardware SKU identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the device's hardware SKU identifier
|
||||||
|
"""
|
||||||
|
config_db = ConfigDBConnector()
|
||||||
|
config_db.connect()
|
||||||
|
|
||||||
|
metadata = config_db.get_table('DEVICE_METADATA')
|
||||||
|
|
||||||
|
if 'localhost' in metadata and 'hwsku' in metadata['localhost']:
|
||||||
|
return metadata['localhost']['hwsku']
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_and_hwsku():
|
||||||
|
"""
|
||||||
|
Convenience function which retrieves both the device's platform identifier
|
||||||
|
and hardware SKU identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A tuple of two strings, the first containing the device's
|
||||||
|
platform identifier, the second containing the device's
|
||||||
|
hardware SKU identifier
|
||||||
|
"""
|
||||||
|
platform = get_platform()
|
||||||
|
hwsku = get_hwsku()
|
||||||
|
|
||||||
|
return (platform, hwsku)
|
||||||
|
|
||||||
|
|
||||||
|
def get_asic_conf_file_path():
|
||||||
|
"""
|
||||||
|
Retrieves the path to the ASIC conguration file on the device
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the path to the ASIC conguration file on success,
|
||||||
|
None on failure
|
||||||
|
"""
|
||||||
|
asic_conf_path_candidates = []
|
||||||
|
|
||||||
|
asic_conf_path_candidates.append(os.path.join(CONTAINER_PLATFORM_PATH, ASIC_CONF_FILENAME))
|
||||||
|
|
||||||
|
platform = get_platform()
|
||||||
|
if platform:
|
||||||
|
asic_conf_path_candidates.append(os.path.join(HOST_DEVICE_PATH, platform, ASIC_CONF_FILENAME))
|
||||||
|
|
||||||
|
for asic_conf_file_path in asic_conf_path_candidates:
|
||||||
|
if os.path.isfile(asic_conf_file_path):
|
||||||
|
return asic_conf_file_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_paths_to_platform_and_hwsku_dirs():
|
||||||
|
"""
|
||||||
|
Retreives the paths to the device's platform and hardware SKU data
|
||||||
|
directories
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A tuple of two strings, the first containing the path to the platform
|
||||||
|
directory of the device, the second containing the path to the hardware
|
||||||
|
SKU directory of the device
|
||||||
|
"""
|
||||||
|
# Get platform and hwsku
|
||||||
|
(platform, hwsku) = get_platform_and_hwsku()
|
||||||
|
|
||||||
|
# Determine whether we're running in a container or on the host
|
||||||
|
platform_path_host = os.path.join(HOST_DEVICE_PATH, platform)
|
||||||
|
|
||||||
|
if os.path.isdir(CONTAINER_PLATFORM_PATH):
|
||||||
|
platform_path = CONTAINER_PLATFORM_PATH
|
||||||
|
elif os.path.isdir(platform_path_host):
|
||||||
|
platform_path = platform_path_host
|
||||||
|
else:
|
||||||
|
raise OSError("Failed to locate platform directory")
|
||||||
|
|
||||||
|
hwsku_path = os.path.join(platform_path, hwsku)
|
||||||
|
|
||||||
|
return (platform_path, hwsku_path)
|
||||||
|
|
||||||
|
|
||||||
|
def get_path_to_port_config_file():
|
||||||
|
"""
|
||||||
|
Retrieves the path to the device's port configuration file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the path the the device's port configuration file
|
||||||
|
"""
|
||||||
|
# Get platform and hwsku path
|
||||||
|
(platform_path, hwsku_path) = get_paths_to_platform_and_hwsku_dirs()
|
||||||
|
|
||||||
|
# First check for the presence of the new 'platform.json' file
|
||||||
|
port_config_file_path = os.path.join(platform_path, PLATFORM_JSON_FILE)
|
||||||
|
if not os.path.isfile(port_config_file_path):
|
||||||
|
# platform.json doesn't exist. Try loading the legacy 'port_config.ini' file
|
||||||
|
port_config_file_path = os.path.join(hwsku_path, PORT_CONFIG_FILE)
|
||||||
|
if not os.path.isfile(port_config_file_path):
|
||||||
|
raise OSError("Failed to detect port config file: {}".format(port_config_file_path))
|
||||||
|
|
||||||
|
return port_config_file_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_sonic_version_info():
|
||||||
|
if not os.path.isfile(SONIC_VERSION_YAML_PATH):
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
with open(SONIC_VERSION_YAML_PATH) as stream:
|
||||||
|
if yaml.__version__ >= "5.1":
|
||||||
|
data = yaml.full_load(stream)
|
||||||
|
else:
|
||||||
|
data = yaml.load(stream)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Multi-NPU functionality
|
||||||
|
#
|
||||||
|
|
||||||
|
def get_num_npus():
|
||||||
|
asic_conf_file_path = get_asic_conf_file_path()
|
||||||
|
if asic_conf_file_path is None:
|
||||||
|
return 1
|
||||||
|
with open(asic_conf_file_path) as asic_conf_file:
|
||||||
|
for line in asic_conf_file:
|
||||||
|
tokens = line.split('=')
|
||||||
|
if len(tokens) < 2:
|
||||||
|
continue
|
||||||
|
if tokens[0].lower() == 'num_asic':
|
||||||
|
num_npus = tokens[1].strip()
|
||||||
|
return int(num_npus)
|
||||||
|
|
||||||
|
|
||||||
|
def is_multi_npu():
|
||||||
|
num_npus = get_num_npus()
|
||||||
|
return (num_npus > 1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_npu_id_from_name(npu_name):
|
||||||
|
if npu_name.startswith(NPU_NAME_PREFIX):
|
||||||
|
return npu_name[len(NPU_NAME_PREFIX):]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_namespaces():
|
||||||
|
"""
|
||||||
|
In a multi NPU platform, each NPU is in a Linux Namespace.
|
||||||
|
This method returns list of all the Namespace present on the device
|
||||||
|
"""
|
||||||
|
ns_list = []
|
||||||
|
for path in glob.glob(NAMESPACE_PATH_GLOB):
|
||||||
|
ns = os.path.basename(path)
|
||||||
|
ns_list.append(ns)
|
||||||
|
return natsorted(ns_list)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_namespaces():
|
||||||
|
"""
|
||||||
|
In case of Multi-Asic platform, Each ASIC will have a linux network namespace created.
|
||||||
|
So we loop through the databases in different namespaces and depending on the sub_role
|
||||||
|
decide whether this is a front end ASIC/namespace or a back end one.
|
||||||
|
"""
|
||||||
|
front_ns = []
|
||||||
|
back_ns = []
|
||||||
|
num_npus = get_num_npus()
|
||||||
|
SonicDBConfig.load_sonic_global_db_config()
|
||||||
|
|
||||||
|
if is_multi_npu():
|
||||||
|
for npu in range(num_npus):
|
||||||
|
namespace = "{}{}".format(NPU_NAME_PREFIX, npu)
|
||||||
|
config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
|
||||||
|
config_db.connect()
|
||||||
|
|
||||||
|
metadata = config_db.get_table('DEVICE_METADATA')
|
||||||
|
if metadata['localhost']['sub_role'] == FRONTEND_ASIC_SUB_ROLE:
|
||||||
|
front_ns.append(namespace)
|
||||||
|
elif metadata['localhost']['sub_role'] == BACKEND_ASIC_SUB_ROLE:
|
||||||
|
back_ns.append(namespace)
|
||||||
|
|
||||||
|
return {'front_ns':front_ns, 'back_ns':back_ns}
|
||||||
|
|
||||||
|
|
||||||
|
def _valid_mac_address(mac):
|
||||||
|
return bool(re.match("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", mac))
|
||||||
|
|
||||||
|
|
||||||
|
def get_system_mac(namespace=None):
|
||||||
|
version_info = get_sonic_version_info()
|
||||||
|
|
||||||
|
if (version_info['asic_type'] == 'mellanox'):
|
||||||
|
# With Mellanox ONIE release(2019.05-5.2.0012) and above
|
||||||
|
# "onie_base_mac" was added to /host/machine.conf:
|
||||||
|
# onie_base_mac=e4:1d:2d:44:5e:80
|
||||||
|
# So we have another way to get the mac address besides decode syseeprom
|
||||||
|
# By this can mitigate the dependency on the hw-management service
|
||||||
|
base_mac_key = "onie_base_mac"
|
||||||
|
machine_vars = get_machine_info()
|
||||||
|
if machine_vars is not None and base_mac_key in machine_vars:
|
||||||
|
mac = machine_vars[base_mac_key]
|
||||||
|
mac = mac.strip()
|
||||||
|
if _valid_mac_address(mac):
|
||||||
|
return mac
|
||||||
|
|
||||||
|
hw_mac_entry_cmds = [ "sudo decode-syseeprom -m" ]
|
||||||
|
elif (version_info['asic_type'] == 'marvell'):
|
||||||
|
# Try valid mac in eeprom, else fetch it from eth0
|
||||||
|
platform = get_platform()
|
||||||
|
hwsku = get_hwsku()
|
||||||
|
profile_cmd = 'cat' + HOST_DEVICE_PATH + '/' + platform +'/'+ hwsku +'/profile.ini | grep switchMacAddress | cut -f2 -d='
|
||||||
|
hw_mac_entry_cmds = [ profile_cmd, "sudo decode-syseeprom -m", "ip link show eth0 | grep ether | awk '{print $2}'" ]
|
||||||
|
else:
|
||||||
|
mac_address_cmd = "cat /sys/class/net/eth0/address"
|
||||||
|
if namespace is not None:
|
||||||
|
mac_address_cmd = "sudo ip netns exec {} {}".format(namespace, mac_address_cmd)
|
||||||
|
|
||||||
|
hw_mac_entry_cmds = [mac_address_cmd]
|
||||||
|
|
||||||
|
for get_mac_cmd in hw_mac_entry_cmds:
|
||||||
|
proc = subprocess.Popen(get_mac_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
(mac, err) = proc.communicate()
|
||||||
|
if err:
|
||||||
|
continue
|
||||||
|
mac = mac.strip()
|
||||||
|
if _valid_mac_address(mac):
|
||||||
|
break
|
||||||
|
|
||||||
|
if not _valid_mac_address(mac):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Align last byte of MAC if necessary
|
||||||
|
if version_info and version_info['asic_type'] == 'centec':
|
||||||
|
last_byte = mac[-2:]
|
||||||
|
aligned_last_byte = format(int(int(last_byte, 16) + 1), '02x')
|
||||||
|
mac = mac[:-2] + aligned_last_byte
|
||||||
|
return mac
|
||||||
|
|
||||||
|
|
||||||
|
def get_system_routing_stack():
|
||||||
|
"""
|
||||||
|
Retrieves the routing stack being utilized on this device
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A string containing the name of the routing stack in use on the device
|
||||||
|
"""
|
||||||
|
command = "sudo docker ps | grep bgp | awk '{print$2}' | cut -d'-' -f3 | cut -d':' -f1"
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
shell=True,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
stdout = proc.communicate()[0]
|
||||||
|
proc.wait()
|
||||||
|
result = stdout.rstrip('\n')
|
||||||
|
except OSError as e:
|
||||||
|
raise OSError("Cannot detect routing stack")
|
||||||
|
|
||||||
|
return result
|
105
src/sonic-py-common/sonic_py_common/logger.py
Normal file
105
src/sonic-py-common/sonic_py_common/logger.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import os
|
||||||
|
import syslog
|
||||||
|
|
||||||
|
"""
|
||||||
|
Logging functionality for SONiC Python applications
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Logger(object):
|
||||||
|
"""
|
||||||
|
Logger class for SONiC Python applications
|
||||||
|
"""
|
||||||
|
LOG_FACILITY_USER = syslog.LOG_USER
|
||||||
|
LOG_FACILITY_DAEMON = syslog.LOG_DAEMON
|
||||||
|
|
||||||
|
LOG_PRIORITY_ERROR = syslog.LOG_ERR
|
||||||
|
LOG_PRIORITY_WARNING = syslog.LOG_WARNING
|
||||||
|
LOG_PRIORITY_NOTICE = syslog.LOG_NOTICE
|
||||||
|
LOG_PRIORITY_INFO = syslog.LOG_INFO
|
||||||
|
LOG_PRIORITY_DEBUG = syslog.LOG_DEBUG
|
||||||
|
|
||||||
|
def __init__(self, log_identifier=None, log_facility=LOG_FACILITY_USER):
|
||||||
|
self.syslog = syslog
|
||||||
|
|
||||||
|
if not log_identifier:
|
||||||
|
log_identifier = os.path.basename(sys.argv[0])
|
||||||
|
|
||||||
|
self.syslog.openlog(ident=log_identifier,
|
||||||
|
logoption=(syslog.LOG_PID | syslog.LOG_NDELAY),
|
||||||
|
facility=log_facility)
|
||||||
|
|
||||||
|
# Set the default minimum log priority to LOG_PRIORITY_NOTICE
|
||||||
|
self.set_min_log_priority(self.LOG_PRIORITY_NOTICE)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.syslog.closelog()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Methods for setting minimum log priority
|
||||||
|
#
|
||||||
|
|
||||||
|
def set_min_log_priority(self, priority):
|
||||||
|
"""
|
||||||
|
Sets the minimum log priority level to <priority>. All log messages
|
||||||
|
with a priority lower than <priority> will not be logged
|
||||||
|
|
||||||
|
Args:
|
||||||
|
priority: The minimum priority at which to log messages
|
||||||
|
"""
|
||||||
|
self.syslog.setlogmask(self.syslog.LOG_UPTO(priority))
|
||||||
|
|
||||||
|
def set_min_log_priority_error(self):
|
||||||
|
"""
|
||||||
|
Convenience function to set minimum log priority to LOG_PRIORITY_ERROR
|
||||||
|
"""
|
||||||
|
self.set_min_log_priority(self.LOG_PRIORITY_ERROR)
|
||||||
|
|
||||||
|
def set_min_log_priority_warning(self):
|
||||||
|
"""
|
||||||
|
Convenience function to set minimum log priority to LOG_PRIORITY_WARNING
|
||||||
|
"""
|
||||||
|
self.set_min_log_priority(self.LOG_PRIORITY_WARNING)
|
||||||
|
|
||||||
|
def set_min_log_priority_notice(self):
|
||||||
|
"""
|
||||||
|
Convenience function to set minimum log priority to LOG_PRIORITY_NOTICE
|
||||||
|
"""
|
||||||
|
self.set_min_log_priority(self.LOG_PRIORITY_NOTICE)
|
||||||
|
|
||||||
|
def set_min_log_priority_info(self):
|
||||||
|
"""
|
||||||
|
Convenience function to set minimum log priority to LOG_PRIORITY_INFO
|
||||||
|
"""
|
||||||
|
self.set_min_log_priority(self.LOG_PRIORITY_INFO)
|
||||||
|
|
||||||
|
def set_min_log_priority_debug(self):
|
||||||
|
"""
|
||||||
|
Convenience function to set minimum log priority to LOG_PRIORITY_DEBUG
|
||||||
|
"""
|
||||||
|
self.set_min_log_priority(self.LOG_PRIORITY_DEBUG)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Methods for logging messages
|
||||||
|
#
|
||||||
|
|
||||||
|
def log(self, priority, msg, also_print_to_console=False):
|
||||||
|
self.syslog.syslog(priority, msg)
|
||||||
|
|
||||||
|
if also_print_to_console:
|
||||||
|
print(msg)
|
||||||
|
|
||||||
|
def log_error(self, msg, also_print_to_console=False):
|
||||||
|
self.log(self.LOG_PRIORITY_ERROR, msg, also_print_to_console)
|
||||||
|
|
||||||
|
def log_warning(self, msg, also_print_to_console=False):
|
||||||
|
self.log(self.LOG_PRIORITY_WARNING, msg, also_print_to_console)
|
||||||
|
|
||||||
|
def log_notice(self, msg, also_print_to_console=False):
|
||||||
|
self.log(self.LOG_PRIORITY_NOTICE, msg, also_print_to_console)
|
||||||
|
|
||||||
|
def log_info(self, msg, also_print_to_console=False):
|
||||||
|
self.log(self.LOG_PRIORITY_INFO, msg, also_print_to_console)
|
||||||
|
|
||||||
|
def log_debug(self, msg, also_print_to_console=False):
|
||||||
|
self.log(self.LOG_PRIORITY_DEBUG, msg, also_print_to_console)
|
Loading…
Reference in New Issue
Block a user