Skip to content

Create xcvrd restart testcases - #26534

Draft
Sutter-Pinewave wants to merge 17 commits into
sonic-net:masterfrom
Sutter-Pinewave:fix/add-tests-transceiver-xcvrd-restart
Draft

Create xcvrd restart testcases#26534
Sutter-Pinewave wants to merge 17 commits into
sonic-net:masterfrom
Sutter-Pinewave:fix/add-tests-transceiver-xcvrd-restart

Conversation

@Sutter-Pinewave

@Sutter-Pinewave Sutter-Pinewave commented Jul 23, 2026

Copy link
Copy Markdown

Description of PR

Summary:
Created xcvrd restart testcase.

Tests located at tests/transceiver/system/process_restart/test_xcvrtd_restart.py
Original testcase described in docs/testplan/transceiver/system_test_plan.md

NOTE: Please note that this PR is dependent on the helper functions in PR #26273

Fixes # (issue)

Type of change

  • Bug fix
  • Testbed and Framework(new/improvement)
  • New Test case
    • Skipped for non-supported platforms
  • Test case improvement

Approach

What is the motivation for this PR?

Fulfilling the test plan described in system_test_plan.md

How did you do it?

Test initially logs link states, uptime, and DUT corefiles. Restarts syncd and waits based on related settle_sec system attribute. Performs standard port validation and recovery process on all participating ports.

How did you verify/test it?

Tested in Pinewave lab on Arista 7060 using loopback connections.
(P5<->P7), (P13<->P15)

Any platform specific information?

Supported testbed topology if it's a new test case?

Documentation

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@mssonicbld

Copy link
Copy Markdown
Collaborator

Hi, there are workflow run(s) waiting for approval, you may be first-time contributor. I will notify maintainers to help approve once PR is approved. Thanks!

---Powered by SONiC BuildBot

@mihirpat1 mihirpat1 added the Transceiver Test Dev 🔌 Action item for transceiver test development label Jul 23, 2026
@mihirpat1
mihirpat1 requested a review from Copilot July 23, 2026 18:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new transceiver System / Process Restart test module intended to validate xcvrd restart/crash recovery behavior as part of the transceiver system test plan.

Changes:

  • Introduces test_system_xcvrd_restart to restart xcvrd and verify port recovery.
  • Introduces test_system_xcvrd_crash_recovery to SIGKILL xcvrd and verify recovery.
  • Wires the tests to shared health-check / link-check style helpers (notably via helper modules referenced as dependencies).
Comments suppressed due to low confidence (2)

tests/transceiver/system/process_restart/test_xcvrd_restart.py:144

  • Keyword arguments should not contain spaces around '=' (flake8 E251).
        health_baseline = health_baseline,

tests/transceiver/system/process_restart/test_xcvrd_restart.py:133

  • check_links_up() returns a dict (with passed/details), but this code treats it as a boolean. Since non-empty dicts are always truthy, the warning branch will never run even when links are down.
    if not check_links_up(duthost, port_attributes_dict):
        logger.warning("Validation on Start FAILED: some ports are down")
    

Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_xcvrd_restart.py Outdated
Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Signed-off-by: sutter <sutter.lum@pinewave.com>
Signed-off-by: sutter <sutter.lum@pinewave.com>
Signed-off-by: sutter <sutter.lum@pinewave.com>
Signed-off-by: sutter <sutter.lum@pinewave.com>
…ilty fixes

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

@Sutter-Pinewave

Copy link
Copy Markdown
Author

test_report_for_xcvrd_restart-Aug5-512pm.html
test_report_for_xcvrd_restart-Aug5-358pm.html

Hi all, uploaded the first draft of the updated version of the code. Also attached two passlogs. The 5:12pm one is a pass, the 3:58 one is a failure on a slightly older version that was misusing a helper, but I've included it as it happened to capture a link flap, proving the negative case.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 5 comments.

Suppressed comments (12)

