diff --git a/clear/main.py b/clear/main.py index f7e5d715ed0e..4302ae00aab1 100755 --- a/clear/main.py +++ b/clear/main.py @@ -5,6 +5,10 @@ import click +from utilities_common import util_base + +from . import plugins + # This is from the aliases example: # https://github.com/pallets/click/blob/57c6f09611fc47ca80db0bd010f05998b3c0aa95/examples/aliases/aliases.py @@ -120,7 +124,6 @@ def cli(): """SONiC command line - 'Clear' command""" pass - # # 'ip' group ### # @@ -446,5 +449,12 @@ def translations(): cmd = "natclear -t" run_command(cmd) + +# Load plugins and register them +helper = util_base.UtilHelper() +for plugin in helper.load_plugins(plugins): + helper.register_plugin(plugin, cli) + + if __name__ == '__main__': cli() diff --git a/clear/plugins/__init__.py b/clear/plugins/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/config/main.py b/config/main.py index d27562bd4eec..8548dfce8fb8 100644 --- a/config/main.py +++ b/config/main.py @@ -16,6 +16,7 @@ from portconfig import get_child_ports from sonic_py_common import device_info, multi_asic from sonic_py_common.interface import get_interface_table_name, get_port_table_name +from utilities_common import util_base from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector, SonicDBConfig from utilities_common.db import Db from utilities_common.intf_filter import parse_interface_in_filter @@ -28,11 +29,11 @@ from . import feature from . import kdump from . import kube -from . import mlnx from . import muxcable from . import nat from . import vlan from . import vxlan +from . import plugins from .config_mgmt import ConfigMgmtDPB # mock masic APIs for unit test @@ -849,9 +850,6 @@ def config(ctx): except (KeyError, TypeError): raise click.Abort() - if asic_type == 'mellanox': - platform.add_command(mlnx.mlnx) - # Load the global config file database_global.json once. num_asic = multi_asic.get_num_asics() if num_asic > 1: @@ -4415,5 +4413,12 @@ def delete(ctx): sflow_tbl['global'].pop('agent_id') config_db.set_entry('SFLOW', 'global', sflow_tbl['global']) + +# Load plugins and register them +helper = util_base.UtilHelper() +for plugin in helper.load_plugins(plugins): + helper.register_plugin(plugin, config) + + if __name__ == '__main__': config() diff --git a/config/plugins/__init__.py b/config/plugins/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/config/mlnx.py b/config/plugins/mlnx.py similarity index 96% rename from config/mlnx.py rename to config/plugins/mlnx.py index c8c9cfb0c238..0883aa579911 100644 --- a/config/mlnx.py +++ b/config/plugins/mlnx.py @@ -11,6 +11,7 @@ import click from sonic_py_common import logger + from sonic_py_common import device_info import utilities_common.cli as clicommon except ImportError as e: raise ImportError("%s - required module not found" % str(e)) @@ -229,5 +230,10 @@ def sdk_sniffer_disable(): # pass +def register(cli): + version_info = device_info.get_sonic_version_info() + if (version_info and version_info.get('asic_type') == 'mellanox'): + cli.commands['platform'].add_command(mlnx) + if __name__ == '__main__': sniffer() diff --git a/setup.py b/setup.py index 8018efd82cb3..7df46084031a 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,9 @@ packages=[ 'acl_loader', 'clear', + 'clear.plugins', 'config', + 'config.plugins', 'connect', 'consutil', 'counterpoll', @@ -42,6 +44,7 @@ 'pddf_ledutil', 'show', 'show.interfaces', + 'show.plugins', 'sonic_installer', 'sonic_installer.bootloader', 'tests', diff --git a/show/main.py b/show/main.py old mode 100644 new mode 100755 index 8e9ee4bcea06..8dbe740e716e --- a/show/main.py +++ b/show/main.py @@ -11,6 +11,7 @@ from sonic_py_common import device_info, multi_asic from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector from tabulate import tabulate +from utilities_common import util_base from utilities_common.db import Db from . import acl @@ -23,7 +24,6 @@ from . import interfaces from . import kdump from . import kube -from . import mlnx from . import muxcable from . import nat from . import platform @@ -35,6 +35,7 @@ from . import vxlan from . import system_health from . import warm_restart +from . import plugins # Global Variables @@ -1413,5 +1414,12 @@ def ztp(status, verbose): cmd = cmd + " --verbose" run_command(cmd, display_cmd=verbose) + +# Load plugins and register them +helper = util_base.UtilHelper() +for plugin in helper.load_plugins(plugins): + helper.register_plugin(plugin, cli) + + if __name__ == '__main__': cli() diff --git a/show/platform.py b/show/platform.py index 029e28f485fa..7e8f8f444c5d 100644 --- a/show/platform.py +++ b/show/platform.py @@ -33,12 +33,6 @@ def platform(): pass -version_info = device_info.get_sonic_version_info() -if (version_info and version_info.get('asic_type') == 'mellanox'): - from . import mlnx - platform.add_command(mlnx.mlnx) - - # 'summary' subcommand ("show platform summary") @platform.command() @click.option('--json', is_flag=True, help="Output in JSON format") diff --git a/show/plugins/__init__.py b/show/plugins/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/show/mlnx.py b/show/plugins/mlnx.py similarity index 94% rename from show/mlnx.py rename to show/plugins/mlnx.py index fa887cf99928..790784fd50a2 100644 --- a/show/mlnx.py +++ b/show/plugins/mlnx.py @@ -10,6 +10,7 @@ import subprocess import click import xml.etree.ElementTree as ET + from sonic_py_common import device_info except ImportError as e: raise ImportError("%s - required module not found" % str(e)) @@ -137,3 +138,8 @@ def issu_status(): click.echo('ISSU is enabled' if res else 'ISSU is disabled') + +def register(cli): + version_info = device_info.get_sonic_version_info() + if (version_info and version_info.get('asic_type') == 'mellanox'): + cli.commands['platform'].add_command(mlnx) diff --git a/utilities_common/util_base.py b/utilities_common/util_base.py index 18da93de7ae1..d32e2dbf70a5 100644 --- a/utilities_common/util_base.py +++ b/utilities_common/util_base.py @@ -1,17 +1,51 @@ - import os -import sonic_platform +import pkgutil +import importlib + +from sonic_py_common import logger # Constants ==================================================================== PDDF_SUPPORT_FILE = '/usr/share/sonic/platform/pddf_support' # Helper classs +log = logger.Logger() + class UtilHelper(object): def __init__(self): pass + def load_plugins(self, plugins_namespace): + """ Discover and load CLI plugins. Yield a plugin module. """ + + def iter_namespace(ns_pkg): + return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + ".") + + for _, module_name, ispkg in iter_namespace(plugins_namespace): + if ispkg: + continue + log.log_debug('importing plugin: {}'.format(module_name)) + try: + module = importlib.import_module(module_name) + except Exception as err: + log.log_error('failed to import plugin {}: {}'.format(module_name, err), + also_print_to_console=True) + continue + + yield module + + def register_plugin(self, plugin, root_command): + """ Register plugin in top-level command root_command. """ + + name = plugin.__name__ + log.log_debug('registering plugin: {}'.format(name)) + try: + plugin.register(root_command) + except Exception as err: + log.log_error('failed to import plugin {}: {}'.format(name, err), + also_print_to_console=True) + # try get information from platform API and return a default value if caught NotImplementedError def try_get(self, callback, default=None): """ @@ -35,6 +69,7 @@ def load_platform_chassis(self): # Load 2.0 platform API chassis class try: + import sonic_platform chassis = sonic_platform.platform.Platform().get_chassis() except Exception as e: raise Exception("Failed to load chassis due to {}".format(repr(e))) @@ -47,3 +82,4 @@ def check_pddf_mode(self): return True else: return False +