Skip to content
Draft
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
69 changes: 69 additions & 0 deletions sonic-xcvrd/tests/test_xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3141,6 +3141,30 @@ def test_CmisManagerTask_update_port_transceiver_status_table_sw_cmis_state(self
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_utilities.common.is_fast_reboot_enabled', MagicMock(return_value=True))
@patch('xcvrd.xcvrd_utilities.common.get_namespace_from_asic_id', MagicMock(return_value='asic1'))
def test_CmisManagerTask_is_fast_reboot_enabled_for_lport(self):
port_mapping = PortMapping()
port_mapping.logical_to_asic = {'Ethernet0': 1}
stop_event = threading.Event()
task = CmisManagerTask(['asic1'], port_mapping, stop_event, platform_chassis=MagicMock())
common.is_fast_reboot_enabled.reset_mock()

assert task.is_fast_reboot_enabled_for_lport('Ethernet0') is True
common.get_namespace_from_asic_id.assert_called_with(1)
common.is_fast_reboot_enabled.assert_not_called()

@patch('xcvrd.xcvrd_utilities.common.is_fast_reboot_enabled', MagicMock(return_value=False))
def test_CmisManagerTask_is_fast_reboot_enabled_for_lport_default_namespace(self):
port_mapping = PortMapping()
stop_event = threading.Event()
task = CmisManagerTask(DEFAULT_NAMESPACE, port_mapping, stop_event, platform_chassis=MagicMock())
# Ethernet999 not in port_dict -> asic_id -1 -> namespace ''
with patch.object(common, 'get_namespace_from_asic_id', MagicMock()) as mock_get_ns:
assert task.is_fast_reboot_enabled_for_lport('Ethernet999') is False
mock_get_ns.assert_not_called()
common.is_fast_reboot_enabled.assert_called_with('')

@patch('xcvrd.xcvrd._wrapper_get_sfp_type', MagicMock(return_value='QSFP_DD'))
def test_CmisManagerTask_handle_port_change_event(self):
port_mapping = PortMapping()
Expand Down Expand Up @@ -5560,6 +5584,51 @@ def test_SfpStateUpdateTask_mapping_event_from_change_event(self):
port_dict = {1, SFP_STATUS_INSERTED}
assert task._mapping_event_from_change_event(True, port_dict) == NORMAL_EVENT

@patch('xcvrd.xcvrd_utilities.common.get_namespace_from_asic_id', MagicMock(return_value='asic2'))
@patch('xcvrd.xcvrd_utilities.common.is_syncd_warm_restore_complete', MagicMock(return_value=False))
@patch('xcvrd.xcvrd_utilities.common.is_fast_reboot_enabled', MagicMock(return_value=True))
def test_SfpStateUpdateTask_is_warm_fast_reboot_for_lport(self):
port_mapping = PortMapping()
port_mapping.logical_to_asic = {'Ethernet0': 2}
mock_sfp_obj_dict = MagicMock()
stop_event = threading.Event()
sfp_error_event = threading.Event()
task = SfpStateUpdateTask(['asic2'], port_mapping, mock_sfp_obj_dict, stop_event, sfp_error_event)
common.is_syncd_warm_restore_complete.assert_called_once_with('asic2')
common.is_fast_reboot_enabled.assert_called_once_with('asic2')
common.is_syncd_warm_restore_complete.reset_mock()
common.is_fast_reboot_enabled.reset_mock()

assert task.is_warm_fast_reboot_for_lport('Ethernet0') is True
common.get_namespace_from_asic_id.assert_called_with(2)
common.is_syncd_warm_restore_complete.assert_not_called()
common.is_fast_reboot_enabled.assert_not_called()

def test_SfpStateUpdateTask_is_warm_fast_reboot_for_lport_invalid_asic(self):
port_mapping = PortMapping()
mock_sfp_obj_dict = MagicMock()
stop_event = threading.Event()
sfp_error_event = threading.Event()
task = SfpStateUpdateTask(DEFAULT_NAMESPACE, port_mapping, mock_sfp_obj_dict, stop_event, sfp_error_event)
task.port_mapping.get_asic_id_for_logical_port = MagicMock(return_value=None)

assert task.is_warm_fast_reboot_for_lport('Ethernet0') is False

def test_SfpStateUpdateTask_should_notify_media_settings(self):
port_mapping = PortMapping()
mock_sfp_obj_dict = MagicMock()
stop_event = threading.Event()
sfp_error_event = threading.Event()
task = SfpStateUpdateTask(DEFAULT_NAMESPACE, port_mapping, mock_sfp_obj_dict, stop_event, sfp_error_event)

task.is_warm_fast_reboot_for_lport = MagicMock(return_value=True)
assert task.should_notify_media_settings('Ethernet0') is False
task.is_warm_fast_reboot_for_lport.assert_called_with('Ethernet0')

task.is_warm_fast_reboot_for_lport = MagicMock(return_value=False)
assert task.should_notify_media_settings('Ethernet0') is True
task.is_warm_fast_reboot_for_lport.assert_called_with('Ethernet0')

@patch('time.sleep', MagicMock())
@patch('xcvrd.xcvrd.XcvrTableHelper', MagicMock())
@patch('xcvrd.xcvrd._wrapper_soak_sfp_insert_event', MagicMock())
Expand Down
23 changes: 13 additions & 10 deletions sonic-xcvrd/xcvrd/cmis/cmis_manager_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,15 @@ def __init__(self, namespaces, port_mapping, main_thread_stop_event, skip_cmis_m
self.namespaces = namespaces
self.platform_chassis = platform_chassis
self.xcvr_table_helper = XcvrTableHelper(self.namespaces)
self._is_fast_reboot_enabled = None
# Cache of gearbox line lanes dict, refreshed once per task_worker iteration.
self._gearbox_lanes_dict = None
self.fast_reboot_status = self.initialize_fast_reboot_status()

def initialize_fast_reboot_status(self):
fast_reboot_status = {}
for namespace in self.namespaces:
fast_reboot_status[namespace] = bool(common.is_fast_reboot_enabled(namespace))
return fast_reboot_status

def log_debug(self, message):
helper_logger.log_debug(message)
Expand All @@ -73,15 +79,14 @@ def log_notice(self, message):
def log_error(self, message):
helper_logger.log_error(message)

def is_fast_reboot_enabled(self):
"""Check if fast reboot is enabled, caching the result"""
if self._is_fast_reboot_enabled is None:
self._is_fast_reboot_enabled = common.is_fast_reboot_enabled()
return self._is_fast_reboot_enabled

def get_asic_id(self, lport):
return self.port_dict.get(lport, {}).get("asic_id", -1)

def is_fast_reboot_enabled_for_lport(self, lport):
asic_id = self.get_asic_id(lport)
namespace = common.get_namespace_from_asic_id(asic_id) if asic_id >= 0 else ''
return self.fast_reboot_status.get(namespace, False)

def update_port_transceiver_status_table_sw_cmis_state(self, lport, cmis_state_to_set):
status_table = self.xcvr_table_helper.get_status_sw_tbl(self.get_asic_id(lport))
if status_table is None:
Expand Down Expand Up @@ -862,7 +867,7 @@ def handle_cmis_inserted_state(self, lport):
speed = port_info.get('speed')
subport = port_info.get('subport')
appl = port_info.get('appl', 0)
is_fast_reboot = self.is_fast_reboot_enabled()
is_fast_reboot = self.is_fast_reboot_enabled_for_lport(lport)

self.port_dict[lport]['appl'] = common.get_cmis_application_desired(api, host_lane_count, speed)
if self.port_dict[lport]['appl'] is None:
Expand Down Expand Up @@ -1058,7 +1063,6 @@ def process_cmis_state_machine(self, lport):
subport = port_info.get('subport')
pport = port_info.get('pport')
sfp = port_info.get('sfp')
is_fast_reboot = self.is_fast_reboot_enabled()

# CMIS expiration and retries
#
Expand Down Expand Up @@ -1367,4 +1371,3 @@ def join(self):
threading.Thread.join(self)
if self.exc:
raise self.exc

48 changes: 30 additions & 18 deletions sonic-xcvrd/xcvrd/xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,27 @@ def __init__(self, namespaces, port_mapping, sfp_obj_dict, main_thread_stop_even
self.sfp_obj_dict = sfp_obj_dict
self.logger = syslogger.SysLogger(SYSLOG_IDENTIFIER_SFPSTATEUPDATETASK, enable_runtime_config=True)
self.xcvr_table_helper = XcvrTableHelper(self.namespaces)
self.warm_fast_reboot_status = self.initialize_warm_fast_reboot_status()
self.dom_db_utils = DOMDBUtils(sfp_obj_dict, self.port_mapping, self.xcvr_table_helper, self.task_stopping_event, self.logger)
self.vdm_db_utils = VDMDBUtils(sfp_obj_dict, self.port_mapping, self.xcvr_table_helper, self.task_stopping_event, self.logger)

def initialize_warm_fast_reboot_status(self):
warm_fast_reboot_status = {}
for namespace in self.namespaces:
warm_fast_reboot_status[namespace] = bool(common.is_syncd_warm_restore_complete(namespace) or
common.is_fast_reboot_enabled(namespace))
return warm_fast_reboot_status

def is_warm_fast_reboot_for_lport(self, logical_port):
Comment thread
YairRaviv marked this conversation as resolved.
asic_index = self.port_mapping.get_asic_id_for_logical_port(logical_port)
if asic_index is None:
return False
namespace = common.get_namespace_from_asic_id(asic_index)
return self.warm_fast_reboot_status.get(namespace, False)

def should_notify_media_settings(self, logical_port):
return not self.is_warm_fast_reboot_for_lport(logical_port)

def _mapping_event_from_change_event(self, status, port_dict):
"""
mapping from what get_transceiver_change_event returns to event defined in the state machine
Expand Down Expand Up @@ -311,11 +329,6 @@ def _post_port_sfp_info_and_dom_thr_to_db_once(self, port_mapping, xcvr_table_he
transceiver_dict = {}
retry_eeprom_set = set()

# Pre-fetch warm start status for all namespaces/ASICs
warm_start_status = {}
for namespace in self.namespaces:
warm_start_status[namespace] = common.is_syncd_warm_restore_complete(namespace)

# Post all the current interface sfp/dom threshold info to STATE_DB
logical_port_list = port_mapping.logical_port_list
for logical_port_name in logical_port_list:
Expand All @@ -328,13 +341,13 @@ def _post_port_sfp_info_and_dom_thr_to_db_once(self, port_mapping, xcvr_table_he
helper_logger.log_warning("Got invalid asic index for {}, ignored while posting SFP info during boot-up".format(logical_port_name))
continue

# Get warm start status for this ASIC's namespace
# Get warm/fast reboot status for this ASIC's namespace
namespace = common.get_namespace_from_asic_id(asic_index)
is_warm_start = warm_start_status.get(namespace, False)
is_warm_fast_reboot = self.warm_fast_reboot_status.get(namespace, False)

rc = post_port_sfp_info_to_db(logical_port_name, port_mapping, xcvr_table_helper.get_intf_tbl(asic_index), transceiver_dict, stop_event)
if rc != SFP_EEPROM_NOT_READY:
if is_warm_start == False:
if is_warm_fast_reboot == False:
media_settings_parser.notify_media_setting(logical_port_name, transceiver_dict, xcvr_table_helper, port_mapping)
else:
retry_eeprom_set.add(logical_port_name)
Expand Down Expand Up @@ -568,7 +581,8 @@ def task_worker(self, stopping_event, sfp_error_event):
self.dom_db_utils.post_port_dom_thresholds_to_db(logical_port)
self.vdm_db_utils.post_port_vdm_thresholds_to_db(logical_port)

media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping)
if self.should_notify_media_settings(logical_port):
media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping)
transceiver_dict.clear()
elif value == sfp_status_helper.SFP_STATUS_REMOVED:
# Remove the SFP API object for this physical port
Expand Down Expand Up @@ -828,7 +842,8 @@ def on_add_logical_port(self, port_change_event):
else:
self.dom_db_utils.post_port_dom_thresholds_to_db(port_change_event.port_name)
self.vdm_db_utils.post_port_vdm_thresholds_to_db(port_change_event.port_name)
media_settings_parser.notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper, self.port_mapping)
if self.should_notify_media_settings(port_change_event.port_name):
media_settings_parser.notify_media_setting(port_change_event.port_name, transceiver_dict, self.xcvr_table_helper, self.port_mapping)
else:
status = sfp_status_helper.SFP_STATUS_REMOVED if not status else status
common.update_port_transceiver_status_table_sw(port_change_event.port_name, status_sw_tbl, status, error_description)
Expand Down Expand Up @@ -856,7 +871,8 @@ def retry_eeprom_reading(self):
self.dom_db_utils.post_port_dom_thresholds_to_db(logical_port)
self.vdm_db_utils.post_port_vdm_thresholds_to_db(logical_port)

media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping)
if self.should_notify_media_settings(logical_port):
media_settings_parser.notify_media_setting(logical_port, transceiver_dict, self.xcvr_table_helper, self.port_mapping)
transceiver_dict.clear()
retry_success_set.add(logical_port)
# Update retry EEPROM set
Expand Down Expand Up @@ -1052,11 +1068,8 @@ def init(self):
# Initialize xcvr table helper
self.xcvr_table_helper = XcvrTableHelper(self.namespaces)

if common.is_fast_reboot_enabled():
self.log_info("Skip loading media_settings.json and optics_si_settings.json in case of fast-reboot")
else:
media_settings_parser.load_media_settings()
optics_si_parser.load_optics_si_settings()
media_settings_parser.load_media_settings()
optics_si_parser.load_optics_si_settings()
Comment thread
YairRaviv marked this conversation as resolved.

# Make sure this daemon started after all port configured
self.log_notice("XCVRD INIT: Wait for port config is done")
Expand All @@ -1082,10 +1095,9 @@ def deinit(self):
self.log_info("Start daemon deinit...")

# Pre-fetch warm/fast reboot status for all namespaces/ASICs
is_fast_reboot = common.is_fast_reboot_enabled()
warm_fast_reboot_status = {}
for namespace in self.namespaces:
warm_fast_reboot_status[namespace] = common.is_syncd_warm_restore_complete(namespace) or is_fast_reboot
warm_fast_reboot_status[namespace] = common.is_syncd_warm_restore_complete(namespace) or common.is_fast_reboot_enabled(namespace)

# Delete all the information from DB and then exit
port_mapping_data = port_event_helper.get_port_mapping(self.namespaces)
Expand Down
8 changes: 5 additions & 3 deletions sonic-xcvrd/xcvrd/xcvrd_utilities/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,12 @@ def _wrapper_get_presence(physical_port):
pass
return False

def is_fast_reboot_enabled():
def is_fast_reboot_enabled(namespace=''):
"""Check if fast reboot is enabled"""
fastboot_enabled = subprocess.check_output('sonic-db-cli STATE_DB hget "FAST_RESTART_ENABLE_TABLE|system" enable', shell=True, universal_newlines=True)
return "true" in fastboot_enabled
state_db = daemon_base.db_connect("STATE_DB", namespace=namespace)
fastboot_enabled = state_db.hget("FAST_RESTART_ENABLE_TABLE|system", "enable")
if isinstance(fastboot_enabled, str):
return fastboot_enabled.strip().lower() == "true"

def is_warm_reboot_enabled():
"""Check if warm reboot is enabled"""
Expand Down
Loading