tests/common/platform/interface_utils.py:120

  • wait_ports_oper_status currently returns a list of formatted failure strings, but callers (e.g. standard_port_recovery_and_verification) treat the return value as a list of port names. This breaks down_ports/up_ports computation and can also raise KeyError when indexing per_port_failures.
    if wait_until(wait_sec, poll_interval_sec, 0, lambda: not _ports_not_at_status()):
        return []
    return [
        "port {} did not reach oper-{} within {}s".format(port, status, wait_sec)
        for port in _ports_not_at_status()

tests/transceiver/common/verification.py:380

  • standard_port_recovery_and_verification’s docstring lists LLDP and CMIS checks, but those steps are currently commented out in the implementation. Either re-enable them or update the docstring so the function contract matches what actually runs.
    """Run the Standard Port Recovery and Verification Procedure on a
    batch of ports (link status, flap/stability, LLDP, CMIS state,
    docker/process health), batched across ``ports`` so fixed per-call
    costs aren't multiplied by port count and every port's failures are
    surfaced in one call.

tests/transceiver/system/conftest.py:63

  • _state_db_key_exists reads STATE_DB without a per-port ASIC namespace. On multi-ASIC DUTs this can report false 'missing' entries because each ASIC has its own STATE_DB namespace. Use the existing db_helpers wrappers and accept a namespace argument.
def _state_db_key_exists(duthost, key):
    cmd = f'sonic-db-cli STATE_DB hgetall "{key}"'
    out = duthost.shell(cmd, module_ignore_errors=True)
    if out.get("rc", 1) != 0:
        return False
    return bool((out.get("stdout") or "").strip())

tests/transceiver/system/conftest.py:123

  • The STATE_DB consistency loop should pass the owning ASIC namespace for each port when checking TRANSCEIVER_INFO/DOM entries; otherwise multi-ASIC DUTs can be incorrectly flagged as missing data.
    for port in sorted(port_attributes_dict.keys()):
        if not _state_db_key_exists(duthost, f"TRANSCEIVER_INFO|{port}"):
            missing_info.append(port)
        if not _state_db_key_exists(duthost, f"TRANSCEIVER_DOM_SENSOR|{port}"):
            missing_dom.append(port)

tests/common/platform/interface_utils.py:105

  • The docstring says wait_ports_oper_status returns one failure string per port, but for downstream use it should return port names (and let callers format messages). Please update the docstring to match the function’s actual contract.

This issue also appears on line 116 of the same file.

    Returns a list with one string per port still not at oper-``status`` after
    ``wait_sec``; empty once all reach it. A port absent from the dump is reported
    as a failure (rather than raising) so a missing/renamed port aggregates like
    any other laggard.

tests/transceiver/common/verification.py:241

  • Typo in the check_cmis_state docstring ('breakou t') makes the comment hard to read and looks like an accidental paste/formatting artifact.

This issue also appears on line 376 of the same file.

    Why: ``TRANSCEIVER_STATUS`` is published once per physical module (under
    the first sub-port of a breakou                   t group) and carries every host lane of
    the module, so a breakout sub-port must be checked only against its own

tests/transceiver/common/verification.py:9

  • Module docstring claims all parent/child helpers return the per-port dict shape, but standard_port_recovery_and_verification returns an aggregate dict with top-level 'passed'/'per_port'/'details'. This mismatch will confuse future call sites and reviewers.
Implements the Standard Port and Verification function,
as well as the related child functions. All parent and
child functions will return an output following the format:

dict: ``{port: {'passed': bool, 'details': str}}``

tests/transceiver/system/process_restart/test_swss_restart.py:64

  • This test restarts swss, so the monitored process expected to change PID is orchagent (in the swss container), not xcvrd. As written, the per-test health check will likely fail because orchagent PID changes are treated as unexpected.
    expected_pid_changes.add("xcvrd")

tests/transceiver/system/process_restart/test_swss_restart.py:73

  • check_links_up returns a result dict (with a 'passed' key). Using it as a boolean will always treat failures as success because non-empty dicts are truthy, so the test will log uptime even when ports are down.
    if not check_links_up(duthost, port_attributes_dict):
        logger.warning("Validation on Start FAILED: some ports are down")
    else:

tests/transceiver/system/process_restart/test_syncd_restart.py:64

  • This test restarts syncd, so the monitored process expected to change PID is syncd (in the syncd container), not xcvrd. As written, the per-test health check will likely fail because syncd PID changes are treated as unexpected.
    expected_pid_changes.add("xcvrd")

tests/transceiver/system/process_restart/test_syncd_restart.py:73

  • check_links_up returns a result dict (with a 'passed' key). Using it as a boolean will always treat failures as success because non-empty dicts are truthy, so the test will log uptime even when ports are down.
    if not check_links_up(duthost, port_attributes_dict):
        logger.warning("Validation on Start FAILED: some ports are down")
    else:

tests/transceiver/system/process_restart/test_pmon_restart.py:70

  • check_links_up returns a result dict (with a 'passed' key). Using it as a boolean will always treat failures as success because non-empty dicts are truthy, so the test will log uptime even when ports are down.
    if not check_links_up(duthost, port_attributes_dict):
        logger.warning("Validation on Start FAILED: some ports are down")
    else:

Comment thread tests/transceiver/system/process_restart/test_swss_restart.py Outdated
Comment thread tests/transceiver/system/process_restart/test_swss_restart.py
Comment on lines +39 to +40
from tests.common.helpers.sonic_db import AppDbCli as sdbHelp
from tests.common.platform.processes_utils import check_process_up
Comment thread tests/transceiver/system/process_restart/test_syncd_restart.py
Comment thread tests/transceiver/system/conftest.py
Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command.

@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Signed-off-by: sutter <sutter.lum@pinewave.com>
Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

…issues

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@az-pz
az-pz requested a balanced review from Copilot August 7, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 10 comments.

Suppressed comments (7)

tests/transceiver/system/process_restart/test_swss_restart.py:40

  • tests.common.platform.processes_utils does not define check_process_up, so this import raises ImportError during pytest collection and none of the swss restart test code can run. Replace it with an existing helper or implement restart verification using a pre/post pmon/xcvrd PID comparison.
from tests.common.platform.processes_utils import check_process_up

tests/transceiver/system/process_restart/test_syncd_restart.py:40

  • tests.common.platform.processes_utils does not define check_process_up, so this import raises ImportError during pytest collection and none of the syncd restart test code can run. Replace it with an existing helper or implement restart verification using a pre/post pmon/xcvrd PID comparison.
from tests.common.platform.processes_utils import check_process_up

tests/transceiver/system/process_restart/test_xcvrd_restart.py:144

  • expected_pid_changes permits a changed PID but does not assert one, while kill_pmon_daemon_pid_w_sig ignores kill errors. A failed SIGKILL can therefore leave the original xcvrd running and this crash-recovery test still passes. Capture the pre-crash PID and require a RUNNING xcvrd with a different PID after recovery.
    expected_pid_changes.add("xcvrd")

tests/transceiver/system/conftest.py:123

  • These STATE_DB reads always use the default namespace. On multi-ASIC DUTs, transceiver keys for frontend ports reside in their owning ASIC namespaces, so every such port is falsely reported missing. Resolve each port's namespace and issue the check there, as the new DB helpers already do.
    for port in sorted(port_attributes_dict.keys()):
        if not _state_db_key_exists(duthost, f"TRANSCEIVER_INFO|{port}"):
            missing_info.append(port)
        if not _state_db_key_exists(duthost, f"TRANSCEIVER_DOM_SENSOR|{port}"):
            missing_dom.append(port)

tests/transceiver/system/process_restart/test_xcvrd_restart.py:21

  • The module advertises an I2C-error restart test as skipped, but no test_system_xcvrd_restart_with_i2c_errors function or skip marker exists, so pytest reports no skipped test and that test-plan case is silently absent. Add the test with an explicit pytest.mark.skip(reason=...) until it is implementable, or narrow the stated PR/module scope.
    `- test_system_xcvrd_restart_with_i2c_errors
            NOTE: this test has been skipped during initial development
            due to library issues

tests/transceiver/common/state_management.py:148

  • The helper claims to restore and report admin/link, high-power, and CMIS datapath state, but the final verdict rechecks only links. A failed sfputil lpmode off command or a datapath that remains invalid after recycling leaves still_failing empty and the teardown reports recovery. Revalidate LPMode and CMIS state after the actions and include those failures in the summary.
    final_link = check_links_up(duthost, port_attributes_dict)
    if not final_link["passed"]:
        summary["still_failing"].extend(final_link["down"])

tests/transceiver/system/conftest.py:123

  • A DOM entry is not expected for every logical breakout port: dom/test_dom_availability.py:108-120 explicitly requires non-primary breakout subports to have no TRANSCEIVER_DOM_SENSOR data. This loop therefore reports valid breakout configurations as inconsistent. Check the physical module's first subport only (using lport_to_first_subport_mapping) for DOM presence.
        if not _state_db_key_exists(duthost, f"TRANSCEIVER_DOM_SENSOR|{port}"):
            missing_dom.append(port)

Comment thread tests/transceiver/common/db_helpers.py Outdated
Comment on lines +37 to +38
# sonic-db-cli database identifiers (the first positional arg to sonic-db-cli).
STATE_DB = "STATE_DB"
Comment on lines +435 to +437
# NOTE: Temporarily disabled for PR streamlining
# All commented code below is UNTESTED
#
All (port, step) failures are accumulated and reported in a single
``pytest.fail`` so one run surfaces every issue.
"""
expected_pid_changes.add("xcvrd")
All (port, step) failures are accumulated and reported in a single
``pytest.fail`` so one run surfaces every issue.
"""
expected_pid_changes.add("xcvrd")
All (port, step) failures are accumulated and reported in a single
``pytest.fail`` so one run surfaces every issue.
"""
expected_pid_changes.add("xcvrd")
Comment on lines +53 to +54
# Failures are reported as warnings instead of fixture errors so they
# don't mask the actual test results that are already on the report.
Comment thread tests/transceiver/common/db_helpers.py Outdated
Comment on lines +279 to +280
config_facts = duthost.get_running_config_facts()
return set(config_facts.get("PORT", {}).keys()) No newline at end of file
Comment on lines +420 to +422
for port in down_ports:
per_port_failures[port].append(
f"port {port} did not reach oper-up within {link_up_timeout_sec}s"
time.sleep(pmon_wait + 60)

# Wait for settle time and verify
result = standard_port_recovery_and_verification(
Comment on lines +333 to +345
if datapath_fields_seen == 0 and config_fields_seen == 0:
per_port[port] = {
"passed": False,
"details": (
f"{port} (parent {parent}) TRANSCEIVER_STATUS|{parent} "
f"has no DP<N>State or config_state_hostlane<N> fields "
f"for this port's active host lanes "
f"{sorted(active_lanes)} - cannot confirm CMIS state "
"(schema mismatch, partial publish, or lane-range "
"mismatch)"
),
}
continue
Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Signed-off-by: sutter <sutter.lum@pinewave.com>
@mssonicbld

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Transceiver Test Dev 🔌 Action item for transceiver test development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants