From 9b9fe4d2dcf7f944a084ae0ae1c0c94af8a65615 Mon Sep 17 00:00:00 2001 From: longhuan-cisco <84595962+longhuan-cisco@users.noreply.github.com> Date: Mon, 7 Nov 2022 21:06:23 -0800 Subject: [PATCH] Add TRANSCEIVER_PM table in STATE_DB for C_CMIS (#3) * Add pm tbl to STATE_DB * Use unique info in post_port_pm_info_to_db log --- sonic-xcvrd/tests/test_xcvrd.py | 52 +++++++++++++-- sonic-xcvrd/xcvrd/xcvrd.py | 114 ++++++++++++++++++++++++-------- 2 files changed, 134 insertions(+), 32 deletions(-) diff --git a/sonic-xcvrd/tests/test_xcvrd.py b/sonic-xcvrd/tests/test_xcvrd.py index 55c7fcd94..9f9c8fee8 100644 --- a/sonic-xcvrd/tests/test_xcvrd.py +++ b/sonic-xcvrd/tests/test_xcvrd.py @@ -80,6 +80,23 @@ def test_post_port_dom_info_to_db(self, mock_get_sfp_type): mock_get_sfp_type.return_value = 'QSFP_DD' post_port_dom_info_to_db(logical_port_name, port_mapping, dom_tbl, stop_event) + @patch('xcvrd.xcvrd_utilities.port_mapping.PortMapping.logical_port_name_to_physical_port_list', MagicMock(return_value=[0])) + @patch('xcvrd.xcvrd._wrapper_get_presence', MagicMock(return_value=True)) + @patch('xcvrd.xcvrd._wrapper_get_transceiver_pm', MagicMock(return_value={'prefec_ber_avg': '0.0003407240007014899', + 'prefec_ber_min': '0.0006814479342250317', + 'prefec_ber_max': '0.0006833674050752236', + 'uncorr_frames_avg': '0.0', + 'uncorr_frames_min': '0.0', + 'uncorr_frames_max': '0.0', })) + def test_post_port_pm_info_to_db(self): + logical_port_name = "Ethernet0" + port_mapping = PortMapping() + stop_event = threading.Event() + pm_tbl = Table("STATE_DB", TRANSCEIVER_PM_TABLE) + assert pm_tbl.get_size() == 0 + 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 + @patch('xcvrd.xcvrd_utilities.port_mapping.PortMapping.logical_port_name_to_physical_port_list', MagicMock(return_value=[0])) @patch('xcvrd.xcvrd._wrapper_get_presence', MagicMock(return_value=True)) def test_del_port_sfp_dom_info_from_db(self): @@ -87,7 +104,8 @@ def test_del_port_sfp_dom_info_from_db(self): port_mapping = PortMapping() dom_tbl = Table("STATE_DB", TRANSCEIVER_DOM_SENSOR_TABLE) init_tbl = Table("STATE_DB", TRANSCEIVER_INFO_TABLE) - del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, init_tbl, dom_tbl) + pm_tbl = Table("STATE_DB", TRANSCEIVER_PM_TABLE) + del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, init_tbl, dom_tbl, pm_tbl) @patch('xcvrd.xcvrd.get_physical_port_name_dict', MagicMock(return_value={0: 'Ethernet0'})) @patch('xcvrd.xcvrd._wrapper_get_presence', MagicMock(return_value=True)) @@ -703,8 +721,10 @@ def test_DomInfoUpdateTask_task_run_stop(self): @patch('swsscommon.swsscommon.SubscriberStateTable') @patch('swsscommon.swsscommon.Select.select') @patch('xcvrd.xcvrd.update_port_transceiver_status_table_hw') - def test_DomInfoUpdateTask_task_worker(self, mock_update_status_hw, mock_select, mock_sub_table, mock_post_dom_th, - mock_post_dom_info, mock_detect_error): + @patch('xcvrd.xcvrd.post_port_pm_info_to_db') + def test_DomInfoUpdateTask_task_worker(self, mock_post_pm_info, mock_update_status_hw, + mock_select, mock_sub_table, mock_post_dom_th, + mock_post_dom_info, mock_detect_error): mock_selectable = MagicMock() mock_selectable.pop = MagicMock( side_effect=[('Ethernet0', swsscommon.SET_COMMAND, (('index', '1'), )), (None, None, None), (None, None, None)]) @@ -724,12 +744,14 @@ def test_DomInfoUpdateTask_task_worker(self, mock_update_status_hw, mock_select, assert mock_post_dom_th.call_count == 0 assert mock_post_dom_info.call_count == 0 assert mock_update_status_hw.call_count == 0 + assert mock_post_pm_info.call_count == 0 mock_detect_error.return_value = False task.task_stopping_event.wait = MagicMock(side_effect=[False, True]) task.task_worker() assert mock_post_dom_th.call_count == 1 assert mock_post_dom_info.call_count == 1 assert mock_update_status_hw.call_count == 1 + assert mock_post_pm_info.call_count == 1 @patch('xcvrd.xcvrd._wrapper_get_presence', MagicMock(return_value=False)) @patch('xcvrd.xcvrd.XcvrTableHelper') @@ -798,6 +820,7 @@ def test_SfpStateUpdateTask_retry_eeprom_reading(self, mock_update_status_hw, mo task.xcvr_table_helper.get_dom_tbl = MagicMock(return_value=mock_table) task.xcvr_table_helper.get_app_port_tbl = MagicMock(return_value=mock_table) task.xcvr_table_helper.get_status_tbl = MagicMock(return_value=mock_table) + task.xcvr_table_helper.get_pm_tbl = MagicMock(return_value=mock_table) task.retry_eeprom_reading() assert mock_post_sfp_info.call_count == 0 @@ -853,9 +876,10 @@ def test_SfpStateUpdateTask_mapping_event_from_change_event(self): @patch('xcvrd.xcvrd.update_port_transceiver_status_table_sw') @patch('xcvrd.xcvrd.update_port_transceiver_status_table_hw') @patch('xcvrd.xcvrd.delete_port_from_status_table_hw') - def test_SfpStateUpdateTask_task_worker(self, mock_del_status_hw, mock_update_status_hw, mock_update_status, - mock_post_sfp_info, mock_post_dom_info, mock_post_dom_th, mock_update_media_setting, mock_del_dom, - mock_change_event, mock_mapping_event, mock_os_kill): + @patch('xcvrd.xcvrd.post_port_pm_info_to_db') + def test_SfpStateUpdateTask_task_worker(self, mock_post_pm_info, mock_del_status_hw, mock_update_status_hw, + mock_update_status, mock_post_sfp_info, mock_post_dom_info, mock_post_dom_th, mock_update_media_setting, + mock_del_dom, mock_change_event, mock_mapping_event, mock_os_kill): port_mapping = PortMapping() retry_eeprom_set = set() task = SfpStateUpdateTask(DEFAULT_NAMESPACE, port_mapping, retry_eeprom_set) @@ -909,6 +933,7 @@ def test_SfpStateUpdateTask_task_worker(self, mock_del_status_hw, mock_update_st assert mock_post_dom_info.call_count == 0 assert mock_post_dom_th.call_count == 0 assert mock_update_status_hw.call_count == 0 + assert mock_post_pm_info.call_count == 0 assert mock_update_media_setting.call_count == 0 assert 'Ethernet0' in task.retry_eeprom_set task.retry_eeprom_set.clear() @@ -924,6 +949,7 @@ def test_SfpStateUpdateTask_task_worker(self, mock_del_status_hw, mock_update_st assert mock_post_dom_info.call_count == 1 assert mock_post_dom_th.call_count == 1 assert mock_update_status_hw.call_count == 1 + assert mock_post_pm_info.call_count == 1 assert mock_update_media_setting.call_count == 1 stop_event.is_set = MagicMock(side_effect=[False, True]) @@ -1173,6 +1199,20 @@ def test_wrapper_get_transceiver_status(self, mock_chassis): mock_chassis.get_sfp = MagicMock(side_effect=NotImplementedError) assert _wrapper_get_transceiver_status(1) == {} + @patch('xcvrd.xcvrd.platform_chassis') + def test_wrapper_get_transceiver_pm(self, mock_chassis): + mock_object = MagicMock() + mock_object.get_transceiver_pm = MagicMock(return_value=True) + mock_chassis.get_sfp = MagicMock(return_value=mock_object) + from xcvrd.xcvrd import _wrapper_get_transceiver_pm + assert _wrapper_get_transceiver_pm(1) + + mock_object.get_transceiver_pm = MagicMock(return_value=False) + assert not _wrapper_get_transceiver_pm(1) + + mock_chassis.get_sfp = MagicMock(side_effect=NotImplementedError) + assert _wrapper_get_transceiver_pm(1) == {} + @patch('xcvrd.xcvrd.platform_chassis') @patch('xcvrd.xcvrd.platform_sfputil') def test_wrapper_get_transceiver_change_event(self, mock_sfputil, mock_chassis): diff --git a/sonic-xcvrd/xcvrd/xcvrd.py b/sonic-xcvrd/xcvrd/xcvrd.py index b76c1d3cf..978cd8f64 100644 --- a/sonic-xcvrd/xcvrd/xcvrd.py +++ b/sonic-xcvrd/xcvrd/xcvrd.py @@ -42,6 +42,7 @@ TRANSCEIVER_INFO_TABLE = 'TRANSCEIVER_INFO' TRANSCEIVER_DOM_SENSOR_TABLE = 'TRANSCEIVER_DOM_SENSOR' TRANSCEIVER_STATUS_TABLE = 'TRANSCEIVER_STATUS' +TRANSCEIVER_PM_TABLE = 'TRANSCEIVER_PM' TRANSCEIVER_STATUS_TABLE_SW_FIELDS = ["status", "error"] @@ -200,6 +201,15 @@ def _wrapper_get_transceiver_status(physical_port): return {} +def _wrapper_get_transceiver_pm(physical_port): + if platform_chassis is not None: + try: + return platform_chassis.get_sfp(physical_port).get_transceiver_pm() + except NotImplementedError: + pass + return {} + + # Soak SFP insert event until management init completes def _wrapper_soak_sfp_insert_event(sfp_insert_events, port_dict): for key, value in list(port_dict.items()): @@ -299,6 +309,13 @@ def beautify_transceiver_status_dict(transceiver_status_dict, physical_port): continue transceiver_status_dict[k] = str(v) + +def beautify_pm_info_dict(pm_info_dict, physical_port): + for k, v in pm_info_dict.items(): + if type(v) is str: + continue + pm_info_dict[k] = str(v) + # Update port sfp info in db @@ -530,6 +547,40 @@ def post_port_dom_info_to_db(logical_port_name, port_mapping, table, stop_event= helper_logger.log_error("This functionality is currently not implemented for this platform") sys.exit(NOT_IMPLEMENTED_ERROR) +# Update port pm info in db + + +def post_port_pm_info_to_db(logical_port_name, port_mapping, table, stop_event=threading.Event(), pm_info_cache=None): + for physical_port, physical_port_name in get_physical_port_name_dict(logical_port_name, port_mapping).items(): + if stop_event.is_set(): + break + + if not _wrapper_get_presence(physical_port): + continue + + try: + if pm_info_cache is not None and physical_port in pm_info_cache: + # If cache is enabled and pm info is in cache, just read from cache, no need read from EEPROM + pm_info_dict = pm_info_cache[physical_port] + else: + pm_info_dict = _wrapper_get_transceiver_pm(physical_port) + if pm_info_cache is not None: + # If cache is enabled, put dom information to cache + pm_info_cache[physical_port] = pm_info_dict + if pm_info_dict is not None: + # ignore if empty + if not pm_info_dict: + continue + beautify_pm_info_dict(pm_info_dict, physical_port) + fvs = swsscommon.FieldValuePairs([(k, v) for k, v in pm_info_dict.items()]) + table.set(physical_port_name, fvs) + else: + return SFP_EEPROM_NOT_READY + + except NotImplementedError: + helper_logger.log_error("get_transceiver_pm is currently not implemented for this platform") + sys.exit(NOT_IMPLEMENTED_ERROR) + # Update port dom/sfp info in db @@ -557,6 +608,7 @@ def post_port_sfp_dom_info_to_db(is_warm_start, port_mapping, xcvr_table_helper, port_mapping, xcvr_table_helper.get_status_tbl(asic_index), stop_event) + post_port_pm_info_to_db(logical_port_name, port_mapping, xcvr_table_helper.get_pm_tbl(asic_index), stop_event) # Do not notify media settings during warm reboot to avoid dataplane traffic impact if is_warm_start == False: @@ -570,27 +622,15 @@ def post_port_sfp_dom_info_to_db(is_warm_start, port_mapping, xcvr_table_helper, # Delete port dom/sfp info from db -def del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, int_tbl, dom_tbl): - ganged_port = False - ganged_member_num = 1 - - physical_port_list = port_mapping.logical_port_name_to_physical_port_list(logical_port_name) - if physical_port_list is None: - helper_logger.log_error("No physical ports found for logical port '{}'".format(logical_port_name)) - return PHYSICAL_PORT_NOT_EXIST - - if len(physical_port_list) > 1: - ganged_port = True - - for physical_port in physical_port_list: - port_name = get_physical_port_name(logical_port_name, ganged_member_num, ganged_port) - ganged_member_num += 1 - +def del_port_sfp_dom_info_from_db(logical_port_name, port_mapping, int_tbl, dom_tbl, pm_tbl): + for physical_port_name in get_physical_port_name_dict(logical_port_name, port_mapping).values(): try: - if int_tbl != None: - int_tbl._del(port_name) - if dom_tbl != None: - dom_tbl._del(port_name) + if int_tbl: + int_tbl._del(physical_port_name) + if dom_tbl: + dom_tbl._del(physical_port_name) + if pm_tbl: + pm_tbl._del(physical_port_name) except NotImplementedError: helper_logger.log_error("This functionality is currently not implemented for this platform") @@ -1597,6 +1637,7 @@ def task_worker(self): dom_info_cache = {} dom_th_info_cache = {} transceiver_status_cache = {} + pm_info_cache = {} sel, asic_context = port_mapping.subscribe_port_config_change(self.namespaces) # Start loop to update dom info in DB periodically @@ -1605,6 +1646,7 @@ def task_worker(self): dom_info_cache.clear() dom_th_info_cache.clear() transceiver_status_cache.clear() + pm_info_cache.clear() # Handle port change event from main thread port_mapping.handle_port_config_change(sel, asic_context, self.task_stopping_event, self.port_mapping, helper_logger, self.on_port_config_change) @@ -1624,6 +1666,7 @@ def task_worker(self): self.xcvr_table_helper.get_status_tbl(asic_index), self.task_stopping_event, transceiver_status_cache=transceiver_status_cache) + 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, pm_info_cache=pm_info_cache) helper_logger.log_info("Stop DOM monitoring loop") @@ -1655,7 +1698,8 @@ def on_remove_logical_port(self, port_change_event): del_port_sfp_dom_info_from_db(port_change_event.port_name, self.port_mapping, None, - self.xcvr_table_helper.get_dom_tbl(port_change_event.asic_id)) + self.xcvr_table_helper.get_dom_tbl(port_change_event.asic_id), + self.xcvr_table_helper.get_pm_tbl(port_change_event.asic_id)) delete_port_from_status_table_hw(port_change_event.port_name, self.port_mapping, self.xcvr_table_helper.get_status_tbl(port_change_event.asic_id)) @@ -1881,6 +1925,7 @@ def task_worker(self, stopping_event, sfp_error_event): post_port_dom_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_dom_tbl(asic_index)) post_port_dom_threshold_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_dom_tbl(asic_index)) update_port_transceiver_status_table_hw(logical_port, self.port_mapping, self.xcvr_table_helper.get_status_tbl(asic_index)) + post_port_pm_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_pm_tbl(asic_index)) notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(asic_index), self.port_mapping) transceiver_dict.clear() elif value == sfp_status_helper.SFP_STATUS_REMOVED: @@ -1888,7 +1933,10 @@ def task_worker(self, stopping_event, sfp_error_event): update_port_transceiver_status_table_sw( logical_port, self.xcvr_table_helper.get_status_tbl(asic_index), sfp_status_helper.SFP_STATUS_REMOVED) helper_logger.log_info("receive plug out and pdate port sfp status table.") - del_port_sfp_dom_info_from_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_intf_tbl(asic_index), self.xcvr_table_helper.get_dom_tbl(asic_index)) + del_port_sfp_dom_info_from_db(logical_port, self.port_mapping, + self.xcvr_table_helper.get_intf_tbl(asic_index), + self.xcvr_table_helper.get_dom_tbl(asic_index), + self.xcvr_table_helper.get_pm_tbl(asic_index)) delete_port_from_status_table_hw(logical_port, self.port_mapping, self.xcvr_table_helper.get_status_tbl(asic_index)) else: try: @@ -1911,7 +1959,10 @@ def task_worker(self, stopping_event, sfp_error_event): # In this case EEPROM is not accessible. The DOM info will be removed since it can be out-of-date. # The interface info remains in the DB since it is static. if sfp_status_helper.is_error_block_eeprom_reading(error_bits): - del_port_sfp_dom_info_from_db(logical_port, self.port_mapping, None, self.xcvr_table_helper.get_dom_tbl(asic_index)) + del_port_sfp_dom_info_from_db(logical_port, self.port_mapping, + None, + self.xcvr_table_helper.get_dom_tbl(asic_index), + self.xcvr_table_helper.get_pm_tbl(asic_index)) delete_port_from_status_table_hw(logical_port, self.port_mapping, self.xcvr_table_helper.get_status_tbl(asic_index)) except (TypeError, ValueError) as e: helper_logger.log_error("Got unrecognized event {}, ignored".format(value)) @@ -1986,7 +2037,8 @@ def on_remove_logical_port(self, port_change_event): del_port_sfp_dom_info_from_db(port_change_event.port_name, self.port_mapping, self.xcvr_table_helper.get_intf_tbl(port_change_event.asic_id), - self.xcvr_table_helper.get_dom_tbl(port_change_event.asic_id)) + self.xcvr_table_helper.get_dom_tbl(port_change_event.asic_id), + self.xcvr_table_helper.get_pm_tbl(port_change_event.asic_id)) delete_port_from_status_table_sw(port_change_event.port_name, self.xcvr_table_helper.get_status_tbl(port_change_event.asic_id)) delete_port_from_status_table_hw(port_change_event.port_name, self.port_mapping, @@ -2021,6 +2073,7 @@ def on_add_logical_port(self, port_change_event): status_tbl = self.xcvr_table_helper.get_status_tbl(port_change_event.asic_id) int_tbl = self.xcvr_table_helper.get_intf_tbl(port_change_event.asic_id) dom_tbl = self.xcvr_table_helper.get_dom_tbl(port_change_event.asic_id) + pm_tbl = self.xcvr_table_helper.get_pm_tbl(port_change_event.asic_id) physical_port_list = self.port_mapping.logical_port_name_to_physical_port_list(port_change_event.port_name) # Try to find a logical port with same physical index in DB @@ -2087,6 +2140,7 @@ def on_add_logical_port(self, port_change_event): update_port_transceiver_status_table_hw(port_change_event.port_name, self.port_mapping, status_tbl) + post_port_pm_info_to_db(port_change_event.port_name, self.port_mapping, pm_tbl) notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(port_change_event.asic_id), self.port_mapping) else: status = sfp_status_helper.SFP_STATUS_REMOVED if not status else status @@ -2117,6 +2171,7 @@ def retry_eeprom_reading(self): post_port_dom_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_dom_tbl(asic_index)) post_port_dom_threshold_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_dom_tbl(asic_index)) update_port_transceiver_status_table_hw(logical_port, self.port_mapping, self.xcvr_table_helper.get_status_tbl(asic_index)) + post_port_pm_info_to_db(logical_port, self.port_mapping, self.xcvr_table_helper.get_pm_tbl(asic_index)) notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper.get_app_port_tbl(asic_index), self.port_mapping) transceiver_dict.clear() retry_success_set.add(logical_port) @@ -2265,7 +2320,10 @@ def deinit(self): helper_logger.log_warning("Got invalid asic index for {}, ignored".format(logical_port_name)) continue - del_port_sfp_dom_info_from_db(logical_port_name, port_mapping_data, self.xcvr_table_helper.get_intf_tbl(asic_index), self.xcvr_table_helper.get_dom_tbl(asic_index)) + del_port_sfp_dom_info_from_db(logical_port_name, port_mapping_data, + self.xcvr_table_helper.get_intf_tbl(asic_index), + self.xcvr_table_helper.get_dom_tbl(asic_index), + self.xcvr_table_helper.get_pm_tbl(asic_index)) delete_port_from_status_table_sw(logical_port_name, self.xcvr_table_helper.get_status_tbl(asic_index)) delete_port_from_status_table_hw(logical_port_name, port_mapping_data, self.xcvr_table_helper.get_status_tbl(asic_index)) @@ -2324,7 +2382,7 @@ def run(self): class XcvrTableHelper: def __init__(self, namespaces): self.int_tbl, self.dom_tbl, self.status_tbl, self.app_port_tbl, \ - self.cfg_port_tbl, self.state_port_tbl = {}, {}, {}, {}, {}, {} + self.cfg_port_tbl, self.state_port_tbl, self.pm_tbl = {}, {}, {}, {}, {}, {}, {} self.state_db = {} self.cfg_db = {} for namespace in namespaces: @@ -2333,6 +2391,7 @@ def __init__(self, namespaces): self.int_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_INFO_TABLE) self.dom_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_DOM_SENSOR_TABLE) self.status_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_STATUS_TABLE) + self.pm_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], TRANSCEIVER_PM_TABLE) self.state_port_tbl[asic_id] = swsscommon.Table(self.state_db[asic_id], swsscommon.STATE_PORT_TABLE_NAME) appl_db = daemon_base.db_connect("APPL_DB", namespace) self.app_port_tbl[asic_id] = swsscommon.ProducerStateTable(appl_db, swsscommon.APP_PORT_TABLE_NAME) @@ -2348,6 +2407,9 @@ def get_dom_tbl(self, asic_id): def get_status_tbl(self, asic_id): return self.status_tbl[asic_id] + def get_pm_tbl(self, asic_id): + return self.pm_tbl[asic_id] + def get_app_port_tbl(self, asic_id): return self.app_port_tbl[asic_id]