Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce sonic-py-common package #5003

Merged
merged 25 commits into from
Jul 27, 2020
Merged

Introduce sonic-py-common package #5003

merged 25 commits into from
Jul 27, 2020

Conversation

jleveque
Copy link
Contributor

@jleveque jleveque commented Jul 18, 2020

- Why I did it
To consolidate common SONiC Python-language functionality into one shared package and eliminate duplicate code. This is the first step toward resolving #4999

The package currently includes three modules:

  • daemon_base
  • device_info
  • logger

- How I did it
Add a new Python package, "sonic-py-common". This will replace the sonic-daemon-base package as well as the sonic_device_util.py file in sonic-config-engine.

I am building both Python 2 and Python 3 versions of the package. The Python 2 package is for backward-compatibility with existing Python 2 code, and the Python 3 version is for use in the SNMP container as well as to prepare for the eventual migration of all Python code to Python 3, since Python 2 is no longer supported as of January 1, 2020.

I think that db_connect() and load_platform_util() should also be moved out of daemon_base.py and into a separate module, but I opted against it for this PR, just to make the initial transition from sonic-daemon-base to sonic-py-common more straightforward. This can be taken care of in a future refactor.

- How to verify it
Test using the following script (replace python2 with python3 in shebang to test Python 3 package):

#!/usr/bin/env python2

from sonic_py_common import device_info, logger, daemon_base

print("machine_info: {}".format(device_info.get_machine_info()))
print("asic_cfg_path: {}".format(device_info.get_asic_conf_file_path()))
print("platform: {}".format(device_info.get_platform()))
print("hwsku: {}".format(device_info.get_hwsku()))

plat, sku = device_info.get_platform_and_hwsku()
print("plat: {}, sku: {}".format(plat, sku))

plat_path, sku_path = device_info.get_paths_to_platform_and_hwsku_dirs()
print("plat_path: {}, sku_path: {}".format(plat_path, sku_path))

print("port_config_path: {}".format(device_info.get_path_to_port_config_file()))
print("version_info: {}".format(device_info.get_sonic_version_info()))
print("num_npus: {}".format(device_info.get_num_npus()))
print("is_multi_npu: {}".format(device_info.is_multi_npu()))
print("namespaces: {}".format(device_info.get_namespaces()))
print("all_namespaces: {}".format(device_info.get_all_namespaces()))

my_logger = logger.Logger("test")
my_logger.log_error("Test error message; Minimum log priority = PRIORITY_NOTICE")
my_logger.log_warning("Test warning message; Minimum log priority = PRIORITY_NOTICE")
my_logger.log_notice("Test notice message; Minimum log priority = PRIORITY_NOTICE")
# The following SHOULDN'T get logged
my_logger.log_info("Test info message; Minimum log priority = PRIORITY_NOTICE")
my_logger.log_debug("Test debug message; Minimum log priority = PRIORITY_NOTICE")

my_logger.set_min_log_priority_info()
my_logger.log_error("Test error message; Minimum log priority = PRIORITY_INFO")
my_logger.log_warning("Test warning message; Minimum log priority = PRIORITY_INFO")
my_logger.log_notice("Test notice message; Minimum log priority = PRIORITY_INFO")
my_logger.log_info("Test info message; Minimum log priority = PRIORITY_INFO")
# The following SHOULDN'T get logged
my_logger.log_debug("Test debug message; Minimum log priority = PRIORITY_INFO")

my_logger.set_min_log_priority_debug()
my_logger.log_error("Test error message; Minimum log priority = PRIORITY_DEBUG")
my_logger.log_warning("Test warning message; Minimum log priority = PRIORITY_DEBUG")
my_logger.log_notice("Test notice message; Minimum log priority = PRIORITY_DEBUG")
my_logger.log_info("Test info message; Minimum log priority = PRIORITY_DEBUG")
my_logger.log_debug("Test debug message; Minimum log priority = PRIORITY_DEBUG")


class MyDaemon(daemon_base.DaemonBase):
    def run(self):
        self.log_error("Test error message; Minimum log priority = PRIORITY_NOTICE")
        self.log_warning("Test warning message; Minimum log priority = PRIORITY_NOTICE")
        self.log_notice("Test notice message; Minimum log priority = PRIORITY_NOTICE")
        # The following SHOULDN'T get logged
        self.log_info("Test info message; Minimum log priority = PRIORITY_NOTICE")
        self.log_debug("Test debug message; Minimum log priority = PRIORITY_NOTICE")

        self.set_min_log_priority_info()
        self.log_error("Test error message; Minimum log priority = PRIORITY_INFO")
        self.log_warning("Test warning message; Minimum log priority = PRIORITY_INFO")
        self.log_notice("Test notice message; Minimum log priority = PRIORITY_INFO")
        self.log_info("Test info message; Minimum log priority = PRIORITY_INFO")
        # The following SHOULDN'T get logged
        self.log_debug("Test debug message; Minimum log priority = PRIORITY_INFO")

        self.set_min_log_priority_debug()
        self.log_error("Test error message; Minimum log priority = PRIORITY_DEBUG")
        self.log_warning("Test warning message; Minimum log priority = PRIORITY_DEBUG")
        self.log_notice("Test notice message; Minimum log priority = PRIORITY_DEBUG")
        self.log_info("Test info message; Minimum log priority = PRIORITY_DEBUG")
        self.log_debug("Test debug message; Minimum log priority = PRIORITY_DEBUG")

