diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 2f701cc01..568b19dd2 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -182,7 +182,8 @@ def test_CmisManagerTask_task_run_with_exception(self): def test_DomInfoUpdateTask_task_run_with_exception(self): port_mapping = PortMapping() stop_event = threading.Event() - dom_info_update = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + mock_cmis_manager = MagicMock() + dom_info_update = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event, mock_cmis_manager) exception_received = None trace = None try: @@ -356,6 +357,16 @@ def test_del_port_sfp_dom_info_from_db(self): firmware_info_tbl = Table("STATE_DB", TRANSCEIVER_FIRMWARE_INFO_TABLE) del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, init_tbl, dom_tbl, dom_threshold_tbl, pm_tbl, firmware_info_tbl) + @pytest.mark.parametrize("mock_found, mock_status_dict, expected_cmis_state", [ + (True, {'cmis_state': CMIS_STATE_INSERTED}, CMIS_STATE_INSERTED), + (False, {}, CMIS_STATE_UNKNOWN), + (True, {'other_key': 'some_value'}, CMIS_STATE_UNKNOWN) + ]) + def test_get_cmis_state_from_state_db(self, mock_found, mock_status_dict, expected_cmis_state): + status_tbl = MagicMock() + status_tbl.get.return_value = (mock_found, mock_status_dict) + assert get_cmis_state_from_state_db("Ethernet0", status_tbl) == expected_cmis_state + @patch('xcvrd.xcvrd.get_physical_port_name_dict', MagicMock(return_value={0: 'Ethernet0'})) @patch('xcvrd.xcvrd._wrapper_get_presence', MagicMock(return_value=True)) @patch('xcvrd.xcvrd._wrapper_get_transceiver_status', MagicMock(return_value={'module_state': 'ModuleReady', @@ -1308,6 +1319,22 @@ def test_SffManagerTask_task_worker(self, mock_chassis): assert mock_xcvr_api.tx_disable_channel.call_count == 2 mock_sfp.get_presence = MagicMock(return_value=True) + def test_CmisManagerTask_update_port_transceiver_status_table_sw_cmis_state(self): + port_mapping = PortMapping() + stop_event = threading.Event() + task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_SET) + task.on_port_update_event(port_change_event) + + task.xcvr_table_helper.get_status_tbl = MagicMock(return_value=None) + task.update_port_transceiver_status_table_sw_cmis_state("Ethernet0", CMIS_STATE_INSERTED) + + mock_get_status_tbl = MagicMock() + mock_get_status_tbl.set = MagicMock() + task.xcvr_table_helper.get_status_tbl.return_value = mock_get_status_tbl + 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._wrapper_get_sfp_type', MagicMock(return_value='QSFP_DD')) def test_CmisManagerTask_handle_port_change_event(self): port_mapping = PortMapping() @@ -1585,12 +1612,14 @@ def test_CmisManagerTask_post_port_active_apsel_to_db(self): assert int_tbl.getKeys() == [] + @patch('xcvrd.xcvrd.XcvrTableHelper.get_status_tbl') @patch('xcvrd.xcvrd.platform_chassis') @patch('xcvrd.xcvrd.PortChangeObserver', MagicMock(handle_port_update_event=MagicMock())) @patch('xcvrd.xcvrd._wrapper_get_sfp_type', MagicMock(return_value='QSFP_DD')) @patch('xcvrd.xcvrd.CmisManagerTask.wait_for_port_config_done', MagicMock()) @patch('xcvrd.xcvrd.is_cmis_api', MagicMock(return_value=True)) - def test_CmisManagerTask_task_worker(self, mock_chassis): + def test_CmisManagerTask_task_worker(self, mock_chassis, mock_get_status_tbl): + mock_get_status_tbl = Table("STATE_DB", TRANSCEIVER_STATUS_TABLE) mock_xcvr_api = MagicMock() mock_xcvr_api.set_datapath_deinit = MagicMock(return_value=True) mock_xcvr_api.set_datapath_init = MagicMock(return_value=True) @@ -1687,7 +1716,13 @@ def test_CmisManagerTask_task_worker(self, mock_chassis): port_mapping = PortMapping() stop_event = threading.Event() task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + task.port_mapping.logical_port_list = ['Ethernet0'] + 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 + 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 @@ -1696,6 +1731,7 @@ def test_CmisManagerTask_task_worker(self, mock_chassis): {'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 task.get_host_tx_status = MagicMock(return_value='true') task.get_port_admin_status = MagicMock(return_value='up') @@ -1707,31 +1743,38 @@ def test_CmisManagerTask_task_worker(self, mock_chassis): # Case 1: Module Inserted --> DP_DEINIT task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) task.task_worker() - assert task.port_dict['Ethernet0']['cmis_state'] == 'DP_DEINIT' + 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 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 task.port_dict['Ethernet0']['cmis_state'] == 'AP_CONFIGURED' + 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 # 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 task.port_dict['Ethernet0']['cmis_state'] == 'DP_INIT' + 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 # 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 task.port_dict['Ethernet0']['cmis_state'] == 'DP_TXON' + 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 # 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 task.port_dict['Ethernet0']['cmis_state'] == 'DP_ACTIVATION' + 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 + + # 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 @pytest.mark.parametrize("lport, expected_dom_polling", [ ('Ethernet0', 'disabled'), @@ -1753,7 +1796,8 @@ def mock_get(key): port_mapping = PortMapping() stop_event = threading.Event() - task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + mock_cmis_manager = MagicMock() + task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event, mock_cmis_manager) task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) task.port_mapping.handle_port_change_event(PortChangeEvent('Ethernet4', 1, 0, PortChangeEvent.PORT_ADD)) task.port_mapping.handle_port_change_event(PortChangeEvent('Ethernet12', 1, 0, PortChangeEvent.PORT_ADD)) @@ -1766,12 +1810,34 @@ def mock_get(key): assert task.get_dom_polling_from_config_db(lport) == expected_dom_polling + @pytest.mark.parametrize("skip_cmis_manager, is_asic_index_none, mock_cmis_state, expected_result", [ + (True, False, None, False), + (False, False, CMIS_STATE_INSERTED, True), + (False, False, CMIS_STATE_READY, False), + (False, False, CMIS_STATE_UNKNOWN, True), + (False, True, None, False), + ]) + @patch('xcvrd.xcvrd.get_cmis_state_from_state_db') + def test_DomInfoUpdateTask_is_port_in_cmis_initialization_process(self, mock_get_cmis_state_from_state_db, skip_cmis_manager, is_asic_index_none, mock_cmis_state, expected_result): + port_mapping = PortMapping() + lport = 'Ethernet0' + port_change_event = PortChangeEvent(lport, 1, 0, PortChangeEvent.PORT_ADD) + stop_event = threading.Event() + task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event, skip_cmis_manager) + task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) + task.on_port_config_change(port_change_event) + mock_get_cmis_state_from_state_db.return_value = mock_cmis_state + if is_asic_index_none: + lport='INVALID_PORT' + assert task.is_port_in_cmis_initialization_process(lport) == expected_result + @patch('xcvrd.xcvrd.XcvrTableHelper', MagicMock()) @patch('xcvrd.xcvrd.delete_port_from_status_table_hw') def test_DomInfoUpdateTask_handle_port_change_event(self, mock_del_status_tbl_hw): port_mapping = PortMapping() stop_event = threading.Event() - task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + mock_cmis_manager = MagicMock() + task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event, mock_cmis_manager) task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) port_change_event = PortChangeEvent('Ethernet0', 1, 0, PortChangeEvent.PORT_ADD) task.on_port_config_change(port_change_event) @@ -1794,7 +1860,8 @@ def test_DomInfoUpdateTask_handle_port_change_event(self, mock_del_status_tbl_hw def test_DomInfoUpdateTask_task_run_stop(self): port_mapping = PortMapping() stop_event = threading.Event() - task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + mock_cmis_manager = MagicMock() + task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event, mock_cmis_manager) task.start() task.join() assert not task.is_alive() @@ -1819,10 +1886,12 @@ def test_DomInfoUpdateTask_task_worker(self, mock_post_pm_info, mock_update_stat port_mapping = PortMapping() stop_event = threading.Event() - task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event) + mock_cmis_manager = MagicMock() + task = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, stop_event, mock_cmis_manager) task.xcvr_table_helper = XcvrTableHelper(DEFAULT_NAMESPACE) task.task_stopping_event.wait = MagicMock(side_effect=[False, True]) task.get_dom_polling_from_config_db = MagicMock(return_value='enabled') + task.is_port_in_cmis_terminal_state = MagicMock(return_value=False) mock_detect_error.return_value = True task.task_worker() assert task.port_mapping.logical_port_list.count('Ethernet0') diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index e0624edfa..aaecb27ed 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -48,7 +48,24 @@ PLATFORM_SPECIFIC_MODULE_NAME = "sfputil" PLATFORM_SPECIFIC_CLASS_NAME = "SfpUtil" -TRANSCEIVER_STATUS_TABLE_SW_FIELDS = ["status", "error"] +TRANSCEIVER_STATUS_TABLE_SW_FIELDS = ["status", "error", "cmis_state"] + +CMIS_STATE_UNKNOWN = 'UNKNOWN' +CMIS_STATE_INSERTED = 'INSERTED' +CMIS_STATE_DP_DEINIT = 'DP_DEINIT' +CMIS_STATE_AP_CONF = 'AP_CONFIGURED' +CMIS_STATE_DP_ACTIVATE = 'DP_ACTIVATION' +CMIS_STATE_DP_INIT = 'DP_INIT' +CMIS_STATE_DP_TXON = 'DP_TXON' +CMIS_STATE_READY = 'READY' +CMIS_STATE_REMOVED = 'REMOVED' +CMIS_STATE_FAILED = 'FAILED' + +CMIS_TERMINAL_STATES = { + CMIS_STATE_FAILED, + CMIS_STATE_READY, + CMIS_STATE_REMOVED + } # Mgminit time required as per CMIS spec MGMT_INIT_TIME_DELAY_SECS = 2 @@ -697,6 +714,14 @@ def update_port_transceiver_status_table_sw(logical_port_name, status_tbl, statu fvs = swsscommon.FieldValuePairs([('status', status), ('error', error_descriptions)]) status_tbl.set(logical_port_name, fvs) +def get_cmis_state_from_state_db(lport, status_tbl): + found, transceiver_status_dict = status_tbl.get(lport) + if found and 'cmis_state' in dict(transceiver_status_dict): + return dict(transceiver_status_dict)['cmis_state'] + else: + return CMIS_STATE_UNKNOWN + + # Update port SFP status table for HW fields @@ -765,17 +790,6 @@ class CmisManagerTask(threading.Thread): CMIS_MODULE_TYPES = ['QSFP-DD', 'QSFP_DD', 'OSFP', 'OSFP-8X', 'QSFP+C'] CMIS_MAX_HOST_LANES = 8 - CMIS_STATE_UNKNOWN = 'UNKNOWN' - CMIS_STATE_INSERTED = 'INSERTED' - CMIS_STATE_DP_DEINIT = 'DP_DEINIT' - CMIS_STATE_AP_CONF = 'AP_CONFIGURED' - CMIS_STATE_DP_ACTIVATE = 'DP_ACTIVATION' - CMIS_STATE_DP_INIT = 'DP_INIT' - CMIS_STATE_DP_TXON = 'DP_TXON' - CMIS_STATE_READY = 'READY' - CMIS_STATE_REMOVED = 'REMOVED' - CMIS_STATE_FAILED = 'FAILED' - def __init__(self, namespaces, port_mapping, main_thread_stop_event, skip_cmis_mgr=False): threading.Thread.__init__(self) self.name = "CmisManagerTask" @@ -799,6 +813,17 @@ def log_notice(self, message): def log_error(self, message): helper_logger.log_error("CMIS: {}".format(message)) + 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) + if status_table is None: + helper_logger.log_error("status_table is None while updating " + "sw CMIS state for lport {}".format(lport)) + return + + fvs = swsscommon.FieldValuePairs([('cmis_state', cmis_state_to_set)]) + status_table.set(lport, fvs) + def on_port_update_event(self, port_change_event): if port_change_event.event_type not in [port_change_event.PORT_SET, port_change_event.PORT_DEL]: return @@ -850,8 +875,7 @@ def on_port_update_event(self, port_change_event): self.force_cmis_reinit(lport, 0) else: - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_REMOVED - + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_REMOVED) def get_cmis_dp_init_duration_secs(self, api): return api.get_datapath_init_duration()/1000 @@ -999,7 +1023,7 @@ def force_cmis_reinit(self, lport, retries=0): """ Try to force the restart of CMIS state machine """ - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_INSERTED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_INSERTED) self.port_dict[lport]['cmis_retries'] = retries self.port_dict[lport]['cmis_expired'] = None # No expiration @@ -1228,6 +1252,10 @@ def task_worker(self): 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) + # APPL_DB for CONFIG updates, and STATE_DB for insertion/removal port_change_observer = PortChangeObserver(self.namespaces, helper_logger, self.task_stopping_event, @@ -1244,12 +1272,9 @@ def task_worker(self): if lport not in self.port_dict: continue - state = self.port_dict[lport].get('cmis_state', self.CMIS_STATE_UNKNOWN) - if state in [self.CMIS_STATE_UNKNOWN, - self.CMIS_STATE_FAILED, - self.CMIS_STATE_READY, - self.CMIS_STATE_REMOVED]: - if state != self.CMIS_STATE_READY: + 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))) + if state in CMIS_TERMINAL_STATES or state == CMIS_STATE_UNKNOWN: + if state != CMIS_STATE_READY: self.port_dict[lport]['appl'] = 0 self.port_dict[lport]['host_lanes_mask'] = 0 continue @@ -1276,7 +1301,7 @@ def task_worker(self): # double-check the HW presence before moving forward sfp = platform_chassis.get_sfp(pport) if not sfp.get_presence(): - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_REMOVED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_REMOVED) continue try: @@ -1284,19 +1309,19 @@ def task_worker(self): api = sfp.get_xcvr_api() if api is None: self.log_error("{}: skipping CMIS state machine since no xcvr api!!!".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) continue # Skip if it's not a paged memory device if api.is_flat_memory(): self.log_notice("{}: skipping CMIS state machine for flat memory xcvr".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) continue # Skip if it's not a CMIS module type = api.get_module_type_abbreviation() if (type is None) or (type not in self.CMIS_MODULE_TYPES): - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) continue if api.is_coherent_module(): @@ -1306,7 +1331,7 @@ def task_worker(self): self.port_dict[lport]['laser_freq'] = self.get_configured_laser_freq_from_db(lport) except AttributeError: # Skip if these essential routines are not available - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) continue # CMIS expiration and retries @@ -1319,10 +1344,10 @@ def task_worker(self): retries = self.port_dict[lport].get('cmis_retries', 0) host_lanes_mask = self.port_dict[lport].get('host_lanes_mask', 0) appl = self.port_dict[lport].get('appl', 0) - if state != self.CMIS_STATE_INSERTED and (host_lanes_mask <= 0 or appl < 1): + if state != CMIS_STATE_INSERTED and (host_lanes_mask <= 0 or appl < 1): self.log_error("{}: Unexpected value for host_lanes_mask {} or appl {} in " "{} state".format(lport, host_lanes_mask, appl, state)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_FAILED) continue self.log_notice("{}: {}G, lanemask=0x{:x}, state={}, appl {} host_lane_count {} " @@ -1330,17 +1355,17 @@ def task_worker(self): state, appl, host_lane_count, retries)) if retries > self.CMIS_MAX_RETRIES: self.log_error("{}: FAILED".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_FAILED) continue try: # CMIS state transitions - if state == self.CMIS_STATE_INSERTED: + if state == CMIS_STATE_INSERTED: self.port_dict[lport]['appl'] = get_cmis_application_desired(api, host_lane_count, host_speed) if self.port_dict[lport]['appl'] is None: self.log_error("{}: no suitable app for the port appl {} host_lane_count {} " "host_speed {}".format(lport, appl, host_lane_count, host_speed)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_FAILED) continue appl = self.port_dict[lport]['appl'] self.log_notice("{}: Setting appl={}".format(lport, appl)) @@ -1350,7 +1375,7 @@ def task_worker(self): if self.port_dict[lport]['host_lanes_mask'] <= 0: self.log_error("{}: Invalid lane mask received - host_lane_count {} subport {} " "appl {}!".format(lport, host_lane_count, subport, appl)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_FAILED) continue host_lanes_mask = self.port_dict[lport]['host_lanes_mask'] self.log_notice("{}: Setting host_lanemask=0x{:x}".format(lport, host_lanes_mask)) @@ -1365,7 +1390,7 @@ def task_worker(self): self.log_error("{}: Invalid media lane mask received - media_lane_count {} " "media_lane_assignment_options {} subport {}" " appl {}!".format(lport, media_lane_count, media_lane_assignment_options, subport, appl)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_FAILED) continue media_lanes_mask = self.port_dict[lport]['media_lanes_mask'] self.log_notice("{}: Setting media_lanemask=0x{:x}".format(lport, media_lanes_mask)) @@ -1375,7 +1400,7 @@ def task_worker(self): self.log_notice("{} Forcing Tx laser OFF".format(lport)) # Force DataPath re-init api.tx_disable_channel(media_lanes_mask, True) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) continue # Configure the target output power if ZR module if api.is_coherent_module(): @@ -1400,11 +1425,11 @@ def task_worker(self): if not need_update: # No application updates self.log_notice("{}: no CMIS application update required...READY".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) continue self.log_notice("{}: force Datapath reinit".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_DEINIT - elif state == self.CMIS_STATE_DP_DEINIT: + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_DP_DEINIT) + elif state == CMIS_STATE_DP_DEINIT: # D.2.2 Software Deinitialization api.set_datapath_deinit(host_lanes_mask) @@ -1417,13 +1442,13 @@ def task_worker(self): #Sets module to high power mode and doesn't impact datapath if module is already in high power mode api.set_lpmode(False) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_AP_CONF + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_AP_CONF) dpDeinitDuration = self.get_cmis_dp_deinit_duration_secs(api) modulePwrUpDuration = self.get_cmis_module_power_up_duration_secs(api) self.log_notice("{}: DpDeinit duration {} secs, modulePwrUp duration {} secs".format(lport, dpDeinitDuration, modulePwrUpDuration)) self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds = max(modulePwrUpDuration, dpDeinitDuration)) - elif state == self.CMIS_STATE_AP_CONF: + elif state == CMIS_STATE_AP_CONF: # Explicit control bit to apply custom Host SI settings. # It will be set to 1 and applied via set_application if # custom SI settings is applicable @@ -1482,8 +1507,8 @@ def task_worker(self): self.force_cmis_reinit(lport, retries + 1) continue - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_INIT - elif state == self.CMIS_STATE_DP_INIT: + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_DP_INIT) + elif state == CMIS_STATE_DP_INIT: if not self.check_config_error(api, host_lanes_mask, ['ConfigSuccess']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'ConfigSuccess'".format(lport)) @@ -1512,8 +1537,8 @@ def task_worker(self): dpInitDuration = self.get_cmis_dp_init_duration_secs(api) self.log_notice("{}: DpInit duration {} secs".format(lport, dpInitDuration)) self.port_dict[lport]['cmis_expired'] = now + datetime.timedelta(seconds=dpInitDuration) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_TXON - elif state == self.CMIS_STATE_DP_TXON: + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_DP_TXON) + elif state == CMIS_STATE_DP_TXON: if not self.check_datapath_state(api, host_lanes_mask, ['DataPathInitialized']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'DataPathInitialized'".format(lport)) @@ -1524,8 +1549,8 @@ def task_worker(self): media_lanes_mask = self.port_dict[lport]['media_lanes_mask'] api.tx_disable_channel(media_lanes_mask, False) self.log_notice("{}: Turning ON tx power".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_DP_ACTIVATE - elif state == self.CMIS_STATE_DP_ACTIVATE: + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_DP_ACTIVATE) + elif state == CMIS_STATE_DP_ACTIVATE: if not self.check_datapath_state(api, host_lanes_mask, ['DataPathActivated']): if (expired is not None) and (expired <= now): self.log_notice("{}: timeout for 'DataPathActivated'".format(lport)) @@ -1533,12 +1558,12 @@ def task_worker(self): continue self.log_notice("{}: READY".format(lport)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_READY + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_READY) self.post_port_active_apsel_to_db(api, lport, host_lanes_mask) except (NotImplementedError, AttributeError) as e: self.log_error("{}: internal errors due to {}".format(lport, e)) - self.port_dict[lport]['cmis_state'] = self.CMIS_STATE_FAILED + self.update_port_transceiver_status_table_sw_cmis_state(lport, CMIS_STATE_FAILED) self.log_notice("Stopped") @@ -1574,7 +1599,7 @@ def join(self): class DomInfoUpdateTask(threading.Thread): - def __init__(self, namespaces, port_mapping, main_thread_stop_event): + def __init__(self, namespaces, port_mapping, main_thread_stop_event, skip_cmis_mgr): threading.Thread.__init__(self) self.name = "DomInfoUpdateTask" self.exc = None @@ -1582,6 +1607,7 @@ def __init__(self, namespaces, port_mapping, main_thread_stop_event): self.main_thread_stop_event = main_thread_stop_event self.port_mapping = copy.deepcopy(port_mapping) self.namespaces = namespaces + self.skip_cmis_mgr = skip_cmis_mgr def get_dom_polling_from_config_db(self, lport): """ @@ -1619,8 +1645,35 @@ def get_dom_polling_from_config_db(self, lport): return dom_polling + """ + Checks if the port is going through CMIS initialization process + This API assumes CMIS_STATE_UNKNOWN as a transitional state since it is the + first state after starting CMIS state machine. + This assumption allows the DomInfoUpdateTask thread to skip polling on the port + to allow CMIS initialization to complete if needed. + Returns: + True if the port is in CMIS initialization process, + otherwise False + """ + def is_port_in_cmis_initialization_process(self, logical_port_name): + # If CMIS manager is not available for the platform, return False + if self.skip_cmis_mgr: + return False + + asic_index = self.port_mapping.get_asic_id_for_logical_port(logical_port_name) + if asic_index is None: + helper_logger.log_warning("Got invalid asic index for {} while checking cmis init status".format(logical_port_name)) + return False + + cmis_state = get_cmis_state_from_state_db(logical_port_name, self.xcvr_table_helper.get_status_tbl(asic_index)) + if cmis_state not in CMIS_TERMINAL_STATES: + return True + else: + return False + def is_port_dom_monitoring_disabled(self, logical_port_name): - return self.get_dom_polling_from_config_db(logical_port_name) == 'disabled' + return self.get_dom_polling_from_config_db(logical_port_name) == 'disabled' or \ + self.is_port_in_cmis_initialization_process(logical_port_name) def task_worker(self): self.xcvr_table_helper = XcvrTableHelper(self.namespaces) @@ -2412,13 +2465,14 @@ def run(self): self.log_notice("Skipping SFF Task Manager") # Start the CMIS manager - cmis_manager = CmisManagerTask(self.namespaces, port_mapping_data, self.stop_event, self.skip_cmis_mgr) + cmis_manager = None if not self.skip_cmis_mgr: + cmis_manager = CmisManagerTask(self.namespaces, port_mapping_data, self.stop_event, self.skip_cmis_mgr) cmis_manager.start() self.threads.append(cmis_manager) # Start the dom sensor info update thread - dom_info_update = DomInfoUpdateTask(self.namespaces, port_mapping_data, self.stop_event) + dom_info_update = DomInfoUpdateTask(self.namespaces, port_mapping_data, self.stop_event, self.skip_cmis_mgr) dom_info_update.start() self.threads.append(dom_info_update)