diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index b21b3d22a..55cb46f54 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -237,6 +237,26 @@ def test_CmisManagerTask_task_run_with_exception(self): assert("sonic-xcvrd/xcvrd/xcvrd.py" in str(trace)) assert("wait_for_port_config_done" in str(trace)) + port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_ADD) + port_mapping.handle_port_change_event(port_change_event) + cmis_manager = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + cmis_manager.wait_for_port_config_done = MagicMock() #no-op + cmis_manager.update_port_transceiver_status_table_sw_cmis_state = MagicMock(side_effect = NotImplementedError) + exception_received = None + trace = None + try: + cmis_manager.start() + cmis_manager.join() + except Exception as e1: + exception_received = e1 + trace = traceback.format_exc() + + assert not cmis_manager.is_alive() + assert(type(exception_received) == NotImplementedError) + assert("NotImplementedError" in str(trace) and "effect" in str(trace)) + assert("sonic-xcvrd/xcvrd/xcvrd.py" in str(trace)) + assert("update_port_transceiver_status_table_sw_cmis_state" in str(trace)) + @patch('xcvrd.xcvrd.PortChangeObserver', MagicMock(handle_port_update_event=MagicMock())) @patch('xcvrd.xcvrd.CmisManagerTask.wait_for_port_config_done', MagicMock()) @patch('xcvrd.xcvrd.is_fast_reboot_enabled', MagicMock(return_value=(False))) @@ -266,7 +286,7 @@ def test_CmisManagerTask_get_xcvr_api_exception(self, mock_platform_chassis, moc mock_sfp.get_xcvr_api = MagicMock(side_effect=NotImplementedError) task.task_worker() assert mock_log_exception_traceback.call_count == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_FAILED + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_FAILED # Case 2: is_flat_memory() raises AttributeError. In this case, CMIS SM should transition to READY state mock_xcvr_api = MagicMock() @@ -275,7 +295,7 @@ def test_CmisManagerTask_get_xcvr_api_exception(self, mock_platform_chassis, moc task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.on_port_update_event(port_change_event) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_READY + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_READY # Case 2.5: get_module_type_abbreviation() returns unsupported module type. In this case, CMIS SM should transition to READY state mock_xcvr_api.is_flat_memory = MagicMock(return_value=False) @@ -283,7 +303,7 @@ def test_CmisManagerTask_get_xcvr_api_exception(self, mock_platform_chassis, moc task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.on_port_update_event(port_change_event) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_READY + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_READY # Case 3: get_cmis_application_desired() raises an exception mock_xcvr_api.is_flat_memory = MagicMock(return_value=False) @@ -294,7 +314,7 @@ def test_CmisManagerTask_get_xcvr_api_exception(self, mock_platform_chassis, moc task.get_cmis_host_lanes_mask = MagicMock() task.task_worker() assert mock_log_exception_traceback.call_count == 2 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_FAILED + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_FAILED assert task.get_cmis_host_lanes_mask.call_count == 0 @patch('xcvrd.xcvrd_utilities.port_event_helper.subscribe_port_config_change', MagicMock(side_effect = NotImplementedError)) @@ -357,7 +377,7 @@ def test_SfpStateUpdateTask_task_run_with_exception(self): @patch('xcvrd.xcvrd.SffManagerTask.join') def test_DaemonXcvrd_run_with_exception(self, mock_task_join_sff, mock_task_join_sfp, mock_task_join_dom, mock_init, mock_os_kill): - mock_init.return_value = (PortMapping(), set()) + mock_init.return_value = PortMapping() xcvrd = DaemonXcvrd(SYSLOG_IDENTIFIER) xcvrd.enable_sff_mgr = True xcvrd.load_feature_flags = MagicMock() @@ -1342,7 +1362,7 @@ def test_DaemonXcvrd_wait_for_port_config_done(self, mock_select, mock_sub_table @patch('xcvrd.xcvrd.DomInfoUpdateTask.join') @patch('xcvrd.xcvrd.SfpStateUpdateTask.join') def test_DaemonXcvrd_run(self, mock_task_stop1, mock_task_stop2, mock_task_run1, mock_task_run2, mock_deinit, mock_init): - mock_init.return_value = (PortMapping(), set()) + mock_init.return_value = PortMapping() xcvrd = DaemonXcvrd(SYSLOG_IDENTIFIER) xcvrd.load_feature_flags = MagicMock() xcvrd.stop_event.wait = MagicMock() @@ -1635,12 +1655,25 @@ def test_CmisManagerTask_handle_port_change_event(self): port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_DEL) task.on_port_update_event(port_change_event) + assert len(task.port_dict) == 0 + + port_dict = {'speed':'400000', 'lanes':'1,2,3,4,5,6,7,8'} + port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_SET, port_dict) + task.on_port_update_event(port_change_event) assert len(task.port_dict) == 1 - port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_SET) + # STATE_DB DEL event doesn't remove port from port_dict + # this happens when transceiver is plugged-out or DPB is used + port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_DEL, {}, db_name='STATE_DB') task.on_port_update_event(port_change_event) assert len(task.port_dict) == 1 + # CONFIG_DB DEL event removes port from port_dict + # this happens when DPB is used + port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_DEL, {}, db_name='CONFIG_DB', table_name='PORT') + task.on_port_update_event(port_change_event) + assert len(task.port_dict) == 0 + @patch('xcvrd.xcvrd.XcvrTableHelper') def test_CmisManagerTask_get_configured_freq(self, mock_table_helper): port_mapping = PortMapping() @@ -2107,16 +2140,15 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl): mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) port_mapping = PortMapping() + port_mapping.handle_port_change_event(PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_ADD)) stop_event = threading.Event() task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) - task.port_mapping.logical_port_list = ['Ethernet0'] task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) task.xcvr_table_helper.get_status_tbl.return_value = mock_get_status_tbl task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_UNKNOWN + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_UNKNOWN - task.port_mapping.logical_port_list = MagicMock() port_change_event = PortChangeEvent('PortConfigDone', -1, 0, PortChangeEvent.PORT_SET) task.on_port_update_event(port_change_event) assert task.isPortConfigDone @@ -2125,7 +2157,7 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl): {'speed':'400000', 'lanes':'1,2,3,4,5,6,7,8'}) task.on_port_update_event(port_change_event) assert len(task.port_dict) == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_INSERTED + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_INSERTED task.get_host_tx_status = MagicMock(return_value='true') task.get_port_admin_status = MagicMock(return_value='up') @@ -2139,51 +2171,50 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl): mock_xcvr_api.decommission_all_datapaths = MagicMock(return_value=True) task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_DP_DEINIT + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_DP_DEINIT task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() assert mock_xcvr_api.set_datapath_deinit.call_count == 1 assert mock_xcvr_api.tx_disable_channel.call_count == 1 assert mock_xcvr_api.set_lpmode.call_count == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_AP_CONF + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_AP_CONF # Case 2: DP_DEINIT --> AP Configured task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() assert mock_xcvr_api.set_application.call_count == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_DP_INIT + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_DP_INIT # Case 3: AP Configured --> DP_INIT task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() assert mock_xcvr_api.set_datapath_init.call_count == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_DP_TXON + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_DP_TXON # Case 4: DP_INIT --> DP_TXON task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() assert mock_xcvr_api.tx_disable_channel.call_count == 2 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_DP_ACTIVATE + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_DP_ACTIVATE # Case 5: DP_TXON --> DP_ACTIVATION task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.post_port_active_apsel_to_db = MagicMock() task.task_worker() assert task.post_port_active_apsel_to_db.call_count == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_READY + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_READY # Fail test coverage - Module Inserted state failing to reach DP_DEINIT port_mapping = PortMapping() + port_mapping.handle_port_change_event(PortChangeEvent('Ethernet1', 1, 0, PortChangeEvent.PORT_ADD)) stop_event = threading.Event() task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) - task.port_mapping.logical_port_list = ['Ethernet1'] task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) task.xcvr_table_helper.get_status_tbl.return_value = mock_get_status_tbl task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet1', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet1'))) == CMIS_STATE_UNKNOWN + assert get_cmis_state_from_state_db('Ethernet1', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet1'))) == CMIS_STATE_UNKNOWN - task.port_mapping.logical_port_list = MagicMock() port_change_event = PortChangeEvent('PortConfigDone', -1, 0, PortChangeEvent.PORT_SET) task.on_port_update_event(port_change_event) assert task.isPortConfigDone @@ -2192,7 +2223,7 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl): {'speed':'400000', 'lanes':'1,2,3,4,5,6,7,8'}) task.on_port_update_event(port_change_event) assert len(task.port_dict) == 1 - assert get_cmis_state_from_state_db('Ethernet1', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet1'))) == CMIS_STATE_INSERTED + assert get_cmis_state_from_state_db('Ethernet1', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet1'))) == CMIS_STATE_INSERTED task.get_host_tx_status = MagicMock(return_value='true') task.get_port_admin_status = MagicMock(return_value='up') @@ -2206,7 +2237,7 @@ def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl): mock_xcvr_api.decommission_all_datapaths = MagicMock(return_value=False) task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert not get_cmis_state_from_state_db('Ethernet1', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet1'))) == CMIS_STATE_DP_DEINIT + assert not get_cmis_state_from_state_db('Ethernet1', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet1'))) == CMIS_STATE_DP_DEINIT @patch('xcvrd.xcvrd.XcvrTableHelper.get_status_tbl') @patch('xcvrd.xcvrd.platform_chassis') @@ -2311,16 +2342,15 @@ def test_CmisManagerTask_task_worker_fastboot(self, mock_chassis, mock_get_statu mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) port_mapping = PortMapping() + port_mapping.handle_port_change_event(PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_ADD)) stop_event = threading.Event() task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) - task.port_mapping.logical_port_list = ['Ethernet0'] task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) task.xcvr_table_helper.get_status_tbl.return_value = mock_get_status_tbl task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_UNKNOWN + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_UNKNOWN - task.port_mapping.logical_port_list = MagicMock() port_change_event = PortChangeEvent('PortConfigDone', -1, 0, PortChangeEvent.PORT_SET) task.on_port_update_event(port_change_event) assert task.isPortConfigDone @@ -2329,7 +2359,7 @@ def test_CmisManagerTask_task_worker_fastboot(self, mock_chassis, mock_get_statu {'speed':'400000', 'lanes':'1,2,3,4,5,6,7,8'}) task.on_port_update_event(port_change_event) assert len(task.port_dict) == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_INSERTED + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_INSERTED task.get_host_tx_status = MagicMock(return_value='false') task.get_port_admin_status = MagicMock(return_value='up') @@ -2341,7 +2371,7 @@ def test_CmisManagerTask_task_worker_fastboot(self, mock_chassis, mock_get_statu task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_READY + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_READY @patch('xcvrd.xcvrd.XcvrTableHelper.get_status_tbl') @patch('xcvrd.xcvrd.platform_chassis') @@ -2446,16 +2476,15 @@ def test_CmisManagerTask_task_worker_host_tx_ready_false(self, mock_chassis, moc mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) port_mapping = PortMapping() + port_mapping.handle_port_change_event(PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_ADD)) stop_event = threading.Event() task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) - task.port_mapping.logical_port_list = ['Ethernet0'] task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) task.xcvr_table_helper.get_status_tbl.return_value = mock_get_status_tbl task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_UNKNOWN + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_UNKNOWN - task.port_mapping.logical_port_list = MagicMock() port_change_event = PortChangeEvent('PortConfigDone', -1, 0, PortChangeEvent.PORT_SET) task.on_port_update_event(port_change_event) assert task.isPortConfigDone @@ -2464,7 +2493,7 @@ def test_CmisManagerTask_task_worker_host_tx_ready_false(self, mock_chassis, moc {'speed':'400000', 'lanes':'1,2,3,4,5,6,7,8'}) task.on_port_update_event(port_change_event) assert len(task.port_dict) == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_INSERTED + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_INSERTED task.get_host_tx_status = MagicMock(return_value='false') task.get_port_admin_status = MagicMock(return_value='up') @@ -2477,7 +2506,7 @@ def test_CmisManagerTask_task_worker_host_tx_ready_false(self, mock_chassis, moc task.task_worker() assert mock_xcvr_api.tx_disable_channel.call_count == 1 - assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.port_mapping.get_asic_id_for_logical_port('Ethernet0'))) == CMIS_STATE_READY + assert get_cmis_state_from_state_db('Ethernet0', task.xcvr_table_helper.get_status_tbl(task.get_asic_id('Ethernet0'))) == CMIS_STATE_READY @pytest.mark.parametrize("lport, expected_dom_polling", [ ('Ethernet0', 'disabled'), diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index f07da1572..6e235779f 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -820,12 +820,12 @@ def __init__(self, namespaces, port_mapping, main_thread_stop_event, skip_cmis_m self.exc = None self.task_stopping_event = threading.Event() self.main_thread_stop_event = main_thread_stop_event - self.port_dict = {} - self.port_mapping = copy.deepcopy(port_mapping) + self.port_dict = {k: {"asic_id": v} for k, v in port_mapping.logical_to_asic.items()} self.isPortInitDone = False self.isPortConfigDone = False self.skip_cmis_mgr = skip_cmis_mgr self.namespaces = namespaces + self.xcvr_table_helper = XcvrTableHelper(self.namespaces) def log_debug(self, message): helper_logger.log_debug("CMIS: {}".format(message)) @@ -836,9 +836,11 @@ def log_notice(self, message): def log_error(self, message): helper_logger.log_error("CMIS: {}".format(message)) + def get_asic_id(self, lport): + return self.port_dict.get(lport, {}).get("asic_id", -1) + def update_port_transceiver_status_table_sw_cmis_state(self, lport, cmis_state_to_set): - asic_index = self.port_mapping.get_asic_id_for_logical_port(lport) - status_table = self.xcvr_table_helper.get_status_tbl(asic_index) + status_table = self.xcvr_table_helper.get_status_tbl(self.get_asic_id(lport)) if status_table is None: helper_logger.log_error("status_table is None while updating " "sw CMIS state for lport {}".format(lport)) @@ -870,15 +872,12 @@ def on_port_update_event(self, port_change_event): if pport is None: return - # Skip if the port/cage type is not a CMIS - # 'index' can be -1 if STATE_DB|PORT_TABLE - if lport not in self.port_dict: - self.port_dict[lport] = {} - if port_change_event.port_dict is None: return if port_change_event.event_type == port_change_event.PORT_SET: + if lport not in self.port_dict: + self.port_dict[lport] = {"asic_id": port_change_event.asic_id} if pport >= 0: self.port_dict[lport]['index'] = pport if 'speed' in port_change_event.port_dict and port_change_event.port_dict['speed'] != 'N/A': @@ -897,8 +896,45 @@ def on_port_update_event(self, port_change_event): self.port_dict[lport]['subport'] = int(port_change_event.port_dict['subport']) self.force_cmis_reinit(lport, 0) - else: - self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_REMOVED) + + elif port_change_event.event_type == port_change_event.PORT_DEL: + # In handling the DEL event, the following two scenarios must be considered: + # 1. PORT_DEL event due to transceiver plug-out + # 2. PORT_DEL event due to Dynamic Port Breakout (DPB) + # + # Scenario 1 is simple, as only a STATE_DB|TRANSCEIVER_INFO PORT_DEL event occurs, + # so we just need to set SW_CMIS_STATE to CMIS_STATE_REMOVED. + # + # Scenario 2 is a bit more complex. First, for the port(s) before DPB, a CONFIG_DB|PORT PORT_DEL + # and a STATE_DB|PORT_TABLE PORT_DEL event occur. Next, for the port(s) after DPB, + # a CONFIG_DB|PORT PORT_SET and a STATE_DB|PORT_TABLE PORT_SET event occur. + # After that (after a short delay), a STATE_DB|TRANSCEIVER_INFO PORT_DEL event + # occurs for the port(s) before DPB, and finally, a STATE_DB|TRANSCEIVER_INFO + # PORT_SET event occurs for the port(s) after DPB. + # + # Below is the event sequence when configuring Ethernet0 from "2x200G" to "1x400G" + # (based on actual logs). + # + # 1. SET Ethernet0 CONFIG_DB|PORT + # 2. DEL Ethernet2 CONFIG_DB|PORT + # 3. DEL Ethernet0 CONFIG_DB|PORT + # 4. DEL Ethernet0 STATE_DB|PORT_TABLE + # 5. DEL Ethernet2 STATE_DB|PORT_TABLE + # 6. SET Ethernet0 CONFIG_DB|PORT + # 7. SET Ethernet0 STATE_DB|PORT_TABLE + # 8. SET Ethernet0 STATE_DB|PORT_TABLE + # 9. DEL Ethernet2 STATE_DB|TRANSCEIVER_INFO + # 10. DEL Ethernet0 STATE_DB|TRANSCEIVER_INFO + # 11. SET Ethernet0 STATE_DB|TRANSCEIVER_INFO + # + # To handle both scenarios, if the lport exists in port_dict for any DEL EVENT, + # set SW_CMIS_STATE to REMOVED. Additionally, for DEL EVENTS from CONFIG_DB due to DPB, + # remove the lport from port_dict. + if lport in self.port_dict: + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_REMOVED) + + if port_change_event.db_name == 'CONFIG_DB' and port_change_event.table_name == 'PORT': + self.port_dict.pop(lport) def get_cmis_dp_init_duration_secs(self, api): return api.get_datapath_init_duration()/1000 @@ -1162,8 +1198,7 @@ def get_configured_laser_freq_from_db(self, lport): Return the Tx power configured by user in CONFIG_DB's PORT table """ freq = 0 - asic_index = self.port_mapping.get_asic_id_for_logical_port(lport) - port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(asic_index) + port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(self.get_asic_id(lport)) found, port_info = port_tbl.get(lport) if found and 'laser_freq' in dict(port_info): @@ -1175,8 +1210,7 @@ def get_configured_tx_power_from_db(self, lport): Return the Tx power configured by user in CONFIG_DB's PORT table """ power = 0 - asic_index = self.port_mapping.get_asic_id_for_logical_port(lport) - port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(asic_index) + port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(self.get_asic_id(lport)) found, port_info = port_tbl.get(lport) if found and 'tx_power' in dict(port_info): @@ -1186,8 +1220,7 @@ def get_configured_tx_power_from_db(self, lport): def get_host_tx_status(self, lport): host_tx_ready = 'false' - asic_index = self.port_mapping.get_asic_id_for_logical_port(lport) - state_port_tbl = self.xcvr_table_helper.get_state_port_tbl(asic_index) + state_port_tbl = self.xcvr_table_helper.get_state_port_tbl(self.get_asic_id(lport)) found, port_info = state_port_tbl.get(lport) if found and 'host_tx_ready' in dict(port_info): @@ -1197,8 +1230,7 @@ def get_host_tx_status(self, lport): def get_port_admin_status(self, lport): admin_status = 'down' - asic_index = self.port_mapping.get_asic_id_for_logical_port(lport) - cfg_port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(asic_index) + cfg_port_tbl = self.xcvr_table_helper.get_cfg_port_tbl(self.get_asic_id(lport)) found, port_info = cfg_port_tbl.get(lport) if found: @@ -1268,8 +1300,7 @@ def post_port_active_apsel_to_db(self, api, lport, host_lanes_mask): media_lane_count = appl_advt_act.get('media_lane_count', 'N/A') if appl_advt_act else 'N/A' tuple_list.append(('media_lane_count', str(media_lane_count))) - asic_index = self.port_mapping.get_asic_id_for_logical_port(lport) - intf_tbl = self.xcvr_table_helper.get_intf_tbl(asic_index) + intf_tbl = self.xcvr_table_helper.get_intf_tbl(self.get_asic_id(lport)) if not intf_tbl: helper_logger.log_warning("Active ApSel db update: TRANSCEIVER_INFO table not found for {}".format(lport)) return @@ -1303,16 +1334,6 @@ def wait_for_port_config_done(self, namespace): break def task_worker(self): - self.xcvr_table_helper = XcvrTableHelper(self.namespaces) - - self.log_notice("Waiting for PortConfigDone...") - for namespace in self.namespaces: - self.wait_for_port_config_done(namespace) - - logical_port_list = self.port_mapping.logical_port_list - for lport in logical_port_list: - self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_UNKNOWN) - is_fast_reboot = is_fast_reboot_enabled() # APPL_DB for CONFIG updates, and STATE_DB for insertion/removal @@ -1331,7 +1352,7 @@ def task_worker(self): if lport not in self.port_dict: continue - state = get_cmis_state_from_state_db(lport, self.xcvr_table_helper.get_status_tbl(self.port_mapping.get_asic_id_for_logical_port(lport))) + state = get_cmis_state_from_state_db(lport, self.xcvr_table_helper.get_status_tbl(self.get_asic_id(lport))) if state in CMIS_TERMINAL_STATES or state == CMIS_STATE_UNKNOWN: if state != CMIS_STATE_READY: self.port_dict[lport]['appl'] = 0 @@ -1664,6 +1685,14 @@ def run(self): return try: + + self.log_notice("Waiting for PortConfigDone...") + for namespace in self.namespaces: + self.wait_for_port_config_done(namespace) + + for lport in self.port_dict.keys(): + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_UNKNOWN) + self.task_worker() except Exception as e: helper_logger.log_error("Exception occured at {} thread due to {}".format(threading.current_thread().getName(), repr(e)))