my_daemon = MyDaemon("test_daemon")
my_daemon.run()

- Description for the changelog
Consolidate common SONiC Python-language functionality into one shared package (sonic-py-common) and eliminate duplicate code.

- Next Steps

  1. Migrate all Python applications in sonic-buildimage from sonic-daemon-base and sonic_device_util to sonic-py-common
  2. Migrate all Python applications in submodules from sonic-daemon-base and sonic_device_util to sonic-py-common
  3. Remove sonic-daemon-base and sonic_device_util entirely
  4. Migrate common code from sonic-utilities into sonic-py-common
  5. Add regression tests for sonic--py-common to sonic-mgmt repo

yxieca
yxieca previously approved these changes Jul 21, 2020
Logging functionality for SONiC Python applications
"""

FACILITY_USER = syslog.LOG_USER
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO there is an issue in current logger implementation, there is no log level control, by default all the debug log will be printed out, if we can have some way to control the log level it would be great.

Copy link
Contributor Author

@jleveque jleveque Jul 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing attention to this. I can add that to this PR fairly easily. What should we set as the default minimum log level -- NOTICE? INFO?

Copy link
Contributor Author

@jleveque jleveque Jul 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a new commit which adds a new method, set_min_log_priority(), and sets the default minimum priority to NOTICE. We can change the default to INFO if there's a consensus to change it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is Great Joe.

Copy link
Collaborator

@zhenggen-xu zhenggen-xu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this will become the common functions, I think we should have automation test cases (probably vs tests) to cover them. Should we consider to add something similar to the tests cases in your PR description to be automated?

def log(self, priority, msg, also_print_to_console=False):
self.syslog.syslog(priority, msg)

if also_print_to_console:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check log level (priority) here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I think it's debatable. In the existing implementation of the Logger class, we do not. So I say for now we keep it as-is. If we decide we want to at a later time, we can add the checks.

@jleveque
Copy link
Contributor Author

As this will become the common functions, I think we should have automation test cases (probably vs tests) to cover them. Should we consider to add something similar to the tests cases in your PR description to be automated?

Absolutely. I have now explicitly added this to the "Next Steps" in the description as well as in issue #4999

Copy link
Collaborator

@lguohan lguohan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@jleveque
Copy link
Contributor Author

Removed "Request for 201911 Branch" label, as backporting this change and the related changes will most likely be better performed by creating new PRs against the 201911 branch.

jleveque added a commit that referenced this pull request Aug 3, 2020
…ge (#5043)

As part of consolidating all common Python-based functionality into the new sonic-py-common package, this pull request:
1. Redirects all Python applications/scripts in sonic-buildimage repo which previously imported sonic_device_util or sonic_daemon_base to instead import sonic-py-common, which was added in #5003
2. Replaces all calls to `sonic_device_util.get_platform_info()` to instead call `sonic_py_common.get_platform()` and removes any calls to `sonic_device_util.get_machine_info()` which are no longer necessary (i.e., those which were only used to pass the results to `sonic_device_util.get_platform_info()`.
3. Removes unused imports to the now-deprecated sonic-daemon-base package and sonic_device_util.py module

This is the next step toward resolving #4999

Also reverted my previous change in which device_info.get_platform() would first try obtaining the platform ID string from Config DB and fall back to gathering it from machine.conf upon failure because this function is called by sonic-cfggen before the data is in the DB, in which case, the db_connect() call will hang indefinitely, which was not the behavior I expected. As of now, the function will always reference machine.conf.
jleveque added a commit that referenced this pull request Aug 3, 2020
Consolidate common SONiC Python-language functionality into one shared package (sonic-py-common) and eliminate duplicate code.

The package currently includes four modules:
- daemon_base
- device_info
- logger
- task_base

NOTE: This is a combination of all changes from #5003, #5049 and some changes from #5043 backported to align with the 201911 branch. As part of the 201911 port, I am not installing the Python 3 package in the base image or in the VS container, because we do not have pip3 installed, and we do not intend to migrate to Python 3 in 201911.
santhosh-kt pushed a commit to santhosh-kt/sonic-buildimage that referenced this pull request Feb 25, 2021
…ge (sonic-net#5043)

As part of consolidating all common Python-based functionality into the new sonic-py-common package, this pull request:
1. Redirects all Python applications/scripts in sonic-buildimage repo which previously imported sonic_device_util or sonic_daemon_base to instead import sonic-py-common, which was added in sonic-net#5003
2. Replaces all calls to `sonic_device_util.get_platform_info()` to instead call `sonic_py_common.get_platform()` and removes any calls to `sonic_device_util.get_machine_info()` which are no longer necessary (i.e., those which were only used to pass the results to `sonic_device_util.get_platform_info()`.
3. Removes unused imports to the now-deprecated sonic-daemon-base package and sonic_device_util.py module

This is the next step toward resolving sonic-net#4999

Also reverted my previous change in which device_info.get_platform() would first try obtaining the platform ID string from Config DB and fall back to gathering it from machine.conf upon failure because this function is called by sonic-cfggen before the data is in the DB, in which case, the db_connect() call will hang indefinitely, which was not the behavior I expected. As of now, the function will always reference machine.conf.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants