diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index a25d742ea..80e03b3a9 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -1386,7 +1386,11 @@ def mock_get_transceiver_status_flags(physical_port): 'prefec_ber_max': '0.0006833674050752236', 'uncorr_frames_avg': '0.0', 'uncorr_frames_min': '0.0', - 'uncorr_frames_max': '0.0', })) + 'uncorr_frames_max': '0.0', + 'rx_bits_pm': '1000000', + 'rx_corr_bits_pm': '1000', + 'tx_bits_pm': '2000000', + 'tx_frames_pm': '20000', })) def test_post_port_pm_info_to_db(self): logical_port_name = "Ethernet0" port_mapping = PortMapping() @@ -1395,9 +1399,14 @@ def test_post_port_pm_info_to_db(self): mock_cmis_manager = MagicMock() dom_info_update = DomInfoUpdateTask(DEFAULT_NAMESPACE, port_mapping, mock_sfp_obj_dict, stop_event, mock_cmis_manager) pm_tbl = Table("STATE_DB", TRANSCEIVER_PM_TABLE) + pm_counters_tbl = Table("STATE_DB", TRANSCEIVER_PM_COUNTERS_TABLE) assert pm_tbl.get_size() == 0 - dom_info_update.post_port_pm_info_to_db(logical_port_name, port_mapping, pm_tbl, stop_event) + assert pm_counters_tbl.get_size() == 0 + dom_info_update.post_port_pm_info_to_db(logical_port_name, port_mapping, pm_tbl, pm_counters_tbl, stop_event) + # Non-counter fields (6 preFEC ratios) go to TRANSCEIVER_PM. assert pm_tbl.get_size_for_key(logical_port_name) == 6 + # Raw FEC counters (2 rx + 2 tx) go to TRANSCEIVER_PM_COUNTERS. + assert pm_counters_tbl.get_size_for_key(logical_port_name) == 4 @patch('xcvrd.xcvrd_utilities.port_event_helper.PortMapping.logical_port_name_to_physical_port_list', MagicMock(return_value=[0])) @patch('xcvrd.xcvrd_utilities.common._wrapper_get_presence', MagicMock(return_value=True)) diff --git a/sonic-xcvrd/xcvrd/dom/dom_mgr.py b/sonic-xcvrd/xcvrd/dom/dom_mgr.py index 2ca37dcf7..486c89eb7 100644 --- a/sonic-xcvrd/xcvrd/dom/dom_mgr.py +++ b/sonic-xcvrd/xcvrd/dom/dom_mgr.py @@ -36,6 +36,23 @@ PORT_UPDATE_EVENT_SELECT_TIMEOUT_MSECS = 1000 PORT_UPDATE_EVENT_SELECT_TIMEOUT_FAST_MSECS = 10 +# Raw FEC counter fields from get_transceiver_pm() that are published to the +# TRANSCEIVER_PM_COUNTERS table instead of TRANSCEIVER_PM. This is a positive list: +# any field not named here (calculated ratios, optical metrics) stays in TRANSCEIVER_PM. +PM_COUNTER_FIELDS = frozenset({ + # media (page 34h) RX FEC counters + 'rx_bits_pm', 'rx_bits_subint_pm', 'rx_corr_bits_pm', + 'rx_min_corr_bits_subint_pm', 'rx_max_corr_bits_subint_pm', + 'rx_frames_pm', 'rx_frames_subint_pm', 'rx_frames_uncorr_err_pm', + 'rx_min_frames_uncorr_err_subint_pm', 'rx_max_frames_uncorr_err_subint_pm', + # host (page 3Ah) TX FEC counters + 'tx_bits_pm', 'tx_bits_subint_pm', 'tx_corr_bits_pm', + 'tx_min_corr_bits_subint_pm', 'tx_max_corr_bits_subint_pm', + 'tx_frames_pm', 'tx_frames_subint_pm', 'tx_frames_uncorr_err_pm', + 'tx_min_frames_uncorr_err_subint_pm', 'tx_max_frames_uncorr_err_subint_pm', + 'tx_corrected_frames_pm', 'tx_corrected_frames_subint_pm', +}) + class DomInfoUpdateBase(threading.Thread): name = '' @@ -235,7 +252,7 @@ def post_port_sfp_firmware_info_to_db(self, logical_port_name, port_mapping, tab sys.exit(xcvrd.NOT_IMPLEMENTED_ERROR) # Update port pm info in db - def post_port_pm_info_to_db(self, logical_port_name, port_mapping, table, stop_event=threading.Event(), pm_info_cache=None): + def post_port_pm_info_to_db(self, logical_port_name, port_mapping, pm_table, pm_counters_table, stop_event=threading.Event(), pm_info_cache=None): for physical_port, physical_port_name in common.get_physical_port_name_dict(logical_port_name, port_mapping).items(): if stop_event.is_set(): break @@ -259,8 +276,14 @@ def post_port_pm_info_to_db(self, logical_port_name, port_mapping, table, stop_e if not pm_info_dict: continue self.db_utils.beautify_info_dict(pm_info_dict) - fvs = swsscommon.FieldValuePairs([(k, v) for k, v in pm_info_dict.items()]) - table.set(physical_port_name, fvs) + # Route raw FEC counters to TRANSCEIVER_PM_COUNTERS; everything else + # (calculated ratios, optical metrics) stays in TRANSCEIVER_PM. + pm_dict = {k: v for k, v in pm_info_dict.items() if k not in PM_COUNTER_FIELDS} + pm_counters_dict = {k: v for k, v in pm_info_dict.items() if k in PM_COUNTER_FIELDS} + if pm_dict: + pm_table.set(physical_port_name, swsscommon.FieldValuePairs(list(pm_dict.items()))) + if pm_counters_dict: + pm_counters_table.set(physical_port_name, swsscommon.FieldValuePairs(list(pm_counters_dict.items()))) else: return xcvrd.SFP_EEPROM_NOT_READY @@ -395,7 +418,7 @@ def task_worker(self): except (KeyError, TypeError) as e: self.log_warning("Got exception {} while processing vdm statistic values for port {}, ignored".format(repr(e), logical_port_name)) try: - self.post_port_pm_info_to_db(logical_port_name, self.port_mapping, self.xcvr_table_helper.get_pm_tbl(asic_index), self.task_stopping_event) + self.post_port_pm_info_to_db(logical_port_name, self.port_mapping, self.xcvr_table_helper.get_pm_tbl(asic_index), self.xcvr_table_helper.get_pm_counters_tbl(asic_index), self.task_stopping_event) except (KeyError, TypeError) as e: self.log_warning("Got exception {} while posting pm info to DB for port {}, ignored".format(repr(e), logical_port_name)) @@ -520,6 +543,7 @@ def on_remove_logical_port(self, port_change_event): self.xcvr_table_helper.get_status_flag_set_time_tbl(port_change_event.asic_id), self.xcvr_table_helper.get_status_flag_clear_time_tbl(port_change_event.asic_id), self.xcvr_table_helper.get_pm_tbl(port_change_event.asic_id), + self.xcvr_table_helper.get_pm_counters_tbl(port_change_event.asic_id), self.xcvr_table_helper.get_firmware_info_tbl(port_change_event.asic_id) ]) diff --git a/sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py b/sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py index 71d62b701..ed54a4481 100644 --- a/sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py +++ b/sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py @@ -45,6 +45,8 @@ TRANSCEIVER_VDM_HWARN_FLAG_CLEAR_TIME = 'TRANSCEIVER_VDM_HWARN_FLAG_CLEAR_TIME' TRANSCEIVER_VDM_LWARN_FLAG_CLEAR_TIME = 'TRANSCEIVER_VDM_LWARN_FLAG_CLEAR_TIME' TRANSCEIVER_PM_TABLE = 'TRANSCEIVER_PM' +TRANSCEIVER_PM_COUNTERS_TABLE = 'TRANSCEIVER_PM_COUNTERS' +TRANSCEIVER_CMIS_STATE_TIMESTAMPS_TABLE = 'TRANSCEIVER_CMIS_STATE_TIMESTAMPS' NPU_SI_SETTINGS_SYNC_STATUS_KEY = 'NPU_SI_SETTINGS_SYNC_STATUS' NPU_SI_SETTINGS_DEFAULT_VALUE = 'NPU_SI_SETTINGS_DEFAULT' @@ -69,6 +71,8 @@ def __init__(self, namespaces): self.status_flag_set_time_tbl = {} self.status_flag_clear_time_tbl = {} self.status_sw_tbl = {} + self.cmis_state_timestamps_tbl = {} + self.pm_counters_tbl = {} self.vdm_real_value_tbl = {} VDM_THRESHOLD_TYPES = ['halarm', 'lalarm', 'hwarn', 'lwarn'] self.vdm_threshold_tbl = {f'vdm_{t}_threshold_tbl': {} for t in VDM_THRESHOLD_TYPES} @@ -94,6 +98,7 @@ def __init__(self, namespaces): self.status_flag_clear_time_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_STATUS_FLAG_CLEAR_TIME_TABLE) self.status_sw_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_STATUS_SW_TABLE) self.pm_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_PM_TABLE) + self.pm_counters_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_PM_COUNTERS_TABLE) self.firmware_info_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_FIRMWARE_INFO_TABLE) self.state_port_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], swsscommon.STATE_PORT_TABLE_NAME) self.appl_db[asic_id] = daemon_base.db_connect("APPL_DB", namespace) @@ -171,6 +176,9 @@ def get_vdm_flag_clear_time_tbl(self, asic_id, threshold_type): def get_pm_tbl(self, asic_id): return self.pm_tbl[asic_id] + def get_pm_counters_tbl(self, asic_id): + return self.pm_counters_tbl[asic_id] + def get_firmware_info_tbl(self, asic_id): return self.firmware_info_tbl[asic_id]