diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c67fc77..4134be1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,7 +18,7 @@ pr: include: - '*' -pool: sonic-common +pool: sonictest resources: containers: diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 705941c..066383b 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -2102,6 +2102,47 @@ def test_SffManagerTask_get_admin_status(self, mock_get_cfg_port_tbl): mock_get_cfg_port_tbl.assert_called_once_with(0) mock_get_cfg_port_tbl.return_value.hget.assert_called_once_with(lport, 'admin_status') + @patch('xcvrd.xcvrd.helper_logger') + @patch('xcvrd.xcvrd.platform_chassis') + @patch('xcvrd.sff_mgr.PortChangeObserver') + def test_SffManagerTask_xcvr_api_none_in_task_worker(self, mock_observer, mock_chassis, mock_logger): + """Test the full task_worker flow when xcvr API is None""" + mock_observer_instance = MagicMock() + mock_observer_instance.handle_port_update_event = MagicMock(side_effect=[True, False]) + mock_observer.return_value = mock_observer_instance + + # Setup mock SFP that returns None for get_xcvr_api + mock_sfp = MagicMock() + mock_sfp.get_presence = MagicMock(return_value=True) + mock_sfp.get_xcvr_api = MagicMock(return_value=None) + mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) + + sff_manager_task = SffManagerTask(DEFAULT_NAMESPACE, + threading.Event(), + mock_chassis, + mock_logger) + + # Setup port_dict with necessary data + sff_manager_task.port_dict['Ethernet0'] = { + 'index': 1, + 'type': 'QSFP28', + 'subport': '0', + 'lanes': ['1', '2', '3', '4'], + 'host_tx_ready': 'true', + 'admin_status': 'up', + 'asic_id': 0 + } + + # Mock task_stopping_event to stop after processing once + sff_manager_task.task_stopping_event.is_set = MagicMock(side_effect=[False, False, True]) + + # Run task_worker - it should handle the None API gracefully + sff_manager_task.task_worker() + + # Verify error was logged + assert any("skipping sff_mgr since no xcvr api!" in str(call) + for call in mock_logger.log_error.call_args_list) + @patch('xcvrd.xcvrd.platform_chassis') @patch('xcvrd.sff_mgr.PortChangeObserver', MagicMock(handle_port_update_event=MagicMock())) def test_SffManagerTask_task_worker(self, mock_chassis): diff --git a/sonic-xcvrd/xcvrd/sff_mgr.py b/sonic-xcvrd/xcvrd/sff_mgr.py index bd6948a..869d826 100644 --- a/sonic-xcvrd/xcvrd/sff_mgr.py +++ b/sonic-xcvrd/xcvrd/sff_mgr.py @@ -14,6 +14,7 @@ from .xcvrd_utilities.port_event_helper import PortChangeObserver from .xcvrd_utilities.xcvr_table_helper import XcvrTableHelper + from xcvrd import xcvrd from sonic_platform_base.sonic_xcvr.api.public.sff8472 import Sff8472Api except ImportError as e: raise ImportError(str(e) + " - required module not found") @@ -348,8 +349,25 @@ def task_worker(self): # TRANSCEIVER_INFO table's XCVR_TYPE is not ready, meaning xcvr is not present continue + # double-check the HW presence before moving forward + sfp = self.platform_chassis.get_sfp(pport) + if not sfp.get_presence(): + self.log_error("{}: module not present!".format(lport)) + del self.port_dict[lport][self.XCVR_TYPE] + continue + try: + # Skip if XcvrApi is not supported + api = sfp.get_xcvr_api() + if api is None: + self.log_error( + "{}: skipping sff_mgr since no xcvr api!".format(lport)) + continue + except (AttributeError, NotImplementedError): + # Skip if these essential routines are not available + continue + # Procced only for QSFP28/QSFP+ transceiver - if not (xcvr_type.startswith('QSFP28') or xcvr_type.startswith('QSFP+')): + if not (xcvr_type.startswith('QSFP28') or xcvr_type.startswith('QSFP+')) or xcvrd.is_cmis_api(api): continue # Handle the case that host_tx_ready value in the local cache hasn't @@ -403,20 +421,7 @@ def task_worker(self): data[self.HOST_TX_READY], host_tx_ready_changed, data[self.ADMIN_STATUS], admin_status_changed)) - # double-check the HW presence before moving forward - sfp = self.platform_chassis.get_sfp(pport) - if not sfp.get_presence(): - self.log_error("{}: module not present!".format(lport)) - del self.port_dict[lport][self.XCVR_TYPE] - continue try: - # Skip if XcvrApi is not supported - api = sfp.get_xcvr_api() - if api is None: - self.log_error( - "{}: skipping sff_mgr since no xcvr api!".format(lport)) - continue - # Skip if it's not a paged memory device if api.is_flat_memory(): self.log_notice(