diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 9566612f0..be8292f25 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -3141,6 +3141,30 @@ def test_CmisManagerTask_update_port_transceiver_status_table_sw_cmis_state(self task.update_port_transceiver_status_table_sw_cmis_state("Ethernet0", CMIS_STATE_INSERTED) assert mock_get_status_tbl.set.call_count == 1 + @patch('xcvrd.xcvrd_utilities.common.is_fast_reboot_enabled', MagicMock(return_value=True)) + @patch('xcvrd.xcvrd_utilities.common.get_namespace_from_asic_id', MagicMock(return_value='asic1')) + def test_CmisManagerTask_is_fast_reboot_enabled_for_lport(self): + port_mapping = PortMapping() + port_mapping.logical_to_asic = {'Ethernet0': 1} + stop_event = threading.Event() + task = CmisManagerTask(['asic1'], port_mapping, stop_event, platform_chassis=MagicMock()) + common.is_fast_reboot_enabled.reset_mock() + + assert task.is_fast_reboot_enabled_for_lport('Ethernet0') is True + common.get_namespace_from_asic_id.assert_called_with(1) + common.is_fast_reboot_enabled.assert_not_called() + + @patch('xcvrd.xcvrd_utilities.common.is_fast_reboot_enabled', MagicMock(return_value=False)) + def test_CmisManagerTask_is_fast_reboot_enabled_for_lport_default_namespace(self): + port_mapping = PortMapping() + stop_event = threading.Event() + task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event, platform_chassis=MagicMock()) + # Ethernet999 not in port_dict -> asic_id -1 -> namespace '' + with patch.object(common, 'get_namespace_from_asic_id', MagicMock()) as mock_get_ns: + assert task.is_fast_reboot_enabled_for_lport('Ethernet999') is False + mock_get_ns.assert_not_called() + common.is_fast_reboot_enabled.assert_called_with('') + @patch('xcvrd.xcvrd._wrapper_get_sfp_type', MagicMock(return_value='QSFP_DD')) def test_CmisManagerTask_handle_port_change_event(self): port_mapping = PortMapping() @@ -5560,6 +5584,36 @@ def test_SfpStateUpdateTask_mapping_event_from_change_event(self): port_dict = {1, SFP_STATUS_INSERTED} assert task._mapping_event_from_change_event(True, port_dict) == NORMAL_EVENT + @patch('xcvrd.xcvrd_utilities.common.get_namespace_from_asic_id', MagicMock(return_value='asic2')) + @patch('xcvrd.xcvrd_utilities.common.is_syncd_warm_restore_complete', MagicMock(return_value=False)) + @patch('xcvrd.xcvrd_utilities.common.is_fast_reboot_enabled', MagicMock(return_value=True)) + def test_SfpStateUpdateTask_is_warm_fast_reboot_for_lport(self): + port_mapping = PortMapping() + port_mapping.logical_to_asic = {'Ethernet0': 2} + mock_sfp_obj_dict = MagicMock() + stop_event = threading.Event() + sfp_error_event = threading.Event() + task = SfpStateUpdateTask(['asic2'], port_mapping, mock_sfp_obj_dict, stop_event, sfp_error_event) + common.is_syncd_warm_restore_complete.assert_called_once_with('asic2') + common.is_fast_reboot_enabled.assert_called_once_with('asic2') + common.is_syncd_warm_restore_complete.reset_mock() + common.is_fast_reboot_enabled.reset_mock() + + assert task.is_warm_fast_reboot_for_lport('Ethernet0') is True + common.get_namespace_from_asic_id.assert_called_with(2) + common.is_syncd_warm_restore_complete.assert_not_called() + common.is_fast_reboot_enabled.assert_not_called() + + def test_SfpStateUpdateTask_is_warm_fast_reboot_for_lport_invalid_asic(self): + port_mapping = PortMapping() + mock_sfp_obj_dict = MagicMock() + stop_event = threading.Event() + sfp_error_event = threading.Event() + task = SfpStateUpdateTask(DEFAULT_NAMESPACE, port_mapping, mock_sfp_obj_dict, stop_event, sfp_error_event) + task.port_mapping.get_asic_id_for_logical_port = MagicMock(return_value=None) + + assert task.is_warm_fast_reboot_for_lport('Ethernet0') is False + @patch('time.sleep', MagicMock()) @patch('xcvrd.xcvrd.XcvrTableHelper', MagicMock()) @patch('xcvrd.xcvrd._wrapper_soak_sfp_insert_event', MagicMock()) diff --git a/sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py b/sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py index 373fc2fbd..4fb3da595 100644 --- a/sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py +++ b/sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py @@ -60,9 +60,15 @@ def __init__(self, namespaces, port_mapping, main_thread_stop_event, skip_cmis_m self.namespaces = namespaces self.platform_chassis = platform_chassis self.xcvr_table_helper = XcvrTableHelper(self.namespaces) - self._is_fast_reboot_enabled = None # Cache of gearbox line lanes dict, refreshed once per task_worker iteration. self._gearbox_lanes_dict = None + self.fast_reboot_status = self.initialize_fast_reboot_status() + + def initialize_fast_reboot_status(self): + fast_reboot_status = {} + for namespace in self.namespaces: + fast_reboot_status[namespace] = bool(common.is_fast_reboot_enabled(namespace)) + return fast_reboot_status def log_debug(self, message): helper_logger.log_debug(message) @@ -73,15 +79,14 @@ def log_notice(self, message): def log_error(self, message): helper_logger.log_error(message) - def is_fast_reboot_enabled(self): - """Check if fast reboot is enabled, caching the result""" - if self._is_fast_reboot_enabled is None: - self._is_fast_reboot_enabled = common.is_fast_reboot_enabled() - return self._is_fast_reboot_enabled - def get_asic_id(self, lport): return self.port_dict.get(lport, {}).get("asic_id", -1) + def is_fast_reboot_enabled_for_lport(self, lport): + asic_id = self.get_asic_id(lport) + namespace = common.get_namespace_from_asic_id(asic_id) if asic_id >= 0 else '' + return self.fast_reboot_status.get(namespace, False) + def update_port_transceiver_status_table_sw_cmis_state(self, lport, cmis_state_to_set): status_table = self.xcvr_table_helper.get_status_sw_tbl(self.get_asic_id(lport)) if status_table is None: @@ -862,7 +867,7 @@ def handle_cmis_inserted_state(self, lport): speed = port_info.get('speed') subport = port_info.get('subport') appl = port_info.get('appl', 0) - is_fast_reboot = self.is_fast_reboot_enabled() + is_fast_reboot = self.is_fast_reboot_enabled_for_lport(lport) self.port_dict[lport]['appl'] = common.get_cmis_application_desired(api, host_lane_count, speed) if self.port_dict[lport]['appl'] is None: @@ -1058,7 +1063,6 @@ def process_cmis_state_machine(self, lport): subport = port_info.get('subport') pport = port_info.get('pport') sfp = port_info.get('sfp') - is_fast_reboot = self.is_fast_reboot_enabled() # CMIS expiration and retries # @@ -1367,4 +1371,3 @@ def join(self): threading.Thread.join(self) if self.exc: raise self.exc - diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index a9051b1e4..1c50db0df 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -278,9 +278,24 @@ def __init__(self, namespaces, port_mapping, sfp_obj_dict, main_thread_stop_even self.sfp_obj_dict = sfp_obj_dict self.logger = syslogger.SysLogger(SYSLOG_IDENTIFIER_SFPSTATEUPDATETASK, enable_runtime_config=True) self.xcvr_table_helper = XcvrTableHelper(self.namespaces) + self.warm_fast_reboot_status = self.initialize_warm_fast_reboot_status() self.dom_db_utils = DOMDBUtils(sfp_obj_dict, self.port_mapping, self.xcvr_table_helper, self.task_stopping_event, self.logger) self.vdm_db_utils = VDMDBUtils(sfp_obj_dict, self.port_mapping, self.xcvr_table_helper, self.task_stopping_event, self.logger) + def initialize_warm_fast_reboot_status(self): + warm_fast_reboot_status = {} + for namespace in self.namespaces: + warm_fast_reboot_status[namespace] = bool(common.is_syncd_warm_restore_complete(namespace) or + common.is_fast_reboot_enabled(namespace)) + return warm_fast_reboot_status + + def is_warm_fast_reboot_for_lport(self, logical_port): + asic_index = self.port_mapping.get_asic_id_for_logical_port(logical_port) + if asic_index is None: + return False + namespace = common.get_namespace_from_asic_id(asic_index) + return self.warm_fast_reboot_status.get(namespace, False) + def _mapping_event_from_change_event(self, status, port_dict): """ mapping from what get_transceiver_change_event returns to event defined in the state machine @@ -311,11 +326,6 @@ def _post_port_sfp_info_and_dom_thr_to_db_once(self, port_mapping, xcvr_table_he transceiver_dict = {} retry_eeprom_set = set() - # Pre-fetch warm start status for all namespaces/ASICs - warm_start_status = {} - for namespace in self.namespaces: - warm_start_status[namespace] = common.is_syncd_warm_restore_complete(namespace) - # Post all the current interface sfp/dom threshold info to STATE_DB logical_port_list = port_mapping.logical_port_list for logical_port_name in logical_port_list: @@ -328,13 +338,13 @@ def _post_port_sfp_info_and_dom_thr_to_db_once(self, port_mapping, xcvr_table_he helper_logger.log_warning("Got invalid asic index for {}, ignored while posting SFP info during boot-up".format(logical_port_name)) continue - # Get warm start status for this ASIC's namespace + # Get warm/fast reboot status for this ASIC's namespace namespace = common.get_namespace_from_asic_id(asic_index) - is_warm_start = warm_start_status.get(namespace, False) + is_warm_fast_reboot = self.warm_fast_reboot_status.get(namespace, False) rc = post_port_sfp_info_to_db(logical_port_name, port_mapping, xcvr_table_helper.get_intf_tbl(asic_index), transceiver_dict, stop_event) if rc != SFP_EEPROM_NOT_READY: - if is_warm_start == False: + if is_warm_fast_reboot == False: media_settings_parser.notify_media_setting(logical_port_name, transceiver_dict, xcvr_table_helper, port_mapping) else: retry_eeprom_set.add(logical_port_name) @@ -568,7 +578,8 @@ def task_worker(self, stopping_event, sfp_error_event): self.dom_db_utils.post_port_dom_thresholds_to_db(logical_port) self.vdm_db_utils.post_port_vdm_thresholds_to_db(logical_port) - media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping) + if not self.is_warm_fast_reboot_for_lport(logical_port): + media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping) transceiver_dict.clear() elif value == sfp_status_helper.SFP_STATUS_REMOVED: # Remove the SFP API object for this physical port @@ -828,7 +839,8 @@ def on_add_logical_port(self, port_change_event): else: self.dom_db_utils.post_port_dom_thresholds_to_db(port_change_event.port_name) self.vdm_db_utils.post_port_vdm_thresholds_to_db(port_change_event.port_name) - media_settings_parser.notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper, self.port_mapping) + if not self.is_warm_fast_reboot_for_lport(port_change_event.port_name): + media_settings_parser.notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper, self.port_mapping) else: status = sfp_status_helper.SFP_STATUS_REMOVED if not status else status common.update_port_transceiver_status_table_sw(port_change_event.port_name, status_sw_tbl, status, error_description) @@ -856,7 +868,8 @@ def retry_eeprom_reading(self): self.dom_db_utils.post_port_dom_thresholds_to_db(logical_port) self.vdm_db_utils.post_port_vdm_thresholds_to_db(logical_port) - media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping) + if not self.is_warm_fast_reboot_for_lport(logical_port): + media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping) transceiver_dict.clear() retry_success_set.add(logical_port) # Update retry EEPROM set @@ -1052,8 +1065,8 @@ def init(self): # Initialize xcvr table helper self.xcvr_table_helper = XcvrTableHelper(self.namespaces) - if common.is_fast_reboot_enabled(): - self.log_info("Skip loading media_settings.json and optics_si_settings.json in case of fast-reboot") + if all(common.is_fast_reboot_enabled(ns) for ns in self.namespaces): + self.log_notice("Skip loading media_settings.json and optics_si_settings.json in case of fast-reboot") else: media_settings_parser.load_media_settings() optics_si_parser.load_optics_si_settings() @@ -1082,10 +1095,9 @@ def deinit(self): self.log_info("Start daemon deinit...") # Pre-fetch warm/fast reboot status for all namespaces/ASICs - is_fast_reboot = common.is_fast_reboot_enabled() warm_fast_reboot_status = {} for namespace in self.namespaces: - warm_fast_reboot_status[namespace] = common.is_syncd_warm_restore_complete(namespace) or is_fast_reboot + warm_fast_reboot_status[namespace] = common.is_syncd_warm_restore_complete(namespace) or common.is_fast_reboot_enabled(namespace) # Delete all the information from DB and then exit port_mapping_data = port_event_helper.get_port_mapping(self.namespaces) diff --git a/sonic-xcvrd/xcvrd/xcvrd_utilities/common.py b/sonic-xcvrd/xcvrd/xcvrd_utilities/common.py index 31f1c28b2..3eed30063 100644 --- a/sonic-xcvrd/xcvrd/xcvrd_utilities/common.py +++ b/sonic-xcvrd/xcvrd/xcvrd_utilities/common.py @@ -145,10 +145,13 @@ def _wrapper_get_presence(physical_port): pass return False -def is_fast_reboot_enabled(): +def is_fast_reboot_enabled(namespace=''): """Check if fast reboot is enabled""" - fastboot_enabled = subprocess.check_output('sonic-db-cli STATE_DB hget "FAST_RESTART_ENABLE_TABLE|system" enable', shell=True, universal_newlines=True) - return "true" in fastboot_enabled + state_db = daemon_base.db_connect("STATE_DB", namespace=namespace) + fastboot_enabled = state_db.hget("FAST_RESTART_ENABLE_TABLE|system", "enable") + if isinstance(fastboot_enabled, str): + return fastboot_enabled.strip().lower() == "true" + return False def is_warm_reboot_enabled(): """Check if warm reboot is enabled"""