Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions sonic-xcvrd/tests/test_xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -1395,9 +1399,15 @@ 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_tbl.get_size_for_key(logical_port_name) == 6
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)
# All fields (6 preFEC ratios + 4 raw FEC counters) go to TRANSCEIVER_PM
# for backward compatibility with existing streaming telemetry consumers.
assert pm_tbl.get_size_for_key(logical_port_name) == 10
# Raw FEC counters (2 rx + 2 tx) are additionally posted 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))
Expand All @@ -1408,9 +1418,11 @@ def test_del_port_sfp_dom_info_from_db(self):
dom_threshold_tbl = Table("STATE_DB", TRANSCEIVER_DOM_THRESHOLD_TABLE)
init_tbl = Table("STATE_DB", TRANSCEIVER_INFO_TABLE)
pm_tbl = Table("STATE_DB", TRANSCEIVER_PM_TABLE)
pm_counters_tbl = Table("STATE_DB", TRANSCEIVER_PM_COUNTERS_TABLE)
firmware_info_tbl = Table("STATE_DB", TRANSCEIVER_FIRMWARE_INFO_TABLE)
common.del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, [init_tbl, dom_tbl, dom_threshold_tbl, pm_tbl, firmware_info_tbl])
common.del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, [init_tbl, dom_tbl, dom_threshold_tbl, pm_tbl, pm_counters_tbl, firmware_info_tbl])
assert dom_tbl.get_size() == 0
assert pm_counters_tbl.get_size() == 0

@pytest.mark.parametrize("mock_found, mock_state, expected_cmis_state", [
(True, CMIS_STATE_INSERTED, CMIS_STATE_INSERTED),
Expand Down
43 changes: 34 additions & 9 deletions sonic-xcvrd/xcvrd/dom/dom_mgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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. This is a positive list: any field not named
# here (calculated ratios, optical metrics) stays in TRANSCEIVER_PM.
PM_COUNTER_FIELDS = frozenset({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nkanchi-nexthop there are streaming telemetry applications already using these counters from TRANSCEIVER_PM so this change will break those. To maintain backward compatibility, I would suggest to not remove these fields from old PM table

# 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 = ''
Expand Down Expand Up @@ -235,10 +252,11 @@ 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=None, pm_info_cache=None):
if stop_event is None:
stop_event = threading.Event()

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

if not common._wrapper_get_presence(physical_port):
continue
Expand All @@ -259,8 +277,13 @@ 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)
# Publish all PM fields and counters to TRANSCEIVER_PM.
# Additionally publish the raw FEC counters to TRANSCEIVER_PM_COUNTERS.
pm_counters_dict = {k: v for k, v in pm_info_dict.items() if k in PM_COUNTER_FIELDS}
if pm_info_dict:
pm_table.set(physical_port_name, swsscommon.FieldValuePairs(list(pm_info_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

Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -498,9 +521,10 @@ def on_remove_logical_port(self, port_change_event):
Args:
port_change_event (object): port change event
"""
# To avoid race condition, remove the entry TRANSCEIVER_FIRMWARE_INFO, TRANSCEIVER_DOM_SENSOR, TRANSCEIVER_PM and HW section of TRANSCEIVER_STATUS table.
# This thread only updates TRANSCEIVER_FIRMWARE_INFO, TRANSCEIVER_DOM_SENSOR, TRANSCEIVER_PM and HW section of TRANSCEIVER_STATUS table,
# so we don't have to remove entries from TRANSCEIVER_INFO, TRANSCEIVER_DOM_THRESHOLD and VDM threshold value tables.
# To avoid race condition, remove the entry TRANSCEIVER_FIRMWARE_INFO, TRANSCEIVER_DOM_SENSOR, TRANSCEIVER_PM, TRANSCEIVER_PM_COUNTERS,
# and HW section of TRANSCEIVER_STATUS table. This thread only updates TRANSCEIVER_FIRMWARE_INFO, TRANSCEIVER_DOM_SENSOR, TRANSCEIVER_PM,
# TRANSCEIVER_PM_COUNTERS and HW section of TRANSCEIVER_STATUS table, so we don't have to remove entries from TRANSCEIVER_INFO,
# TRANSCEIVER_DOM_THRESHOLD and VDM threshold value tables.
common.del_port_sfp_dom_info_from_db(port_change_event.port_name,
self.port_mapping,
[self.xcvr_table_helper.get_dom_tbl(port_change_event.asic_id),
Expand All @@ -520,6 +544,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)
])

Expand Down
6 changes: 6 additions & 0 deletions sonic-xcvrd/xcvrd/xcvrd_utilities/xcvr_table_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
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'

NPU_SI_SETTINGS_SYNC_STATUS_KEY = 'NPU_SI_SETTINGS_SYNC_STATUS'
NPU_SI_SETTINGS_DEFAULT_VALUE = 'NPU_SI_SETTINGS_DEFAULT'
Expand All @@ -69,6 +70,7 @@ def __init__(self, namespaces):
self.status_flag_set_time_tbl = {}
self.status_flag_clear_time_tbl = {}
self.status_sw_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}
Expand All @@ -94,6 +96,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)
Expand Down Expand Up @@ -171,6 +174,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]

Expand Down
Loading