From e85552958a035cad3a0857ece631f405e6e6e303 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 9 Sep 2020 21:32:22 -0700 Subject: [PATCH 01/12] Chassisd to monitor status of cards on chassis Support of monitoring of line-cards and fabric-cards. This runs in the main thread periodically. Support of handling configuration of moving the cards to administratively up/down state. The handling happens as part of a separate thread that waits on select() for config event. --- sonic-chassisd/scripts/chassisd | 232 ++++++++++++++++++++++++++++++++ sonic-chassisd/setup.py | 29 ++++ 2 files changed, 261 insertions(+) create mode 100644 sonic-chassisd/scripts/chassisd create mode 100644 sonic-chassisd/setup.py diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd new file mode 100644 index 000000000..8e9bc7875 --- /dev/null +++ b/sonic-chassisd/scripts/chassisd @@ -0,0 +1,232 @@ +#!/usr/bin/env python2 + +""" + chassisd + Linecard information update daemon for SONiC + This daemon will loop to collect Linecard related information and then write the information to state DB. + The loop interval is CHASSIS_INFO_UPDATE_PERIOD_SECS in seconds. +""" + +try: + import sys + import time + import signal + import threading + import os + from swsscommon import swsscommon + from sonic_py_common import daemon_base, logger + from sonic_platform_base.card_base import CardBase +except ImportError, e: + raise ImportError (str(e) + " - required module not found") + +# +# Constants ==================================================================== +# + +SYSLOG_IDENTIFIER = "chassisd" + +CHASSIS_INFO_TABLE = 'CHASSIS_TABLE' +CHASSIS_INFO_KEY_TEMPLATE = 'CHASSIS {}' +CHASSIS_INFO_CARD_NUM_FIELD = 'card_num' + +CHASSIS_MODULE_INFO_TABLE = 'CHASSIS_MODULE_TABLE' +CHASSIS_MODULE_INFO_KEY_TEMPLATE = 'CHASSIS_MODULE {}' +CHASSIS_MODULE_INFO_INSTANCE_FIELD = 'instance' +CHASSIS_MODULE_INFO_TYPE_FIELD = 'type' +CHASSIS_MODULE_INFO_NAME_FIELD = 'name' +CHASSIS_MODULE_INFO_STATUS_FIELD = 'status' + +CHASSIS_INFO_UPDATE_PERIOD_SECS = 10 + +CHASSIS_LOAD_ERROR = 1 +CHASSIS_NOT_AVAIL = 2 + +platform_chassis = None + +SELECT_TIMEOUT = 1000 +# +# Helper functions ============================================================= +# +info_dict_keys = [CHASSIS_MODULE_INFO_INSTANCE_FIELD, + CHASSIS_MODULE_INFO_TYPE_FIELD, + CHASSIS_MODULE_INFO_NAME_FIELD, + CHASSIS_MODULE_INFO_STATUS_FIELD] + +def get_card_info(card_index): + """ + Retrieves card info of this card + """ + card_info_dict = {} + card_info_dict = dict.fromkeys(info_dict_keys, 'N/A') + card_info_dict[CHASSIS_MODULE_INFO_INSTANCE_FIELD] = str(platform_chassis.get_card(card_index - 1).get_instance()) + card_info_dict[CHASSIS_MODULE_INFO_TYPE_FIELD] = str(platform_chassis.get_card(card_index - 1).get_type_name()) + card_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = str(platform_chassis.get_card(card_index - 1).get_name()) + card_info_dict[CHASSIS_MODULE_INFO_STATUS_FIELD] = str(platform_chassis.get_card(card_index - 1).get_status()) + return card_info_dict + +def card_db_update(card_tbl, num_cards): + for card_index in range(1, num_cards + 1): + card_info_dict = get_card_info(card_index) + if card_info_dict is not None: + fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_INSTANCE_FIELD, card_info_dict[CHASSIS_MODULE_INFO_INSTANCE_FIELD]), + (CHASSIS_MODULE_INFO_TYPE_FIELD, card_info_dict[CHASSIS_MODULE_INFO_TYPE_FIELD]), + (CHASSIS_MODULE_INFO_STATUS_FIELD, card_info_dict[CHASSIS_MODULE_INFO_STATUS_FIELD]), + (CHASSIS_MODULE_INFO_NAME_FIELD, card_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD])]) + card_tbl.set(CHASSIS_MODULE_INFO_KEY_TEMPLATE.format(card_index), fvs) + +# +# Config Handling task ======================================================== +# +class config_handling_task: + def __init__(self): + self.task_thread = None + self.config_list = {} + self.task_stopping_event = threading.Event() + + self.logger = logger.Logger(SYSLOG_IDENTIFIER) + + def task_worker(self): + config_db = daemon_base.db_connect("CONFIG_DB") + + #Subscribe to CHASSIS_MODULE table notifications in the Config DB + sel = swsscommon.Select() + sst = swsscommon.SubscriberStateTable(config_db, 'CHASSIS_MODULE') + sel.addSelectable(sst) + + # Listen indefinitely for changes to the CFG_CHASSIS_MODULE_TABLE table in the Config DB + while True: + # Use timeout to prevent ignoring the signals we want to handle + # in signal_handler() (e.g. SIGTERM for graceful shutdown) + (state, c) = sel.select(SELECT_TIMEOUT) + + if state == swsscommon.Select.TIMEOUT: + # Do not flood log when select times out + continue + if state != swsscommon.Select.OBJECT: + self.logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + continue + + (key, op, fvp) = sst.pop() + + fvp_dict = dict(fvp) + + if op == "SET": + if (fvp_dict['module_type'] == 'LINE-CARD'): + card_index = platform_chassis.get_card_index(fvp_dict['instance'], CardBase.MODULE_TYPE_LINE) + elif (fvp_dict['module_type'] == 'FABRIC-CARD'): + card_index = platform_chassis.get_card_index(fvp_dict['instance'], CardBase.MODULE_TYPE_FABRIC) + else: + continue + + #Continue if the index is invalid + if card_index < 0: + continue + + config_fvs = {} + config_fvs['index'] = card_index + config_fvs['module_type'] = fvp_dict['module_type'] + config_fvs['instance'] = fvp_dict['instance'] + self.config_list[key] = config_fvs + + #Setting the card to administratively down state + platform_chassis.get_card(card_index).set_admin_state(0) + elif op == "DEL": + config_fvs = self.config_list[key] + card_index = config_fvs['index'] + + #Setting the card to administratively up state + platform_chassis.get_card(card_index).set_admin_state(1) + + return 1 + + def task_run(self): + if self.task_stopping_event.is_set(): + return + + self.task_thread = threading.Thread(target=self.task_worker) + self.task_thread.start() + + def task_stop(self): + self.task_stopping_event.set() + self.task_thread.join() +# +# Daemon ======================================================================= +# + +class ChassisdDaemon(daemon_base.DaemonBase): + def __init__(self, log_identifier): + super(ChassisdDaemon, self).__init__(log_identifier) + + self.stop = threading.Event() + + # Signal handler + def signal_handler(self, sig, frame): + if sig == signal.SIGHUP: + self.log_info("Caught SIGHUP - ignoring...") + elif sig == signal.SIGINT: + self.log_info("Caught SIGINT - exiting...") + self.stop.set() + elif sig == signal.SIGTERM: + self.log_info("Caught SIGTERM - exiting...") + self.stop.set() + else: + self.log_warning("Caught unhandled signal '" + sig + "'") + + # Run daemon + def run(self): + global platform_chassis + + self.log_info("Starting up...") + + # Load new platform api class + try: + import sonic_platform.platform + platform_chassis = sonic_platform.platform.Platform().get_chassis() + except Exception as e: + self.log_error("Failed to load chassis due to {}".format(repr(e))) + sys.exit(CHASSIS_LOAD_ERROR) + + # Connect to STATE_DB and create chassis card/chassis info tables + state_db = daemon_base.db_connect("STATE_DB") + chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) + card_tbl = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) + + # Post number-of-cards info to STATE_DB + num_cards = platform_chassis.get_num_cards() + fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_cards))]) + chassis_tbl.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) + + if num_cards == 0: + self.log_error("Chassisd has no cards available") + + #Start configuration handling task on control card + if platform_chassis.get_controlcard_instance() == platform_chassis.get_my_instance(): + config_update = config_handling_task() + config_update.task_run() + + # Start main loop + self.log_info("Start daemon main loop") + + while not self.stop.wait(CHASSIS_INFO_UPDATE_PERIOD_SECS): + card_db_update(card_tbl, num_cards) + + self.log_info("Stop daemon main loop") + + # Delete all the information from DB and then exit + for card_index in range(1, num_cards + 1): + card_tbl._del(CHASSIS_MODULE_INFO_KEY_TEMPLATE.format(card_index)) + + chassis_tbl._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + + self.log_info("Shutting down...") + +# +# Main ========================================================================= +# + +def main(): + chassisd = ChassisdDaemon(SYSLOG_IDENTIFIER) + chassisd.run() + +if __name__ == '__main__': + main() diff --git a/sonic-chassisd/setup.py b/sonic-chassisd/setup.py new file mode 100644 index 000000000..8d5eb92a8 --- /dev/null +++ b/sonic-chassisd/setup.py @@ -0,0 +1,29 @@ +from setuptools import setup + +setup( + name='sonic-chassisd', + version='1.0', + description='Chassis daemon for SONiC', + license='Apache 2.0', + author='SONiC Team', + author_email='linuxnetdev@microsoft.com', + url='https://github.com/Azure/sonic-platform-daemons', + maintainer='Manju Prabhu', + maintainer_email='manjunath.prabhu@nokia.com', + scripts=[ + 'scripts/chassisd', + ], + 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 chassis Chassis daemon chassisd', +) From c1a72c39a3eee76af1bd2f6338085714454d7553 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 14 Oct 2020 00:20:26 -0700 Subject: [PATCH 02/12] Chassisd changes to db key-name Additional changes to config-handling as part of another process. --- sonic-chassisd/scripts/chassisd | 154 +++++++++++++++----------------- 1 file changed, 73 insertions(+), 81 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 8e9bc7875..87e9cd7d0 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -2,8 +2,8 @@ """ chassisd - Linecard information update daemon for SONiC - This daemon will loop to collect Linecard related information and then write the information to state DB. + Module information update daemon for SONiC + This daemon will loop to collect all modules related information and then write the information to state DB. The loop interval is CHASSIS_INFO_UPDATE_PERIOD_SECS in seconds. """ @@ -15,7 +15,8 @@ try: import os from swsscommon import swsscommon from sonic_py_common import daemon_base, logger - from sonic_platform_base.card_base import CardBase + from sonic_platform_base.module_base import ModuleBase + from sonic_py_common.task_base import ProcessTaskBase except ImportError, e: raise ImportError (str(e) + " - required module not found") @@ -25,16 +26,18 @@ except ImportError, e: SYSLOG_IDENTIFIER = "chassisd" +CHASSIS_CFG_TABLE = 'CHASSIS_MODULE' + CHASSIS_INFO_TABLE = 'CHASSIS_TABLE' CHASSIS_INFO_KEY_TEMPLATE = 'CHASSIS {}' -CHASSIS_INFO_CARD_NUM_FIELD = 'card_num' +CHASSIS_INFO_CARD_NUM_FIELD = 'module_num' CHASSIS_MODULE_INFO_TABLE = 'CHASSIS_MODULE_TABLE' CHASSIS_MODULE_INFO_KEY_TEMPLATE = 'CHASSIS_MODULE {}' -CHASSIS_MODULE_INFO_INSTANCE_FIELD = 'instance' -CHASSIS_MODULE_INFO_TYPE_FIELD = 'type' CHASSIS_MODULE_INFO_NAME_FIELD = 'name' -CHASSIS_MODULE_INFO_STATUS_FIELD = 'status' +CHASSIS_MODULE_INFO_DESC_FIELD = 'desc' +CHASSIS_MODULE_INFO_SLOT_FIELD = 'slot' +CHASSIS_MODULE_INFO_OPERSTATUS_FIELD = 'oper_status' CHASSIS_INFO_UPDATE_PERIOD_SECS = 10 @@ -47,42 +50,40 @@ SELECT_TIMEOUT = 1000 # # Helper functions ============================================================= # -info_dict_keys = [CHASSIS_MODULE_INFO_INSTANCE_FIELD, - CHASSIS_MODULE_INFO_TYPE_FIELD, - CHASSIS_MODULE_INFO_NAME_FIELD, - CHASSIS_MODULE_INFO_STATUS_FIELD] +info_dict_keys = [CHASSIS_MODULE_INFO_NAME_FIELD, + CHASSIS_MODULE_INFO_DESC_FIELD, + CHASSIS_MODULE_INFO_SLOT_FIELD, + CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] -def get_card_info(card_index): +def get_module_info(module_index): """ - Retrieves card info of this card + Retrieves module info of this module """ - card_info_dict = {} - card_info_dict = dict.fromkeys(info_dict_keys, 'N/A') - card_info_dict[CHASSIS_MODULE_INFO_INSTANCE_FIELD] = str(platform_chassis.get_card(card_index - 1).get_instance()) - card_info_dict[CHASSIS_MODULE_INFO_TYPE_FIELD] = str(platform_chassis.get_card(card_index - 1).get_type_name()) - card_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = str(platform_chassis.get_card(card_index - 1).get_name()) - card_info_dict[CHASSIS_MODULE_INFO_STATUS_FIELD] = str(platform_chassis.get_card(card_index - 1).get_status()) - return card_info_dict - -def card_db_update(card_tbl, num_cards): - for card_index in range(1, num_cards + 1): - card_info_dict = get_card_info(card_index) - if card_info_dict is not None: - fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_INSTANCE_FIELD, card_info_dict[CHASSIS_MODULE_INFO_INSTANCE_FIELD]), - (CHASSIS_MODULE_INFO_TYPE_FIELD, card_info_dict[CHASSIS_MODULE_INFO_TYPE_FIELD]), - (CHASSIS_MODULE_INFO_STATUS_FIELD, card_info_dict[CHASSIS_MODULE_INFO_STATUS_FIELD]), - (CHASSIS_MODULE_INFO_NAME_FIELD, card_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD])]) - card_tbl.set(CHASSIS_MODULE_INFO_KEY_TEMPLATE.format(card_index), fvs) + module_info_dict = {} + module_info_dict = dict.fromkeys(info_dict_keys, 'N/A') + module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = str(platform_chassis.get_module(module_index - 1).get_name()) + module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(platform_chassis.get_module(module_index - 1).get_description()) + module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD] = str(platform_chassis.get_module(module_index - 1).get_slot()) + module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] = str(platform_chassis.get_module(module_index - 1).get_status()) + return module_info_dict + +def module_db_update(config_tbl, module_tbl, num_modules): + for module_index in range(1, num_modules + 1): + module_info_dict = get_module_info(module_index) + if module_info_dict is not None: + fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_DESC_FIELD, module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD]), + (CHASSIS_MODULE_INFO_SLOT_FIELD, module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD]), + (CHASSIS_MODULE_INFO_OPERSTATUS_FIELD, module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD])]) + module_tbl.set(module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD], fvs) # # Config Handling task ======================================================== # -class config_handling_task: +class config_handling_task(ProcessTaskBase): def __init__(self): - self.task_thread = None - self.config_list = {} - self.task_stopping_event = threading.Event() + ProcessTaskBase.__init__(self) + # TODO: Refactor to eliminate the need for this Logger instance self.logger = logger.Logger(SYSLOG_IDENTIFIER) def task_worker(self): @@ -90,7 +91,7 @@ class config_handling_task: #Subscribe to CHASSIS_MODULE table notifications in the Config DB sel = swsscommon.Select() - sst = swsscommon.SubscriberStateTable(config_db, 'CHASSIS_MODULE') + sst = swsscommon.SubscriberStateTable(config_db, CHASSIS_CFG_TABLE) sel.addSelectable(sst) # Listen indefinitely for changes to the CFG_CHASSIS_MODULE_TABLE table in the Config DB @@ -108,47 +109,30 @@ class config_handling_task: (key, op, fvp) = sst.pop() + if not key.startswith(ModuleBase.MODULE_TYPE_CONTROL) and not key.startswith(ModuleBase.MODULE_TYPE_LINE) and not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): + self.logger.log_warning("Incorrect module-name {}. Should start with {} or {} or {}".format(ModuleBase.MODULE_TYPE_CONTROL, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC)) + continue + fvp_dict = dict(fvp) + module_index = platform_chassis.get_module_index(key) + + #Continue if the index is invalid + if module_index < 0: + self.logger.log_error("Unable to get module-index for key {} during op {}". format(key, op)) + continue + if op == "SET": - if (fvp_dict['module_type'] == 'LINE-CARD'): - card_index = platform_chassis.get_card_index(fvp_dict['instance'], CardBase.MODULE_TYPE_LINE) - elif (fvp_dict['module_type'] == 'FABRIC-CARD'): - card_index = platform_chassis.get_card_index(fvp_dict['instance'], CardBase.MODULE_TYPE_FABRIC) - else: - continue - - #Continue if the index is invalid - if card_index < 0: - continue - - config_fvs = {} - config_fvs['index'] = card_index - config_fvs['module_type'] = fvp_dict['module_type'] - config_fvs['instance'] = fvp_dict['instance'] - self.config_list[key] = config_fvs - - #Setting the card to administratively down state - platform_chassis.get_card(card_index).set_admin_state(0) + #Setting the module to administratively down state + self.logger.log_info("Changing module {} to admin DOWN state".format(key)) + platform_chassis.get_module(module_index).set_admin_state(0) elif op == "DEL": - config_fvs = self.config_list[key] - card_index = config_fvs['index'] - - #Setting the card to administratively up state - platform_chassis.get_card(card_index).set_admin_state(1) + #Setting the module to administratively up state + self.logger.log_info("Changing module {} to admin UP state".format(key)) + platform_chassis.get_module(module_index).set_admin_state(1) return 1 - def task_run(self): - if self.task_stopping_event.is_set(): - return - - self.task_thread = threading.Thread(target=self.task_worker) - self.task_thread.start() - - def task_stop(self): - self.task_stopping_event.set() - self.task_thread.join() # # Daemon ======================================================================= # @@ -157,6 +141,7 @@ class ChassisdDaemon(daemon_base.DaemonBase): def __init__(self, log_identifier): super(ChassisdDaemon, self).__init__(log_identifier) + self.logger = logger.Logger(SYSLOG_IDENTIFIER) self.stop = threading.Event() # Signal handler @@ -186,21 +171,25 @@ class ChassisdDaemon(daemon_base.DaemonBase): self.log_error("Failed to load chassis due to {}".format(repr(e))) sys.exit(CHASSIS_LOAD_ERROR) - # Connect to STATE_DB and create chassis card/chassis info tables + # Connect to STATE_DB and create chassis module/chassis info tables state_db = daemon_base.db_connect("STATE_DB") chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) - card_tbl = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) + module_tbl = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) - # Post number-of-cards info to STATE_DB - num_cards = platform_chassis.get_num_cards() - fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_cards))]) + # Connect to CONFIG_DB and check with chassis cfg table + config_db = daemon_base.db_connect("CONFIG_DB") + config_tbl = swsscommon.Table(config_db, CHASSIS_CFG_TABLE) + + # Post number-of-modules info to STATE_DB + num_modules = platform_chassis.get_num_modules() + fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_modules))]) chassis_tbl.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) - if num_cards == 0: - self.log_error("Chassisd has no cards available") + if num_modules == 0: + self.log_error("Chassisd has no modules available") - #Start configuration handling task on control card - if platform_chassis.get_controlcard_instance() == platform_chassis.get_my_instance(): + #Start configuration handling task on control module + if platform_chassis.get_controlcard_slot() == platform_chassis.get_my_slot(): config_update = config_handling_task() config_update.task_run() @@ -208,13 +197,16 @@ class ChassisdDaemon(daemon_base.DaemonBase): self.log_info("Start daemon main loop") while not self.stop.wait(CHASSIS_INFO_UPDATE_PERIOD_SECS): - card_db_update(card_tbl, num_cards) + module_db_update(config_tbl, module_tbl, num_modules) self.log_info("Stop daemon main loop") + if config_update is not None: + config_update.task_stop() + # Delete all the information from DB and then exit - for card_index in range(1, num_cards + 1): - card_tbl._del(CHASSIS_MODULE_INFO_KEY_TEMPLATE.format(card_index)) + for module_index in range(1, num_modules + 1): + module_tbl._del(CHASSIS_MODULE_INFO_KEY_TEMPLATE.format(module_index)) chassis_tbl._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) From d82046bf512f8adf3d42e6b6a0e2c5835b7612a9 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 14 Oct 2020 00:24:11 -0700 Subject: [PATCH 03/12] Fix error log message --- sonic-chassisd/scripts/chassisd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 87e9cd7d0..9b11d4712 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -110,7 +110,7 @@ class config_handling_task(ProcessTaskBase): (key, op, fvp) = sst.pop() if not key.startswith(ModuleBase.MODULE_TYPE_CONTROL) and not key.startswith(ModuleBase.MODULE_TYPE_LINE) and not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): - self.logger.log_warning("Incorrect module-name {}. Should start with {} or {} or {}".format(ModuleBase.MODULE_TYPE_CONTROL, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC)) + self.logger.log_warning("Incorrect module-name {}. Should start with {} or {} or {}".format(key, ModuleBase.MODULE_TYPE_CONTROL, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC)) continue fvp_dict = dict(fvp) From 2a1f40b12643ae188cdf648d88fec62dc45d0b8b Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 21 Oct 2020 00:29:51 -0700 Subject: [PATCH 04/12] Add error logs for incorrect module name --- sonic-chassisd/scripts/chassisd | 39 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 9b11d4712..56cc7f870 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -15,6 +15,7 @@ try: import os from swsscommon import swsscommon from sonic_py_common import daemon_base, logger + from sonic_platform_base.device_base import DeviceBase from sonic_platform_base.module_base import ModuleBase from sonic_py_common.task_base import ProcessTaskBase except ImportError, e: @@ -42,7 +43,7 @@ CHASSIS_MODULE_INFO_OPERSTATUS_FIELD = 'oper_status' CHASSIS_INFO_UPDATE_PERIOD_SECS = 10 CHASSIS_LOAD_ERROR = 1 -CHASSIS_NOT_AVAIL = 2 +CHASSIS_NOT_SUPPORTED = 2 platform_chassis = None @@ -61,20 +62,29 @@ def get_module_info(module_index): """ module_info_dict = {} module_info_dict = dict.fromkeys(info_dict_keys, 'N/A') - module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = str(platform_chassis.get_module(module_index - 1).get_name()) - module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(platform_chassis.get_module(module_index - 1).get_description()) - module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD] = str(platform_chassis.get_module(module_index - 1).get_slot()) - module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] = str(platform_chassis.get_module(module_index - 1).get_status()) + module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = str(platform_chassis.get_module(module_index).get_name()) + module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(platform_chassis.get_module(module_index).get_description()) + module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD] = str(platform_chassis.get_module(module_index).get_slot()) + module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] = str(platform_chassis.get_module(module_index).get_status()) return module_info_dict -def module_db_update(config_tbl, module_tbl, num_modules): - for module_index in range(1, num_modules + 1): +def module_db_update(logger, module_tbl, num_modules): + for module_index in range(0, num_modules): module_info_dict = get_module_info(module_index) if module_info_dict is not None: + key = module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] + + if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ + not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ + not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): + (logger.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(\ + ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) + continue + fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_DESC_FIELD, module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD]), (CHASSIS_MODULE_INFO_SLOT_FIELD, module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD]), (CHASSIS_MODULE_INFO_OPERSTATUS_FIELD, module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD])]) - module_tbl.set(module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD], fvs) + module_tbl.set(key, fvs) # # Config Handling task ======================================================== @@ -109,8 +119,11 @@ class config_handling_task(ProcessTaskBase): (key, op, fvp) = sst.pop() - if not key.startswith(ModuleBase.MODULE_TYPE_CONTROL) and not key.startswith(ModuleBase.MODULE_TYPE_LINE) and not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): - self.logger.log_warning("Incorrect module-name {}. Should start with {} or {} or {}".format(key, ModuleBase.MODULE_TYPE_CONTROL, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC)) + if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ + not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ + not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): + (self.logger.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(\ + ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) continue fvp_dict = dict(fvp) @@ -188,6 +201,10 @@ class ChassisdDaemon(daemon_base.DaemonBase): if num_modules == 0: self.log_error("Chassisd has no modules available") + if platform_chassis.get_my_slot == DeviceBase.NotImplementedInt: + self.log_error("Chassisd not supported for this platform") + sys.exit(CHASSIS_NOT_SUPPORTED) + #Start configuration handling task on control module if platform_chassis.get_controlcard_slot() == platform_chassis.get_my_slot(): config_update = config_handling_task() @@ -197,7 +214,7 @@ class ChassisdDaemon(daemon_base.DaemonBase): self.log_info("Start daemon main loop") while not self.stop.wait(CHASSIS_INFO_UPDATE_PERIOD_SECS): - module_db_update(config_tbl, module_tbl, num_modules) + module_db_update(self, module_tbl, num_modules) self.log_info("Stop daemon main loop") From ec2e5ea1787323d43059bdd0b46bf3ac5ab68eb6 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 21 Oct 2020 00:40:09 -0700 Subject: [PATCH 05/12] Fix static analysis error for chassisd --- sonic-chassisd/scripts/chassisd | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 56cc7f870..08fcc7d5b 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -9,10 +9,8 @@ try: import sys - import time import signal import threading - import os from swsscommon import swsscommon from sonic_py_common import daemon_base, logger from sonic_platform_base.device_base import DeviceBase @@ -114,7 +112,7 @@ class config_handling_task(ProcessTaskBase): # Do not flood log when select times out continue if state != swsscommon.Select.OBJECT: - self.logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT") + self.logger.log_warning("sel.select() did not return swsscommon.Select.OBJECT") continue (key, op, fvp) = sst.pop() @@ -126,8 +124,6 @@ class config_handling_task(ProcessTaskBase): ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) continue - fvp_dict = dict(fvp) - module_index = platform_chassis.get_module_index(key) #Continue if the index is invalid @@ -144,8 +140,6 @@ class config_handling_task(ProcessTaskBase): self.logger.log_info("Changing module {} to admin UP state".format(key)) platform_chassis.get_module(module_index).set_admin_state(1) - return 1 - # # Daemon ======================================================================= # From 8ef8f2238c90e823dd03947df377e1c8e53fe1a7 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 21 Oct 2020 00:45:24 -0700 Subject: [PATCH 06/12] Remove unused code for config_db --- sonic-chassisd/scripts/chassisd | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 08fcc7d5b..40d15f50a 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -183,10 +183,6 @@ class ChassisdDaemon(daemon_base.DaemonBase): chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) module_tbl = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) - # Connect to CONFIG_DB and check with chassis cfg table - config_db = daemon_base.db_connect("CONFIG_DB") - config_tbl = swsscommon.Table(config_db, CHASSIS_CFG_TABLE) - # Post number-of-modules info to STATE_DB num_modules = platform_chassis.get_num_modules() fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_modules))]) From 34902a7944341ebe6bc4153244fe10011c0828f6 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Fri, 23 Oct 2020 01:19:58 -0700 Subject: [PATCH 07/12] Update chassisd to add UTs Separated chassisd tasks into ModuleUpdater and ModuleConfigManager classes. UTs are run using Mock platform, chassis and modules on the above classes --- sonic-chassisd/pytest.ini | 3 + sonic-chassisd/scripts/chassisd | 246 ++++++++++++++++------- sonic-chassisd/setup.cfg | 2 + sonic-chassisd/setup.py | 12 ++ sonic-chassisd/tests/__init__.py | 0 sonic-chassisd/tests/mock_module_base.py | 18 ++ sonic-chassisd/tests/mock_platform.py | 73 +++++++ sonic-chassisd/tests/mock_swsscommon.py | 25 +++ sonic-chassisd/tests/test_chassisd.py | 202 +++++++++++++++++++ 9 files changed, 506 insertions(+), 75 deletions(-) create mode 100644 sonic-chassisd/pytest.ini create mode 100644 sonic-chassisd/setup.cfg create mode 100644 sonic-chassisd/tests/__init__.py create mode 100644 sonic-chassisd/tests/mock_module_base.py create mode 100644 sonic-chassisd/tests/mock_platform.py create mode 100644 sonic-chassisd/tests/mock_swsscommon.py create mode 100644 sonic-chassisd/tests/test_chassisd.py diff --git a/sonic-chassisd/pytest.ini b/sonic-chassisd/pytest.ini new file mode 100644 index 000000000..c24fe5bb9 --- /dev/null +++ b/sonic-chassisd/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +filterwarnings = + ignore::DeprecationWarning diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 40d15f50a..11fee3f15 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -11,14 +11,22 @@ try: import sys import signal import threading - from swsscommon import swsscommon + from sonic_py_common import daemon_base, logger - from sonic_platform_base.device_base import DeviceBase - from sonic_platform_base.module_base import ModuleBase from sonic_py_common.task_base import ProcessTaskBase except ImportError, e: raise ImportError (str(e) + " - required module not found") +try: + from swsscommon import swsscommon +except ImportError, e: + from tests import mock_swsscommon as swsscommon + +try: + from sonic_platform_base.module_base import ModuleBase +except ImportError, e: + from tests.mock_module_base import ModuleBase + # # Constants ==================================================================== # @@ -46,48 +54,148 @@ CHASSIS_NOT_SUPPORTED = 2 platform_chassis = None SELECT_TIMEOUT = 1000 + +NOT_AVAILABLE = 'N/A' +INVALID_SLOT = '-1' +INVALID_MODULE_INDEX = -1 + +MODULE_ADMIN_DOWN = 0 +MODULE_ADMIN_UP = 1 + # # Helper functions ============================================================= # -info_dict_keys = [CHASSIS_MODULE_INFO_NAME_FIELD, - CHASSIS_MODULE_INFO_DESC_FIELD, - CHASSIS_MODULE_INFO_SLOT_FIELD, - CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] -def get_module_info(module_index): +# try get information from platform API and return a default value if caught NotImplementedError +def try_get(callback, *args, **kwargs): """ - Retrieves module info of this module + Handy function to invoke the callback and catch NotImplementedError + :param callback: Callback to be invoked + :param default: Default return value if exception occur + :return: Default return value if exception occur else return value of the callback """ - module_info_dict = {} - module_info_dict = dict.fromkeys(info_dict_keys, 'N/A') - module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = str(platform_chassis.get_module(module_index).get_name()) - module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(platform_chassis.get_module(module_index).get_description()) - module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD] = str(platform_chassis.get_module(module_index).get_slot()) - module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] = str(platform_chassis.get_module(module_index).get_status()) - return module_info_dict - -def module_db_update(logger, module_tbl, num_modules): - for module_index in range(0, num_modules): - module_info_dict = get_module_info(module_index) - if module_info_dict is not None: - key = module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] - - if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ - not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ - not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): - (logger.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(\ - ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) - continue + default = kwargs.get('default', NOT_AVAILABLE) + try: + ret = callback(*args) + if ret is None: + ret = default + except NotImplementedError: + ret = default - fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_DESC_FIELD, module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD]), - (CHASSIS_MODULE_INFO_SLOT_FIELD, module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD]), - (CHASSIS_MODULE_INFO_OPERSTATUS_FIELD, module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD])]) - module_tbl.set(key, fvs) + return ret # -# Config Handling task ======================================================== +# Module Config Updater ======================================================== +# +class ModuleConfigUpdater(logger.Logger): + + def __init__(self, log_identifier, chassis): + """ + Constructor for ModuleConfigUpdater + :param chassis: Object representing a platform chassis + """ + super(ModuleConfigUpdater, self).__init__(log_identifier) + + self.chassis = chassis + + def deinit(self): + """ + Destructor of ModuleConfigUpdater + :return: + """ + + def module_config_update(self, key, admin_state): + if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ + not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ + not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): + (self.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(key,\ + ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) + return + + module_index = try_get(self.chassis.get_module_index, key, default=INVALID_MODULE_INDEX) + + #Continue if the index is invalid + if module_index < 0: + self.log_error("Unable to get module-index for key {} to set admin-state {}". format(key, admin_state)) + return + + if (admin_state == MODULE_ADMIN_DOWN) or (admin_state == MODULE_ADMIN_UP): + #Setting the module to administratively up/down state + (self.log_info("Changing module {} to admin {} state".format(key, + 'DOWN' if admin_state == MODULE_ADMIN_DOWN else 'UP'))) + try_get(self.chassis.get_module(module_index).set_admin_state, admin_state, default=False) + # -class config_handling_task(ProcessTaskBase): +# Module Updater ============================================================== +# +class ModuleUpdater(logger.Logger): + + def __init__(self, log_identifier, chassis): + """ + Constructor for ModuleUpdater + :param chassis: Object representing a platform chassis + """ + super(ModuleUpdater, self).__init__(log_identifier) + + self.chassis = chassis + self.num_modules = chassis.get_num_modules() + state_db = daemon_base.db_connect("STATE_DB") + self.module_table = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) + self.info_dict_keys = [CHASSIS_MODULE_INFO_NAME_FIELD, + CHASSIS_MODULE_INFO_DESC_FIELD, + CHASSIS_MODULE_INFO_SLOT_FIELD, + CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] + + def deinit(self): + """ + Destructor of ModuleUpdater + :return: + """ + # Delete all the information from DB and then exit + for module_index in range(0, self.num_modules): + name = try_get(self.chassis.get_module(module_index).get_name) + self.module_table._del(name) + + def module_db_update(self): + for module_index in range(0, self.num_modules): + module_info_dict = self._get_module_info(module_index) + if module_info_dict is not None: + key = module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] + + if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ + not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ + not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): + (self.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(key, + ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) + continue + + fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_DESC_FIELD, module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD]), + (CHASSIS_MODULE_INFO_SLOT_FIELD, module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD]), + (CHASSIS_MODULE_INFO_OPERSTATUS_FIELD, module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD])]) + self.module_table.set(key, fvs) + + def _get_module_info(self, module_index): + """ + Retrieves module info of this module + """ + module_info_dict = {} + module_info_dict = dict.fromkeys(self.info_dict_keys, 'N/A') + name = try_get(self.chassis.get_module(module_index).get_name) + desc = try_get(self.chassis.get_module(module_index).get_description) + slot = try_get(self.chassis.get_module(module_index).get_slot, default=INVALID_SLOT) + status = try_get(self.chassis.get_module(module_index).get_status, default=ModuleBase.MODULE_STATUS_OFFLINE) + + module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = name + module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(desc) + module_info_dict[CHASSIS_MODULE_INFO_SLOT_FIELD] = str(slot) + module_info_dict[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] = str(status) + + return module_info_dict + +# +# Config Manager task ======================================================== +# +class ConfigManagerTask(ProcessTaskBase): def __init__(self): ProcessTaskBase.__init__(self) @@ -95,6 +203,7 @@ class config_handling_task(ProcessTaskBase): self.logger = logger.Logger(SYSLOG_IDENTIFIER) def task_worker(self): + self.config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, platform_chassis) config_db = daemon_base.db_connect("CONFIG_DB") #Subscribe to CHASSIS_MODULE table notifications in the Config DB @@ -117,28 +226,14 @@ class config_handling_task(ProcessTaskBase): (key, op, fvp) = sst.pop() - if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ - not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ - not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): - (self.logger.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(\ - ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) - continue - - module_index = platform_chassis.get_module_index(key) - - #Continue if the index is invalid - if module_index < 0: - self.logger.log_error("Unable to get module-index for key {} during op {}". format(key, op)) + if op == 'SET': + admin_state = MODULE_ADMIN_DOWN + elif op == 'DEL': + admin_state = MODULE_ADMIN_UP + else: continue - if op == "SET": - #Setting the module to administratively down state - self.logger.log_info("Changing module {} to admin DOWN state".format(key)) - platform_chassis.get_module(module_index).set_admin_state(0) - elif op == "DEL": - #Setting the module to administratively up state - self.logger.log_info("Changing module {} to admin UP state".format(key)) - platform_chassis.get_module(module_index).set_admin_state(1) + self.config_updater.module_config_update(key, admin_state) # # Daemon ======================================================================= @@ -148,7 +243,6 @@ class ChassisdDaemon(daemon_base.DaemonBase): def __init__(self, log_identifier): super(ChassisdDaemon, self).__init__(log_identifier) - self.logger = logger.Logger(SYSLOG_IDENTIFIER) self.stop = threading.Event() # Signal handler @@ -178,44 +272,46 @@ class ChassisdDaemon(daemon_base.DaemonBase): self.log_error("Failed to load chassis due to {}".format(repr(e))) sys.exit(CHASSIS_LOAD_ERROR) - # Connect to STATE_DB and create chassis module/chassis info tables + # Connect to STATE_DB and create chassis info tables state_db = daemon_base.db_connect("STATE_DB") - chassis_tbl = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) - module_tbl = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) + chassis_table = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) - # Post number-of-modules info to STATE_DB + #Check if module list is populated num_modules = platform_chassis.get_num_modules() - fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_modules))]) - chassis_tbl.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) - if num_modules == 0: self.log_error("Chassisd has no modules available") - if platform_chassis.get_my_slot == DeviceBase.NotImplementedInt: + # Post number-of-modules info to STATE_DB + fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_modules))]) + chassis_table.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) + + #Check for valid slot numbers + my_slot = try_get(platform_chassis.get_my_slot, default=INVALID_SLOT) + supervisor_slot = try_get(platform_chassis.get_supervisor_slot, default=INVALID_SLOT) + if (my_slot == INVALID_SLOT) or (supervisor_slot == INVALID_SLOT): self.log_error("Chassisd not supported for this platform") sys.exit(CHASSIS_NOT_SUPPORTED) - #Start configuration handling task on control module - if platform_chassis.get_controlcard_slot() == platform_chassis.get_my_slot(): - config_update = config_handling_task() - config_update.task_run() + #Start configuration manager task on supervisor module + if supervisor_slot == my_slot: + config_manager = ConfigManagerTask() + config_manager.task_run() # Start main loop self.log_info("Start daemon main loop") + self.module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, platform_chassis) while not self.stop.wait(CHASSIS_INFO_UPDATE_PERIOD_SECS): - module_db_update(self, module_tbl, num_modules) + self.module_updater.module_db_update() self.log_info("Stop daemon main loop") - if config_update is not None: - config_update.task_stop() + if config_manager is not None: + config_manager.task_stop() # Delete all the information from DB and then exit - for module_index in range(1, num_modules + 1): - module_tbl._del(CHASSIS_MODULE_INFO_KEY_TEMPLATE.format(module_index)) - - chassis_tbl._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + self.module_updater.deinit() + chassis_table._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) self.log_info("Shutting down...") diff --git a/sonic-chassisd/setup.cfg b/sonic-chassisd/setup.cfg new file mode 100644 index 000000000..b7e478982 --- /dev/null +++ b/sonic-chassisd/setup.cfg @@ -0,0 +1,2 @@ +[aliases] +test=pytest diff --git a/sonic-chassisd/setup.py b/sonic-chassisd/setup.py index 8d5eb92a8..0e6176014 100644 --- a/sonic-chassisd/setup.py +++ b/sonic-chassisd/setup.py @@ -10,9 +10,20 @@ url='https://github.com/Azure/sonic-platform-daemons', maintainer='Manju Prabhu', maintainer_email='manjunath.prabhu@nokia.com', + packages=[ + 'tests' + ], scripts=[ 'scripts/chassisd', ], + setup_requires= [ + 'pytest-runner', + 'wheel' + ], + tests_require = [ + 'pytest', + 'mock>=2.0.0' + ], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: No Input/Output (Daemon)', @@ -26,4 +37,5 @@ 'Topic :: System :: Hardware', ], keywords='sonic SONiC chassis Chassis daemon chassisd', + test_suite='setup.get_test_suite' ) diff --git a/sonic-chassisd/tests/__init__.py b/sonic-chassisd/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sonic-chassisd/tests/mock_module_base.py b/sonic-chassisd/tests/mock_module_base.py new file mode 100644 index 000000000..534bf5837 --- /dev/null +++ b/sonic-chassisd/tests/mock_module_base.py @@ -0,0 +1,18 @@ +class ModuleBase(): + # Possible card types + MODULE_TYPE_SUPERVISOR = "SUPERVISOR" + MODULE_TYPE_LINE = "LINE-CARD" + MODULE_TYPE_FABRIC = "FABRIC-CARD" + + # Possible card status + #Module state is Empty if no module is inserted in the slot + MODULE_STATUS_EMPTY = "Empty" + #Module state if Offline if powered down. This is also the admin-down state. + MODULE_STATUS_OFFLINE = "Offline" + #Module state is Present when it is powered up, but not fully functional. + MODULE_STATUS_PRESENT = "Present" + #Module state is Present when it is powered up, but entered a fault state. + #Module is not able to go Online. + MODULE_STATUS_FAULT = "Fault" + #Module state is Online when fully operational + MODULE_STATUS_ONLINE = "Online" diff --git a/sonic-chassisd/tests/mock_platform.py b/sonic-chassisd/tests/mock_platform.py new file mode 100644 index 000000000..2b8086929 --- /dev/null +++ b/sonic-chassisd/tests/mock_platform.py @@ -0,0 +1,73 @@ +class MockDevice: + def __init__(self): + self.name = None + self.presence = True + self.model = 'Module Model' + self.serial = 'Module Serial' + + def get_name(self): + return self.name + + def get_presence(self): + return self.presence + + def get_model(self): + return self.model + + def get_serial(self): + return self.serial + + +class MockModule(MockDevice): + def __init__(self, module_index, module_name, module_desc, module_type, module_slot): + self.module_index = module_index + self.module_name = module_name + self.module_desc = module_desc + self.module_type = module_type + self.hw_slot = module_slot + self.module_status = '' + self.admin_state = 1 + + def get_name(self): + return self.module_name + + def get_description(self): + return self.module_desc + + def get_type(self): + return self.module_type + + def get_slot(self): + return self.hw_slot + + def get_status(self): + return self.module_status + + def set_status(self, status): + self.module_status = status + + def set_admin_state(self, up): + self.admin_state = up + + def get_admin_state(self): + return self.admin_state + +class MockChassis: + def __init__(self): + self.module_list = [] + + def get_num_modules(self): + return len(self.module_list) + + def get_module(self, index): + module = self.module_list[index] + return module + + def get_all_modules(self): + return self.module_list + + def get_module_index(self, module_name): + for module in self.module_list: + if module.module_name == module_name: + return module.module_index + return -1 diff --git a/sonic-chassisd/tests/mock_swsscommon.py b/sonic-chassisd/tests/mock_swsscommon.py new file mode 100644 index 000000000..df8c052d5 --- /dev/null +++ b/sonic-chassisd/tests/mock_swsscommon.py @@ -0,0 +1,25 @@ +STATE_DB = '' + + +class Table: + def __init__(self, db, table_name): + self.table_name = table_name + self.mock_dict = {} + + def _del(self, key): + del self.mock_dict[key] + pass + + def set(self, key, fvs): + self.mock_dict[key] = fvs.fv_dict + pass + + def get(self, key): + if key in self.mock_dict: + return self.mock_dict[key] + return None + +class FieldValuePairs: + def __init__(self, fvs): + self.fv_dict = dict(fvs) + pass diff --git a/sonic-chassisd/tests/test_chassisd.py b/sonic-chassisd/tests/test_chassisd.py new file mode 100644 index 000000000..2f47e59b7 --- /dev/null +++ b/sonic-chassisd/tests/test_chassisd.py @@ -0,0 +1,202 @@ +import os +import sys + +from mock import Mock, MagicMock, patch +from sonic_py_common import daemon_base + +from .mock_platform import MockChassis, MockModule +from .mock_module_base import ModuleBase + +SYSLOG_IDENTIFIER = 'chassisd_test' +NOT_AVAILABLE = 'N/A' + +daemon_base.db_connect = MagicMock() + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, modules_path) + +from imp import load_source + +load_source('chassisd', scripts_path + '/chassisd') +from chassisd import * + +CHASSIS_MODULE_INFO_NAME_FIELD = 'name' +CHASSIS_MODULE_INFO_DESC_FIELD = 'desc' +CHASSIS_MODULE_INFO_SLOT_FIELD = 'slot' +CHASSIS_MODULE_INFO_OPERSTATUS_FIELD = 'oper_status' + +def setup_function(): + ModuleUpdater.log_notice = MagicMock() + ModuleUpdater.log_warning = MagicMock() + + +def teardown_function(): + ModuleUpdater.log_notice.reset() + ModuleUpdater.log_warning.reset() + + +def test_moduleupdater_check_valid_fields(): + chassis = MockChassis() + index = 0 + name = "FABRIC-CARD0" + desc = "Switch Fabric Module" + slot = 10 + module_type = ModuleBase.MODULE_TYPE_FABRIC + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_ONLINE + module.set_status(status) + + chassis.module_list.append(module) + + module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) + module_updater.module_db_update() + fvs = module_updater.module_table.get(name) + assert desc == fvs[CHASSIS_MODULE_INFO_DESC_FIELD] + assert slot == int(fvs[CHASSIS_MODULE_INFO_SLOT_FIELD]) + assert status == fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] + +def test_moduleupdater_check_invalid_name(): + chassis = MockChassis() + index = 0 + name = "TEST-CARD0" + desc = "36 port 400G card" + slot = 2 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_PRESENT + module.set_status(status) + + chassis.module_list.append(module) + + module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) + module_updater.module_db_update() + fvs = module_updater.module_table.get(name) + assert fvs == None + +def test_moduleupdater_check_status_update(): + chassis = MockChassis() + index = 0 + name = "LINE-CARD0" + desc = "36 port 400G card" + slot = 1 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_ONLINE + module.set_status(status) + chassis.module_list.append(module) + + module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) + module_updater.module_db_update() + fvs = module_updater.module_table.get(name) + print('Initial DB-entry {}'.format(fvs)) + assert status == fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] + + #Update status + status = ModuleBase.MODULE_STATUS_OFFLINE + module.set_status(status) + fvs = module_updater.module_table.get(name) + print('Not updated DB-entry {}'.format(fvs)) + assert status != fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] + + #Update status and db + module_updater.module_db_update() + fvs = module_updater.module_table.get(name) + print('Updated DB-entry {}'.format(fvs)) + assert status == fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] + +def test_moduleupdater_check_deinit(): + chassis = MockChassis() + index = 0 + name = "LINE-CARD0" + desc = "36 port 400G card" + slot = 1 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_ONLINE + module.set_status(status) + chassis.module_list.append(module) + + module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) + module_updater.module_db_update() + fvs = module_updater.module_table.get(name) + assert status == fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] + + module_table = module_updater.module_table + module_updater.deinit() + fvs = module_table.get(name) + assert fvs == None + +def test_configupdater_check_valid_names(): + chassis = MockChassis() + index = 0 + name = "TEST-CARD0" + desc = "36 port 400G card" + slot = 1 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_ONLINE + module.set_status(status) + chassis.module_list.append(module) + + config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, chassis) + admin_state = 0 + config_updater.module_config_update(name, admin_state) + + #No change since invalid key + assert module.get_admin_state() != admin_state + +def test_configupdater_check_valid_index(): + chassis = MockChassis() + index = -1 + name = "LINE-CARD0" + desc = "36 port 400G card" + slot = 1 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_ONLINE + module.set_status(status) + chassis.module_list.append(module) + + config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, chassis) + admin_state = 0 + config_updater.module_config_update(name, admin_state) + + #No change since invalid index + assert module.get_admin_state() != admin_state + +def test_configupdater_check_admin_state(): + chassis = MockChassis() + index = 0 + name = "LINE-CARD0" + desc = "36 port 400G card" + slot = 1 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #Set initial state + status = ModuleBase.MODULE_STATUS_ONLINE + module.set_status(status) + chassis.module_list.append(module) + + config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, chassis) + admin_state = 0 + config_updater.module_config_update(name, admin_state) + assert module.get_admin_state() == admin_state + + admin_state = 1 + config_updater.module_config_update(name, admin_state) + assert module.get_admin_state() == admin_state From aa96fd2545e7b4019aa82400c29ba05d32eb4218 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Tue, 27 Oct 2020 01:02:00 -0700 Subject: [PATCH 08/12] Moving chassisd to python3 --- sonic-chassisd/scripts/chassisd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 11fee3f15..e549c0782 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ chassisd @@ -14,17 +14,17 @@ try: from sonic_py_common import daemon_base, logger from sonic_py_common.task_base import ProcessTaskBase -except ImportError, e: +except ImportError as e: raise ImportError (str(e) + " - required module not found") try: from swsscommon import swsscommon -except ImportError, e: +except ImportError as e: from tests import mock_swsscommon as swsscommon try: from sonic_platform_base.module_base import ModuleBase -except ImportError, e: +except ImportError as e: from tests.mock_module_base import ModuleBase # @@ -56,7 +56,7 @@ platform_chassis = None SELECT_TIMEOUT = 1000 NOT_AVAILABLE = 'N/A' -INVALID_SLOT = '-1' +INVALID_SLOT = ModuleBase.MODULE_INVALID_SLOT INVALID_MODULE_INDEX = -1 MODULE_ADMIN_DOWN = 0 From 69398bdf7b12e309cb5102cfc57050b7b58a5f56 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Wed, 28 Oct 2020 15:30:34 -0700 Subject: [PATCH 09/12] Added pytest coverage support --- .gitignore | 6 ++++++ sonic-chassisd/pytest.ini | 3 +-- sonic-chassisd/scripts/chassisd | 31 ++++++++++++++++----------- sonic-chassisd/setup.py | 3 ++- sonic-chassisd/tests/test_chassisd.py | 29 +++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 1c2875d68..2d9c861eb 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,9 @@ # Compiled code which doesn't end in '.pyc' sonic-thermalctld/scripts/thermalctldc +sonic-chassisd/scripts/chassisdc + +# Unit test / coverage reports +coverage.xml +.coverage +htmlcov/ diff --git a/sonic-chassisd/pytest.ini b/sonic-chassisd/pytest.ini index c24fe5bb9..83b74d373 100644 --- a/sonic-chassisd/pytest.ini +++ b/sonic-chassisd/pytest.ini @@ -1,3 +1,2 @@ [pytest] -filterwarnings = - ignore::DeprecationWarning +addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index e549c0782..de8dd4bd1 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -139,7 +139,9 @@ class ModuleUpdater(logger.Logger): self.chassis = chassis self.num_modules = chassis.get_num_modules() + # Connect to STATE_DB and create chassis info tables state_db = daemon_base.db_connect("STATE_DB") + self.chassis_table = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) self.module_table = swsscommon.Table(state_db, CHASSIS_MODULE_INFO_TABLE) self.info_dict_keys = [CHASSIS_MODULE_INFO_NAME_FIELD, CHASSIS_MODULE_INFO_DESC_FIELD, @@ -156,6 +158,20 @@ class ModuleUpdater(logger.Logger): name = try_get(self.chassis.get_module(module_index).get_name) self.module_table._del(name) + if self.chassis_table is not None: + self.chassis_table._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + + def modules_num_update(self): + #Check if module list is populated + num_modules = self.chassis.get_num_modules() + if num_modules == 0: + self.log_error("Chassisd has no modules available") + return + + # Post number-of-modules info to STATE_DB + fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_modules))]) + self.chassis_table.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) + def module_db_update(self): for module_index in range(0, self.num_modules): module_info_dict = self._get_module_info(module_index) @@ -272,18 +288,9 @@ class ChassisdDaemon(daemon_base.DaemonBase): self.log_error("Failed to load chassis due to {}".format(repr(e))) sys.exit(CHASSIS_LOAD_ERROR) - # Connect to STATE_DB and create chassis info tables - state_db = daemon_base.db_connect("STATE_DB") - chassis_table = swsscommon.Table(state_db, CHASSIS_INFO_TABLE) - #Check if module list is populated - num_modules = platform_chassis.get_num_modules() - if num_modules == 0: - self.log_error("Chassisd has no modules available") - - # Post number-of-modules info to STATE_DB - fvs = swsscommon.FieldValuePairs([(CHASSIS_INFO_CARD_NUM_FIELD, str(num_modules))]) - chassis_table.set(CHASSIS_INFO_KEY_TEMPLATE.format(1), fvs) + self.module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, platform_chassis) + self.module_updater.modules_num_update() #Check for valid slot numbers my_slot = try_get(platform_chassis.get_my_slot, default=INVALID_SLOT) @@ -300,7 +307,6 @@ class ChassisdDaemon(daemon_base.DaemonBase): # Start main loop self.log_info("Start daemon main loop") - self.module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, platform_chassis) while not self.stop.wait(CHASSIS_INFO_UPDATE_PERIOD_SECS): self.module_updater.module_db_update() @@ -311,7 +317,6 @@ class ChassisdDaemon(daemon_base.DaemonBase): # Delete all the information from DB and then exit self.module_updater.deinit() - chassis_table._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) self.log_info("Shutting down...") diff --git a/sonic-chassisd/setup.py b/sonic-chassisd/setup.py index 0e6176014..fa415e597 100644 --- a/sonic-chassisd/setup.py +++ b/sonic-chassisd/setup.py @@ -22,7 +22,8 @@ ], tests_require = [ 'pytest', - 'mock>=2.0.0' + 'mock>=2.0.0', + 'pytest-cov' ], classifiers=[ 'Development Status :: 4 - Beta', diff --git a/sonic-chassisd/tests/test_chassisd.py b/sonic-chassisd/tests/test_chassisd.py index 2f47e59b7..1dc989512 100644 --- a/sonic-chassisd/tests/test_chassisd.py +++ b/sonic-chassisd/tests/test_chassisd.py @@ -27,6 +27,9 @@ CHASSIS_MODULE_INFO_SLOT_FIELD = 'slot' CHASSIS_MODULE_INFO_OPERSTATUS_FIELD = 'oper_status' +CHASSIS_INFO_KEY_TEMPLATE = 'CHASSIS {}' +CHASSIS_INFO_CARD_NUM_FIELD = 'module_num' + def setup_function(): ModuleUpdater.log_notice = MagicMock() ModuleUpdater.log_warning = MagicMock() @@ -127,6 +130,7 @@ def test_moduleupdater_check_deinit(): chassis.module_list.append(module) module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) + module_updater.modules_num_update() module_updater.module_db_update() fvs = module_updater.module_table.get(name) assert status == fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] @@ -200,3 +204,28 @@ def test_configupdater_check_admin_state(): admin_state = 1 config_updater.module_config_update(name, admin_state) assert module.get_admin_state() == admin_state + +def test_configupdater_check_num_modules(): + chassis = MockChassis() + index = 0 + name = "LINE-CARD0" + desc = "36 port 400G card" + slot = 1 + module_type = ModuleBase.MODULE_TYPE_LINE + module = MockModule(index, name, desc, module_type, slot) + + #No modules + module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) + module_updater.modules_num_update() + fvs = module_updater.chassis_table.get(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + assert fvs == None + + #Add a module + chassis.module_list.append(module) + module_updater.modules_num_update() + fvs = module_updater.chassis_table.get(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + assert chassis.get_num_modules() == int(fvs[CHASSIS_INFO_CARD_NUM_FIELD]) + + module_updater.deinit() + fvs = module_updater.chassis_table.get(CHASSIS_INFO_KEY_TEMPLATE.format(1)) + assert fvs == None From b71cf95e11aa80bff4a8837337c33537987da2df Mon Sep 17 00:00:00 2001 From: mprabhu Date: Sun, 1 Nov 2020 18:45:02 -0800 Subject: [PATCH 10/12] Removing parathesis around multi log lines --- sonic-chassisd/scripts/chassisd | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index de8dd4bd1..3d66ad66e 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -108,8 +108,9 @@ class ModuleConfigUpdater(logger.Logger): if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): - (self.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(key,\ - ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) + self.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(key, + ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, + ModuleBase.MODULE_TYPE_FABRIC)) return module_index = try_get(self.chassis.get_module_index, key, default=INVALID_MODULE_INDEX) @@ -121,8 +122,8 @@ class ModuleConfigUpdater(logger.Logger): if (admin_state == MODULE_ADMIN_DOWN) or (admin_state == MODULE_ADMIN_UP): #Setting the module to administratively up/down state - (self.log_info("Changing module {} to admin {} state".format(key, - 'DOWN' if admin_state == MODULE_ADMIN_DOWN else 'UP'))) + self.log_info("Changing module {} to admin {} state".format(key, + 'DOWN' if admin_state == MODULE_ADMIN_DOWN else 'UP')) try_get(self.chassis.get_module(module_index).set_admin_state, admin_state, default=False) # @@ -181,8 +182,9 @@ class ModuleUpdater(logger.Logger): if not key.startswith(ModuleBase.MODULE_TYPE_SUPERVISOR) and \ not key.startswith(ModuleBase.MODULE_TYPE_LINE) and \ not key.startswith(ModuleBase.MODULE_TYPE_FABRIC): - (self.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(key, - ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, ModuleBase.MODULE_TYPE_FABRIC))) + self.log_error("Incorrect module-name {}. Should start with {} or {} or {}".format(key, + ModuleBase.MODULE_TYPE_SUPERVISOR, ModuleBase.MODULE_TYPE_LINE, + ModuleBase.MODULE_TYPE_FABRIC)) continue fvs = swsscommon.FieldValuePairs([(CHASSIS_MODULE_INFO_DESC_FIELD, module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD]), From f506d991834afad1c9c943941162181de69546b0 Mon Sep 17 00:00:00 2001 From: mprabhu Date: Tue, 3 Nov 2020 17:09:26 -0800 Subject: [PATCH 11/12] Replacing get_status with get_oper_status --- sonic-chassisd/scripts/chassisd | 4 ++-- sonic-chassisd/tests/mock_module_base.py | 3 +++ sonic-chassisd/tests/mock_platform.py | 4 ++-- sonic-chassisd/tests/test_chassisd.py | 16 ++++++++-------- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 3d66ad66e..2161b6c20 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -8,8 +8,8 @@ """ try: - import sys import signal + import sys import threading from sonic_py_common import daemon_base, logger @@ -201,7 +201,7 @@ class ModuleUpdater(logger.Logger): name = try_get(self.chassis.get_module(module_index).get_name) desc = try_get(self.chassis.get_module(module_index).get_description) slot = try_get(self.chassis.get_module(module_index).get_slot, default=INVALID_SLOT) - status = try_get(self.chassis.get_module(module_index).get_status, default=ModuleBase.MODULE_STATUS_OFFLINE) + status = try_get(self.chassis.get_module(module_index).get_oper_status, default=ModuleBase.MODULE_STATUS_OFFLINE) module_info_dict[CHASSIS_MODULE_INFO_NAME_FIELD] = name module_info_dict[CHASSIS_MODULE_INFO_DESC_FIELD] = str(desc) diff --git a/sonic-chassisd/tests/mock_module_base.py b/sonic-chassisd/tests/mock_module_base.py index 534bf5837..7335f5fcf 100644 --- a/sonic-chassisd/tests/mock_module_base.py +++ b/sonic-chassisd/tests/mock_module_base.py @@ -1,4 +1,7 @@ class ModuleBase(): + # Invalid slot for modular chassis + MODULE_INVALID_SLOT = -1 + # Possible card types MODULE_TYPE_SUPERVISOR = "SUPERVISOR" MODULE_TYPE_LINE = "LINE-CARD" diff --git a/sonic-chassisd/tests/mock_platform.py b/sonic-chassisd/tests/mock_platform.py index 2b8086929..a8350a2cd 100644 --- a/sonic-chassisd/tests/mock_platform.py +++ b/sonic-chassisd/tests/mock_platform.py @@ -40,10 +40,10 @@ def get_type(self): def get_slot(self): return self.hw_slot - def get_status(self): + def get_oper_status(self): return self.module_status - def set_status(self, status): + def set_oper_status(self, status): self.module_status = status def set_admin_state(self, up): diff --git a/sonic-chassisd/tests/test_chassisd.py b/sonic-chassisd/tests/test_chassisd.py index 1dc989512..3e7d356bd 100644 --- a/sonic-chassisd/tests/test_chassisd.py +++ b/sonic-chassisd/tests/test_chassisd.py @@ -51,7 +51,7 @@ def test_moduleupdater_check_valid_fields(): #Set initial state status = ModuleBase.MODULE_STATUS_ONLINE - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) @@ -73,7 +73,7 @@ def test_moduleupdater_check_invalid_name(): #Set initial state status = ModuleBase.MODULE_STATUS_PRESENT - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) @@ -93,7 +93,7 @@ def test_moduleupdater_check_status_update(): #Set initial state status = ModuleBase.MODULE_STATUS_ONLINE - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) @@ -104,7 +104,7 @@ def test_moduleupdater_check_status_update(): #Update status status = ModuleBase.MODULE_STATUS_OFFLINE - module.set_status(status) + module.set_oper_status(status) fvs = module_updater.module_table.get(name) print('Not updated DB-entry {}'.format(fvs)) assert status != fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] @@ -126,7 +126,7 @@ def test_moduleupdater_check_deinit(): #Set initial state status = ModuleBase.MODULE_STATUS_ONLINE - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) @@ -151,7 +151,7 @@ def test_configupdater_check_valid_names(): #Set initial state status = ModuleBase.MODULE_STATUS_ONLINE - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, chassis) @@ -172,7 +172,7 @@ def test_configupdater_check_valid_index(): #Set initial state status = ModuleBase.MODULE_STATUS_ONLINE - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, chassis) @@ -193,7 +193,7 @@ def test_configupdater_check_admin_state(): #Set initial state status = ModuleBase.MODULE_STATUS_ONLINE - module.set_status(status) + module.set_oper_status(status) chassis.module_list.append(module) config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, chassis) From ced331f5c7dfe0ceed162cfd9768ae29016c832c Mon Sep 17 00:00:00 2001 From: mprabhu Date: Tue, 3 Nov 2020 19:46:13 -0800 Subject: [PATCH 12/12] Fixing comments to follow PEP8 standards --- sonic-chassisd/scripts/chassisd | 14 ++++++------- sonic-chassisd/tests/mock_module_base.py | 16 +++++++-------- sonic-chassisd/tests/test_chassisd.py | 26 ++++++++++++------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 2161b6c20..d064f7e07 100644 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -115,13 +115,13 @@ class ModuleConfigUpdater(logger.Logger): module_index = try_get(self.chassis.get_module_index, key, default=INVALID_MODULE_INDEX) - #Continue if the index is invalid + # Continue if the index is invalid if module_index < 0: self.log_error("Unable to get module-index for key {} to set admin-state {}". format(key, admin_state)) return if (admin_state == MODULE_ADMIN_DOWN) or (admin_state == MODULE_ADMIN_UP): - #Setting the module to administratively up/down state + # Setting the module to administratively up/down state self.log_info("Changing module {} to admin {} state".format(key, 'DOWN' if admin_state == MODULE_ADMIN_DOWN else 'UP')) try_get(self.chassis.get_module(module_index).set_admin_state, admin_state, default=False) @@ -163,7 +163,7 @@ class ModuleUpdater(logger.Logger): self.chassis_table._del(CHASSIS_INFO_KEY_TEMPLATE.format(1)) def modules_num_update(self): - #Check if module list is populated + # Check if module list is populated num_modules = self.chassis.get_num_modules() if num_modules == 0: self.log_error("Chassisd has no modules available") @@ -224,7 +224,7 @@ class ConfigManagerTask(ProcessTaskBase): self.config_updater = ModuleConfigUpdater(SYSLOG_IDENTIFIER, platform_chassis) config_db = daemon_base.db_connect("CONFIG_DB") - #Subscribe to CHASSIS_MODULE table notifications in the Config DB + # Subscribe to CHASSIS_MODULE table notifications in the Config DB sel = swsscommon.Select() sst = swsscommon.SubscriberStateTable(config_db, CHASSIS_CFG_TABLE) sel.addSelectable(sst) @@ -290,18 +290,18 @@ class ChassisdDaemon(daemon_base.DaemonBase): self.log_error("Failed to load chassis due to {}".format(repr(e))) sys.exit(CHASSIS_LOAD_ERROR) - #Check if module list is populated + # Check if module list is populated self.module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, platform_chassis) self.module_updater.modules_num_update() - #Check for valid slot numbers + # Check for valid slot numbers my_slot = try_get(platform_chassis.get_my_slot, default=INVALID_SLOT) supervisor_slot = try_get(platform_chassis.get_supervisor_slot, default=INVALID_SLOT) if (my_slot == INVALID_SLOT) or (supervisor_slot == INVALID_SLOT): self.log_error("Chassisd not supported for this platform") sys.exit(CHASSIS_NOT_SUPPORTED) - #Start configuration manager task on supervisor module + # Start configuration manager task on supervisor module if supervisor_slot == my_slot: config_manager = ConfigManagerTask() config_manager.task_run() diff --git a/sonic-chassisd/tests/mock_module_base.py b/sonic-chassisd/tests/mock_module_base.py index 7335f5fcf..f49222980 100644 --- a/sonic-chassisd/tests/mock_module_base.py +++ b/sonic-chassisd/tests/mock_module_base.py @@ -2,20 +2,20 @@ class ModuleBase(): # Invalid slot for modular chassis MODULE_INVALID_SLOT = -1 - # Possible card types + # Possible card types for modular chassis MODULE_TYPE_SUPERVISOR = "SUPERVISOR" MODULE_TYPE_LINE = "LINE-CARD" MODULE_TYPE_FABRIC = "FABRIC-CARD" - # Possible card status - #Module state is Empty if no module is inserted in the slot + # Possible card status for modular chassis + # Module state is Empty if no module is inserted in the slot MODULE_STATUS_EMPTY = "Empty" - #Module state if Offline if powered down. This is also the admin-down state. + # Module state if Offline if powered down. This is also the admin-down state. MODULE_STATUS_OFFLINE = "Offline" - #Module state is Present when it is powered up, but not fully functional. + # Module state is Present when it is powered up, but not fully functional. MODULE_STATUS_PRESENT = "Present" - #Module state is Present when it is powered up, but entered a fault state. - #Module is not able to go Online. + # Module state is Present when it is powered up, but entered a fault state. + # Module is not able to go Online. MODULE_STATUS_FAULT = "Fault" - #Module state is Online when fully operational + # Module state is Online when fully operational MODULE_STATUS_ONLINE = "Online" diff --git a/sonic-chassisd/tests/test_chassisd.py b/sonic-chassisd/tests/test_chassisd.py index 3e7d356bd..6684ea932 100644 --- a/sonic-chassisd/tests/test_chassisd.py +++ b/sonic-chassisd/tests/test_chassisd.py @@ -49,7 +49,7 @@ def test_moduleupdater_check_valid_fields(): module_type = ModuleBase.MODULE_TYPE_FABRIC module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_ONLINE module.set_oper_status(status) @@ -71,7 +71,7 @@ def test_moduleupdater_check_invalid_name(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_PRESENT module.set_oper_status(status) @@ -91,7 +91,7 @@ def test_moduleupdater_check_status_update(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_ONLINE module.set_oper_status(status) chassis.module_list.append(module) @@ -102,14 +102,14 @@ def test_moduleupdater_check_status_update(): print('Initial DB-entry {}'.format(fvs)) assert status == fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] - #Update status + # Update status status = ModuleBase.MODULE_STATUS_OFFLINE module.set_oper_status(status) fvs = module_updater.module_table.get(name) print('Not updated DB-entry {}'.format(fvs)) assert status != fvs[CHASSIS_MODULE_INFO_OPERSTATUS_FIELD] - #Update status and db + # Update status and db module_updater.module_db_update() fvs = module_updater.module_table.get(name) print('Updated DB-entry {}'.format(fvs)) @@ -124,7 +124,7 @@ def test_moduleupdater_check_deinit(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_ONLINE module.set_oper_status(status) chassis.module_list.append(module) @@ -149,7 +149,7 @@ def test_configupdater_check_valid_names(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_ONLINE module.set_oper_status(status) chassis.module_list.append(module) @@ -158,7 +158,7 @@ def test_configupdater_check_valid_names(): admin_state = 0 config_updater.module_config_update(name, admin_state) - #No change since invalid key + # No change since invalid key assert module.get_admin_state() != admin_state def test_configupdater_check_valid_index(): @@ -170,7 +170,7 @@ def test_configupdater_check_valid_index(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_ONLINE module.set_oper_status(status) chassis.module_list.append(module) @@ -179,7 +179,7 @@ def test_configupdater_check_valid_index(): admin_state = 0 config_updater.module_config_update(name, admin_state) - #No change since invalid index + # No change since invalid index assert module.get_admin_state() != admin_state def test_configupdater_check_admin_state(): @@ -191,7 +191,7 @@ def test_configupdater_check_admin_state(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #Set initial state + # Set initial state status = ModuleBase.MODULE_STATUS_ONLINE module.set_oper_status(status) chassis.module_list.append(module) @@ -214,13 +214,13 @@ def test_configupdater_check_num_modules(): module_type = ModuleBase.MODULE_TYPE_LINE module = MockModule(index, name, desc, module_type, slot) - #No modules + # No modules module_updater = ModuleUpdater(SYSLOG_IDENTIFIER, chassis) module_updater.modules_num_update() fvs = module_updater.chassis_table.get(CHASSIS_INFO_KEY_TEMPLATE.format(1)) assert fvs == None - #Add a module + # Add a module chassis.module_list.append(module) module_updater.modules_num_update() fvs = module_updater.chassis_table.get(CHASSIS_INFO_KEY_TEMPLATE.format(1))