Skip to content
Merged
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
32 changes: 32 additions & 0 deletions sonic-xcvrd/tests/test_xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,38 @@ def test_post_port_sfp_firmware_info_to_db_lport_list_None(self, mock_get_presen
dom_info_update.post_port_sfp_firmware_info_to_db(logical_port_name, port_mapping, firmware_info_tbl, stop_event)
assert firmware_info_tbl.set.call_count == 0

@pytest.mark.parametrize(
"restore_count, system_enabled, expected",
[
(1, None, True),
(0, None, False),
("2", None, True),
("0", None, False),
(None, "true", True),
(None, "false", False),
(None, None, False),
]
)
def test_is_syncd_warm_restore_complete_valid_cases(self, restore_count, system_enabled, expected):
mock_db = MagicMock()
mock_db.hget.side_effect = lambda table, key: (
restore_count if "WARM_RESTART_TABLE|syncd" in table else system_enabled
)

with patch("xcvrd.xcvrd.daemon_base.db_connect", return_value=mock_db):
assert is_syncd_warm_restore_complete() == expected

def test_is_syncd_warm_restore_complete_invalid_restore_count(self):
# restore_count = "abc" triggers ValueError in int("abc")
mock_db = MagicMock()
mock_db.hget.side_effect = lambda table, key: (
"abc" if "WARM_RESTART_TABLE|syncd" in table else None
)

with patch("xcvrd.xcvrd.daemon_base.db_connect", return_value=mock_db):
result = is_syncd_warm_restore_complete()
assert result is False

def test_post_port_dom_sensor_info_to_db(self):
def mock_get_transceiver_dom_sensor_real_value(physical_port):
return {
Expand Down
32 changes: 30 additions & 2 deletions sonic-xcvrd/xcvrd/xcvrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,34 @@ def is_warm_reboot_enabled():
is_warm_start = warmstart.isWarmStart()
return is_warm_start

def is_syncd_warm_restore_complete():
"""
This function determins whether syncd's restore count is not 0, which indicates warm-reboot
to avoid premature config push by xcvrd that caused port flaps.
"""
state_db = daemon_base.db_connect("STATE_DB")
restore_count = state_db.hget("WARM_RESTART_TABLE|syncd", "restore_count")
system_enabled = state_db.hget("WARM_RESTART_ENABLE_TABLE|system", "enable")
try:
# --- Handle restore_count (could be int, str, or None) ---
if restore_count is not None:
if isinstance(restore_count, int):
if restore_count > 0:
return True
elif isinstance(restore_count, str):
if restore_count.strip().isdigit() and int(restore_count.strip()) > 0:
return True

# --- Handle system_enabled (only care about "true"/"false"/None) ---
if isinstance(system_enabled, str):
if system_enabled.strip().lower() == "true":
return True

except Exception as e:
helper_logger.log_warning(f"Unexpected value: restore_count={restore_count}, system_enabled={system_enabled}, error={e}")
log_exception_traceback()
return False

#
# Helper classes ===============================================================
#
Expand Down Expand Up @@ -1493,7 +1521,7 @@ def _post_port_sfp_info_and_dom_thr_to_db_once(self, port_mapping, xcvr_table_he
transceiver_dict = {}
retry_eeprom_set = set()

is_warm_start = is_warm_reboot_enabled()
is_warm_start = is_syncd_warm_restore_complete()
# 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 Down Expand Up @@ -2216,7 +2244,7 @@ def init(self):
def deinit(self):
self.log_info("Start daemon deinit...")

is_warm_fast_reboot = is_warm_reboot_enabled() or is_fast_reboot_enabled()
is_warm_fast_reboot = is_syncd_warm_restore_complete() or is_fast_reboot_enabled()

# Delete all the information from DB and then exit
port_mapping_data = port_event_helper.get_port_mapping(self.namespaces)
Expand Down
Loading