Skip to content

Add LACP bond health check for DPU deployments - #39

Open
szigmon wants to merge 1 commit into
RedHatInsights:mainfrom
szigmon:dpf-lacp-bond-health
Open

Add LACP bond health check for DPU deployments#39
szigmon wants to merge 1 commit into
RedHatInsights:mainfrom
szigmon:dpf-lacp-bond-health

Conversation

@szigmon

@szigmon szigmon commented May 18, 2026

Copy link
Copy Markdown

Summary

  • Add DpuBondLacpHealth rule to validate 802.3ad (LACP) bond health on nodes with NVIDIA BlueField DPUs
  • Add unit tests covering healthy bond, slave down, different aggregators, and single slave scenarios
  • Register the new rule in the network domain

Motivation

On OpenShift clusters with NVIDIA BlueField DPUs, worker nodes use an LACP bond (802.3ad) across the two DPU ports as the primary network uplink. A degraded bond — where one port goes down, LACP negotiation fails, or ports land in different aggregators — causes traffic to flow through a single port, silently halving available bandwidth. The node remains Ready and no existing check detects this condition.

What the check validates

  • All slave interfaces have MII Status up
  • All slaves are in the same aggregator ID (LACP fully negotiated)
  • No actor or partner churn state on any slave
  • At least 2 slaves present in the LACP bond

The rule runs on all nodes, skips nodes without bond interfaces, and skips non-LACP bond modes (e.g. active-backup).

Test plan

  • Tested on a real OpenShift 4.20 cluster with BlueField-3 DPU workers
  • Unit tests cover: healthy bond (pass), slave down (fail), different aggregators (fail), single slave (fail), no bond interfaces (prerequisite not met)

Summary by CodeRabbit

  • New Features

    • Added NVIDIA DPU bond LACP health validation.
    • Added OVN Geneve tunnel local-IP consistency check.
    • Network health checks updated to run both validators.
  • Tests

    • Added unit tests covering DPU bond LACP scenarios and Geneve local-IP behaviors.
    • Updated network domain tests to expect and include the two new validators.
  • Profiles

    • Added a new "dpf" profile for DPU deployments.

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds two DPF-focused network validators: DpuBondLacpHealth (checks LACP bond health from /proc/net/bonding) and OvnGeneveTunnelLocalIp (validates OVN Geneve local_ip vs node InternalIP). Integrates validators into NetworkValidationDomain, adds a dpf profile, and provides unit tests for both rules.

Changes

DPF Network Validators

Layer / File(s) Summary
Rules implementation
src/in_cluster_checks/rules/network/dpf_validations.py
Adds DpuBondLacpHealth (parses /proc/net/bonding, checks LACP bonds for MII, slave counts, aggregator consistency, churn; returns skip/failed/passed) and OvnGeneveTunnelLocalIp (parses ovs-vsctl show for Geneve local_ip, compares to node InternalIP; returns skip/failed/passed).
Unit tests for validators
tests/rules/network/test_dpf_validations.py
Adds fixtures simulating bonding outputs and ovs-vsctl show outputs, and tests covering healthy and multiple failing LACP scenarios, OVS/Geneve prerequisites, matching and stale Geneve local_ip cases.
Network domain integration
src/in_cluster_checks/domains/network_domain.py, tests/domains/test_network_domain.py, src/profiles/profiles.yaml
Imports the new validators, updates the domain docstring to mention DPF, includes DpuBondLacpHealth and OvnGeneveTunnelLocalIp in NetworkValidationDomain.get_rule_classes(), updates domain tests to expect the added rules, and adds a dpf profile that includes general.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested reviewers

  • sarad-rh
  • hoberger-rh

Poem

🐰 I nibbled logs and parsed each line,
LACP bonds in orderly design,
Geneve IPs I softly hop,
Matching nodes — I never stop,
Cheers for networks running fine!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a LACP bond health check for DPU deployments, which is the primary focus of the PR and is well-reflected in the code changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/rules/network/test_dpf_validations.py (1)

121-136: ⚡ Quick win

Add a scenario for non-LACP bond mode being ignored.

BOND_ACTIVE_BACKUP is defined but never exercised. Please add a scenario asserting non-802.3ad bonds are ignored (and result is SKIP when only non-LACP bonds are present), matching the rule contract.

As per coding guidelines, Prefer comprehensive test coverage... including edge cases and verify that the rule validates the intended functionality.

Also applies to: 198-218

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/rules/network/test_dpf_validations.py` around lines 121 - 136, Add a
new test scenario using the BOND_ACTIVE_BACKUP fixture in
tests/rules/network/test_dpf_validations.py that exercises the rule when only
non-802.3ad bonds exist and assert the rule returns SKIP; specifically, create a
test (e.g., test_non_lacp_bonds_are_ignored) that feeds the BOND_ACTIVE_BACKUP
string into the same test harness/validator used by the other cases and asserts
the outcome is "SKIP" and no failures are reported, mirroring how existing
scenarios are structured so the BOND_ACTIVE_BACKUP constant is actually
exercised; apply the same pattern for the equivalent cases noted around the
other section mentioned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/in_cluster_checks/rules/network/dpf_validations.py`:
- Around line 87-89: In run_rule, check the return code from
self.run_cmd(SafeCmdString("ls /proc/net/bonding/")) instead of ignoring rc; if
rc is non-zero, raise UnExpectedSystemOutput including the rc and the command
output (stdout/stderr) so failures are surfaced instead of falling through to
"No LACP (802.3ad) bonds found". Locate the call to run_cmd/SafeCmdString in
run_rule and add the conditional branch that raises UnExpectedSystemOutput with
a clear message and the captured output.
- Line 28: The rule currently defines an empty links list (links = []) and must
include its GitHub wiki URL; update the links variable in
src/in_cluster_checks/rules/network/dpf_validations.py by adding the rule's wiki
page string (e.g. "https://github.com/<org>/<repo>/wiki/<Rule-Name>") to the
list so links contains the wiki entry; ensure the string matches the project's
wiki URL pattern used by other rules.
- Around line 116-133: The code currently builds agg_ids = set(...) filtering
out falsy aggregator_id so missing IDs are ignored; update the logic in the
block handling agg_ids (around the variable agg_ids, info["slaves"], bond_name
and down_slaves) to first detect any slaves with missing or empty aggregator_id
and append an issue (e.g. "{bond_name}: missing aggregator_id on slaves ...") to
all_issues before performing the set-consistency check; this ensures a slave
lacking aggregator_id is treated as an error rather than being silently ignored
when others appear consistent.

---

Nitpick comments:
In `@tests/rules/network/test_dpf_validations.py`:
- Around line 121-136: Add a new test scenario using the BOND_ACTIVE_BACKUP
fixture in tests/rules/network/test_dpf_validations.py that exercises the rule
when only non-802.3ad bonds exist and assert the rule returns SKIP;
specifically, create a test (e.g., test_non_lacp_bonds_are_ignored) that feeds
the BOND_ACTIVE_BACKUP string into the same test harness/validator used by the
other cases and asserts the outcome is "SKIP" and no failures are reported,
mirroring how existing scenarios are structured so the BOND_ACTIVE_BACKUP
constant is actually exercised; apply the same pattern for the equivalent cases
noted around the other section mentioned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 214b2be1-7bd3-455a-9536-3c6a9e33be85

📥 Commits

Reviewing files that changed from the base of the PR and between 076a10b and 23536ff.

📒 Files selected for processing (3)
  • src/in_cluster_checks/domains/network_domain.py
  • src/in_cluster_checks/rules/network/dpf_validations.py
  • tests/rules/network/test_dpf_validations.py

Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from e1bbf86 to 309360d Compare May 18, 2026 19:03

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/rules/network/test_dpf_validations.py (1)

121-135: ⚡ Quick win

Add an explicit scenario asserting non-LACP bonds are skipped.

BOND_ACTIVE_BACKUP is defined, but there is no test that verifies the rule returns skip when only non-802.3ad bonds exist.

As per coding guidelines, Prefer comprehensive test coverage. Check that tests cover all meaningful scenarios including edge cases, partial state, empty output, and missing resources..

Also applies to: 161-219

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/rules/network/test_dpf_validations.py` around lines 121 - 135, Add a
new unit test in tests/rules/network/test_dpf_validations.py that feeds the
BOND_ACTIVE_BACKUP fixture into the same validation helper used by the other
tests in this file and asserts the rule returns a skip/ignored result (i.e.,
non-802.3ad bonds should produce a skip). Locate where other scenarios call the
rule/validator in this file and replicate that pattern (use the same test helper
and assert the skip status) so the case "only non-LACP bonds exist" is
explicitly covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/in_cluster_checks/rules/network/dpf_validations.py`:
- Around line 179-187: The current loop incorrectly excludes lines containing
"geneve" and therefore can pick up local_ip values from non-Geneve interfaces;
update the condition so you only extract local_ip for Geneve interfaces by
requiring "geneve" in the line (e.g., replace the if "local_ip=" in line and
"geneve" not in line check with one that requires "local_ip=" and "geneve" in
line), then keep the existing per-part parse that extracts ip and appends to
local_ips (variables: stdout, local_ips, ip, and the "local_ip" token) so the
rule only validates Geneve interface local_ip values.

---

Nitpick comments:
In `@tests/rules/network/test_dpf_validations.py`:
- Around line 121-135: Add a new unit test in
tests/rules/network/test_dpf_validations.py that feeds the BOND_ACTIVE_BACKUP
fixture into the same validation helper used by the other tests in this file and
asserts the rule returns a skip/ignored result (i.e., non-802.3ad bonds should
produce a skip). Locate where other scenarios call the rule/validator in this
file and replicate that pattern (use the same test helper and assert the skip
status) so the case "only non-LACP bonds exist" is explicitly covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d02b31fc-f35d-4aa7-b2d7-c1ae8e50ca81

📥 Commits

Reviewing files that changed from the base of the PR and between 23536ff and 309360d.

📒 Files selected for processing (4)
  • src/in_cluster_checks/domains/network_domain.py
  • src/in_cluster_checks/rules/network/dpf_validations.py
  • tests/domains/test_network_domain.py
  • tests/rules/network/test_dpf_validations.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/in_cluster_checks/domains/network_domain.py

Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
Comment on lines +5 to +6
Validates LACP bond health on DPU network interfaces to detect
degraded bond configurations that reduce available bandwidth.

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.

please remove

@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from 309360d to 0f78ea6 Compare May 19, 2026 09:58

@coderabbitai coderabbitai Bot 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.

♻️ Duplicate comments (5)
src/in_cluster_checks/rules/network/dpf_validations.py (5)

158-158: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the required wiki link to links.

This new rule has an empty links list. As per coding guidelines, every new rule MUST have a corresponding wiki page in the GitHub wiki and the wiki URL must be added to the rule's links field pointing to: https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-OVN-Geneve-tunnel-local_ip-matches-node-InternalIP

📝 Proposed fix
-    links = []
+    links = ["https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-OVN-Geneve-tunnel-local_ip-matches-node-InternalIP"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` at line 158, The
rule's links list is empty; update the links variable by adding the required
GitHub wiki URL for this rule so the rule points to its documentation. Locate
the links list (variable name links) in
src/in_cluster_checks/rules/network/dpf_validations.py and append the URL
https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-OVN-Geneve-tunnel-local_ip-matches-node-InternalIP
to that list (ensure it's a string entry in the existing list format used by
this rule).

82-86: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle ls /proc/net/bonding/ failure in run_rule.

The return code rc is unpacked but never checked. If the ls command fails (rc != 0), the code proceeds to parse stdout which could contain an error message, leading to misreporting as "No LACP (802.3ad) bonds found" instead of surfacing the actual failure. As per coding guidelines, prefer raising UnExpectedSystemOutput exception when a command produces unexpected output or fails.

🛡️ Proposed fix
+from in_cluster_checks.utils.exceptions import UnExpectedSystemOutput
+
 def run_rule(self) -> RuleResult:
     """Check LACP bond health on all bond interfaces."""
     rc, stdout, _ = self.run_cmd(SafeCmdString("ls /proc/net/bonding/"))
+    if rc != 0:
+        raise UnExpectedSystemOutput(f"Failed to list bond interfaces: {stdout}")
     bond_names = stdout.strip().split()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` around lines 82 - 86,
In run_rule, check the return code from run_cmd(SafeCmdString("ls
/proc/net/bonding/")) before parsing stdout: if rc != 0, raise
UnExpectedSystemOutput including the command context and the stdout/stderr
content so failures are surfaced instead of treating an error string as bond
names; only proceed to split stdout into bond_names when rc == 0. Ensure you
reference the run_rule method and the run_cmd/SafeCmdString call when adding the
guard and raising the exception.

26-26: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the required wiki link to links.

This new rule has an empty links list. As per coding guidelines, every new rule MUST have a corresponding wiki page in the GitHub wiki and the wiki URL must be added to the rule's links field pointing to: https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-LACP-bond-health-on-DPU-ports

📝 Proposed fix
-    links = []
+    links = ["https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-LACP-bond-health-on-DPU-ports"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` at line 26, The
rule's links list is empty in dpf_validations.py; update the links variable
(links = []) to include the required wiki URL for this rule by adding
"https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-LACP-bond-health-on-DPU-ports"
so the links list contains that single string entry (ensure the variable name
links in the module remains unchanged).

183-211: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Scope local_ip extraction strictly to Geneve interfaces.

The regex _OVS_LOCAL_IP_RE.findall(ovs_output) at line 189 matches any local_ip="..." pattern anywhere in the OVS output. If other interface types (e.g., vxlan, gre) also have local_ip options, those would be incorrectly included in the validation, producing false failures or false passes. The prerequisite only checks that "geneve" appears somewhere in the output, not that extracted local_ip values belong to geneve interfaces.

🔍 Proposed fix to parse geneve interfaces correctly
     def run_rule(self) -> RuleResult:
         """Check that Geneve tunnel local_ip matches node InternalIP."""
         ovs_output = self._get_ovs_show()
         if not ovs_output:
             return RuleResult.skip("Cannot read OVS state")
 
-        local_ips = list(set(self._OVS_LOCAL_IP_RE.findall(ovs_output)))
+        local_ips: List[str] = []
+        in_geneve_interface = False
+        for line in ovs_output.splitlines():
+            line = line.strip()
+            if line.startswith("type:"):
+                in_geneve_interface = line.split(":", 1)[1].strip() == "geneve"
+            elif in_geneve_interface and line.startswith("options:"):
+                matches = self._OVS_LOCAL_IP_RE.findall(line)
+                for ip in matches:
+                    if ip and ip not in local_ips:
+                        local_ips.append(ip)
+                in_geneve_interface = False
+        
         if not local_ips:
             return RuleResult.skip("No Geneve local_ip found in OVS")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` around lines 183 -
211, The current run_rule uses _OVS_LOCAL_IP_RE.findall(ovs_output) which picks
up any local_ip="..." anywhere in the OVS dump; restrict extraction to only
Geneve interfaces by first locating the Geneve interface blocks/lines in the
output (e.g., lines/stanzas that contain "type=geneve" or an interface name/type
that identifies geneve) from _get_ovs_show(), then apply the local_ip regex only
within those geneve-specific blocks to build local_ips; update the logic around
_OVS_LOCAL_IP_RE, local_ips and geneve_local_ip accordingly so only Geneve
local_ip values are considered for the comparison with _get_node_ip().

113-131: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle missing aggregator IDs as a bond issue.

The aggregator ID check at line 113 filters out empty aggregator_id values using if s["aggregator_id"], so slaves with missing aggregator IDs are silently ignored. Then at line 129, the healthy check uses len(agg_ids) <= 1, which passes when agg_ids is empty (all IDs missing) or contains a single ID. This incorrectly marks a bond as healthy when aggregator IDs are missing. Missing aggregator ID should be treated as a bond issue before the consistency check.

🛡️ Proposed fix
         down_slaves = [s for s in info["slaves"] if s["mii_status"] != "up"]
         if down_slaves:
             names = ", ".join(s["name"] for s in down_slaves)
             all_issues.append(f"{bond_name}: slave(s) down: {names}")
 
+        missing_agg = [s["name"] for s in info["slaves"] if not s["aggregator_id"]]
+        if missing_agg:
+            all_issues.append(
+                f"{bond_name}: missing aggregator ID for slave(s): {', '.join(missing_agg)}"
+            )
+
         agg_ids = set(s["aggregator_id"] for s in info["slaves"] if s["aggregator_id"])
         if len(agg_ids) > 1:
             details = ", ".join(f"{s['name']}=agg{s['aggregator_id']}" for s in info["slaves"])
             all_issues.append(
                 f"{bond_name}: slaves in different aggregators ({details}), LACP not fully negotiated"
             )
 
         churned: List[str] = []
         for s in info["slaves"]:
             if s["actor_churn"] and s["actor_churn"] != "none":
                 churned.append(f"{s['name']} actor={s['actor_churn']}")
             if s["partner_churn"] and s["partner_churn"] != "none":
                 churned.append(f"{s['name']} partner={s['partner_churn']}")
         if churned:
             all_issues.append(f"{bond_name}: LACP churn detected: {', '.join(churned)}")
 
-        if not down_slaves and len(agg_ids) <= 1 and not churned:
+        if not down_slaves and not missing_agg and len(agg_ids) == 1 and not churned:
             slave_info = ", ".join(f"{s['name']} ({s['speed']})" for s in info["slaves"])
             all_passed.append(f"{bond_name}: LACP healthy, {len(info['slaves'])} slaves UP ({slave_info})")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` around lines 113 -
131, The code currently builds agg_ids with set(s["aggregator_id"] for s in
info["slaves"] if s["aggregator_id"]) which ignores missing/empty aggregator IDs
and can make agg_ids empty (wrongly marking the bond healthy); update validation
to first detect any slave where s["aggregator_id"] is missing/empty and append
an all_issues entry referencing bond_name and those slave names (e.g. "missing
aggregator_id on ..."), then build agg_ids from all non-empty IDs and change the
healthy condition (the final if that appends to all_passed) to require that at
least one aggregator ID exists and is consistent (e.g. agg_ids and len(agg_ids)
<= 1) so bonds with all-missing IDs are not considered healthy; refer to symbols
agg_ids, info["slaves"], churned, down_slaves, and bond_name when making these
checks and messages.
🧹 Nitpick comments (2)
tests/rules/network/test_dpf_validations.py (2)

138-219: ⚡ Quick win

Consider adding test coverage for non-LACP bond skip scenario.

The BOND_ACTIVE_BACKUP fixture is defined but not used in any test scenario. Consider adding a test case to scenario_passed or a new skip scenario list to verify that the rule correctly skips when only non-LACP bonds (e.g., active-backup) are present. This would exercise the skip path at lines 97-98 and 134 of the rule implementation.

💡 Suggested additional test scenario
     scenario_passed = [
         RuleScenarioParams(
             scenario_title="lacp_bond_healthy_two_slaves",
             cmd_input_output_dict={
                 "ls /proc/net/bonding/": CmdOutput("bond0"),
                 "cat /proc/net/bonding/bond0": CmdOutput(BOND_LACP_HEALTHY),
             },
         ),
     ]
 
+    scenario_skip = [
+        RuleScenarioParams(
+            scenario_title="only_non_lacp_bonds",
+            cmd_input_output_dict={
+                "ls /proc/net/bonding/": CmdOutput("bond0"),
+                "cat /proc/net/bonding/bond0": CmdOutput(BOND_ACTIVE_BACKUP),
+            },
+        ),
+    ]
+
     scenario_failed = [

Then add the corresponding test method:

    `@pytest.mark.parametrize`("scenario_params", scenario_skip)
    def test_scenario_skip(self, scenario_params, tested_object):
        RuleTestBase.test_scenario_skip(self, scenario_params, tested_object)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/rules/network/test_dpf_validations.py` around lines 138 - 219, Add a
test that exercises the non‑LACP (active-backup) skip path for DpuBondLacpHealth
by creating a new scenario list (e.g., scenario_skip) that uses the
BOND_ACTIVE_BACKUP fixture in cmd_input_output_dict and expected to be skipped,
then add a pytest param test method def test_scenario_skip(self,
scenario_params, tested_object): that calls
RuleTestBase.test_scenario_skip(self, scenario_params, tested_object). Ensure
the new scenario references BOND_ACTIVE_BACKUP and that the test name
(test_scenario_skip) and scenario list (scenario_skip) match exactly so the
rule’s skip branch is covered.

246-314: ⚡ Quick win

Add test coverage for multiple local_ip and missing node_ip scenarios.

The test suite is missing coverage for two scenarios handled by the rule:

  1. Multiple different local_ip values (lines 197-201 in rule): When geneve tunnels have inconsistent local_ip values, the rule returns a failed result with a specific error message. This scenario is not tested.

  2. Cannot determine node primary IP (lines 194-195 in rule): When /run/nodeip-configuration/primary-ip cannot be read, the rule returns skip. This scenario is not tested.

💡 Suggested additional test scenarios

Add a fixture for multiple local_ip values:

OVS_SHOW_MULTIPLE_LOCAL_IPS = """Bridge br-int
    Port ovn-abc123-0
        Interface ovn-abc123-0
            type: geneve
            options: {csum="true", key=flow, local_ip="10.6.135.202", remote_ip="10.6.135.236"}
    Port ovn-def456-0
        Interface ovn-def456-0
            type: geneve
            options: {csum="true", key=flow, local_ip="10.6.135.1", remote_ip="10.6.135.225"}
"""

Add to scenario_failed:

        RuleScenarioParams(
            scenario_title="multiple_different_local_ips",
            cmd_input_output_dict={
                "ovs-vsctl show": CmdOutput(OVS_SHOW_MULTIPLE_LOCAL_IPS),
                "cat /run/nodeip-configuration/primary-ip": CmdOutput("10.6.135.202"),
            },
            failed_msg=(
                "Multiple different local_ip values in Geneve tunnels: ['10.6.135.1', '10.6.135.202']. "
                "OVS configuration may be inconsistent."
            ),
        ),

Add a skip scenario list:

    scenario_skip = [
        RuleScenarioParams(
            scenario_title="cannot_read_node_ip",
            cmd_input_output_dict={
                "ovs-vsctl show": CmdOutput(OVS_SHOW_WITH_GENEVE),
                "cat /run/nodeip-configuration/primary-ip": CmdOutput("", return_code=1),
            },
        ),
    ]

Then add the corresponding test method:

    `@pytest.mark.parametrize`("scenario_params", scenario_skip)
    def test_scenario_skip(self, scenario_params, tested_object):
        RuleTestBase.test_scenario_skip(self, scenario_params, tested_object)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/rules/network/test_dpf_validations.py` around lines 246 - 314, Tests
for TestOvnGeneveTunnelLocalIp are missing two scenarios: inconsistent Geneve
local_ip values and unreadable node primary IP; add a new fixture constant
(e.g., OVS_SHOW_MULTIPLE_LOCAL_IPS) representing two different local_ip values
and add a RuleScenarioParams to scenario_failed (title
"multiple_different_local_ips") using that fixture plus a primary-ip that
matches one of them and the expected failed_msg matching the rule's message;
also add a scenario_skip list with a RuleScenarioParams (title
"cannot_read_node_ip") where "ovs-vsctl show" uses OVS_SHOW_WITH_GENEVE and the
"cat /run/nodeip-configuration/primary-ip" CmdOutput has return_code=1, then add
a test method test_scenario_skip decorated with pytest.mark.parametrize over
scenario_skip that calls RuleTestBase.test_scenario_skip(self, scenario_params,
tested_object); reference TestOvnGeneveTunnelLocalIp, scenario_failed,
scenario_skip, OVS_SHOW_WITH_GENEVE, OVS_SHOW_MULTIPLE_LOCAL_IPS,
RuleScenarioParams, CmdOutput, and test_scenario_skip to locate where to insert
these.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/in_cluster_checks/rules/network/dpf_validations.py`:
- Line 158: The rule's links list is empty; update the links variable by adding
the required GitHub wiki URL for this rule so the rule points to its
documentation. Locate the links list (variable name links) in
src/in_cluster_checks/rules/network/dpf_validations.py and append the URL
https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-OVN-Geneve-tunnel-local_ip-matches-node-InternalIP
to that list (ensure it's a string entry in the existing list format used by
this rule).
- Around line 82-86: In run_rule, check the return code from
run_cmd(SafeCmdString("ls /proc/net/bonding/")) before parsing stdout: if rc !=
0, raise UnExpectedSystemOutput including the command context and the
stdout/stderr content so failures are surfaced instead of treating an error
string as bond names; only proceed to split stdout into bond_names when rc == 0.
Ensure you reference the run_rule method and the run_cmd/SafeCmdString call when
adding the guard and raising the exception.
- Line 26: The rule's links list is empty in dpf_validations.py; update the
links variable (links = []) to include the required wiki URL for this rule by
adding
"https://github.com/RedHatInsights/incluster-checks/wiki/Network---Verify-LACP-bond-health-on-DPU-ports"
so the links list contains that single string entry (ensure the variable name
links in the module remains unchanged).
- Around line 183-211: The current run_rule uses
_OVS_LOCAL_IP_RE.findall(ovs_output) which picks up any local_ip="..." anywhere
in the OVS dump; restrict extraction to only Geneve interfaces by first locating
the Geneve interface blocks/lines in the output (e.g., lines/stanzas that
contain "type=geneve" or an interface name/type that identifies geneve) from
_get_ovs_show(), then apply the local_ip regex only within those geneve-specific
blocks to build local_ips; update the logic around _OVS_LOCAL_IP_RE, local_ips
and geneve_local_ip accordingly so only Geneve local_ip values are considered
for the comparison with _get_node_ip().
- Around line 113-131: The code currently builds agg_ids with
set(s["aggregator_id"] for s in info["slaves"] if s["aggregator_id"]) which
ignores missing/empty aggregator IDs and can make agg_ids empty (wrongly marking
the bond healthy); update validation to first detect any slave where
s["aggregator_id"] is missing/empty and append an all_issues entry referencing
bond_name and those slave names (e.g. "missing aggregator_id on ..."), then
build agg_ids from all non-empty IDs and change the healthy condition (the final
if that appends to all_passed) to require that at least one aggregator ID exists
and is consistent (e.g. agg_ids and len(agg_ids) <= 1) so bonds with all-missing
IDs are not considered healthy; refer to symbols agg_ids, info["slaves"],
churned, down_slaves, and bond_name when making these checks and messages.

---

Nitpick comments:
In `@tests/rules/network/test_dpf_validations.py`:
- Around line 138-219: Add a test that exercises the non‑LACP (active-backup)
skip path for DpuBondLacpHealth by creating a new scenario list (e.g.,
scenario_skip) that uses the BOND_ACTIVE_BACKUP fixture in cmd_input_output_dict
and expected to be skipped, then add a pytest param test method def
test_scenario_skip(self, scenario_params, tested_object): that calls
RuleTestBase.test_scenario_skip(self, scenario_params, tested_object). Ensure
the new scenario references BOND_ACTIVE_BACKUP and that the test name
(test_scenario_skip) and scenario list (scenario_skip) match exactly so the
rule’s skip branch is covered.
- Around line 246-314: Tests for TestOvnGeneveTunnelLocalIp are missing two
scenarios: inconsistent Geneve local_ip values and unreadable node primary IP;
add a new fixture constant (e.g., OVS_SHOW_MULTIPLE_LOCAL_IPS) representing two
different local_ip values and add a RuleScenarioParams to scenario_failed (title
"multiple_different_local_ips") using that fixture plus a primary-ip that
matches one of them and the expected failed_msg matching the rule's message;
also add a scenario_skip list with a RuleScenarioParams (title
"cannot_read_node_ip") where "ovs-vsctl show" uses OVS_SHOW_WITH_GENEVE and the
"cat /run/nodeip-configuration/primary-ip" CmdOutput has return_code=1, then add
a test method test_scenario_skip decorated with pytest.mark.parametrize over
scenario_skip that calls RuleTestBase.test_scenario_skip(self, scenario_params,
tested_object); reference TestOvnGeneveTunnelLocalIp, scenario_failed,
scenario_skip, OVS_SHOW_WITH_GENEVE, OVS_SHOW_MULTIPLE_LOCAL_IPS,
RuleScenarioParams, CmdOutput, and test_scenario_skip to locate where to insert
these.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6174766b-3a4c-4acb-ac15-126d520afd59

📥 Commits

Reviewing files that changed from the base of the PR and between 309360d and 0f78ea6.

📒 Files selected for processing (4)
  • src/in_cluster_checks/domains/network_domain.py
  • src/in_cluster_checks/rules/network/dpf_validations.py
  • tests/domains/test_network_domain.py
  • tests/rules/network/test_dpf_validations.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/domains/test_network_domain.py
  • src/in_cluster_checks/domains/network_domain.py

@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from 0f78ea6 to b27c66f Compare May 19, 2026 10:04
if (
not down_slaves
and not missing_agg
and len(set(s["aggregator_id"] for s in info["slaves"])) <= 1

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.

isn't that problematic if it's < 1 (meaning it's 0)?
it will be the same as missing_agg has a value

objective_hosts = [Objectives.ALL_NODES]
unique_name = "dpu_bond_lacp_health"
title = "Verify LACP bond health on DPU ports"
links = ["https://github.com/RedHatInsights/incluster-checks/wiki/dpu_bond_lacp_health"]

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.

need to update this wiki with validation info

Comment thread src/in_cluster_checks/rules/network/dpf_validations.py
objective_hosts = [Objectives.ALL_NODES]
unique_name = "ovn_geneve_tunnel_local_ip"
title = "Verify OVN Geneve tunnel local_ip matches node InternalIP"
links = ["https://github.com/RedHatInsights/incluster-checks/wiki/ovn_geneve_tunnel_local_ip"]

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.

need to update this wiki with validation info

Comment on lines +204 to +205
if not ovs_output:
return RuleResult.skip("Cannot read OVS state")

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.

can be deleted as validation won't run as it covered on is_prerequisite_fulfilled

Comment on lines +207 to +209
local_ips = list(set(self._OVS_LOCAL_IP_RE.findall(ovs_output)))
if not local_ips:
return RuleResult.skip("No Geneve local_ip found in OVS")

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.

isn't that covered also as part of is_prerequisite_fulfilled on line 190?

@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from b27c66f to a2f6e26 Compare May 19, 2026 12:11

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/in_cluster_checks/rules/network/dpf_validations.py (2)

178-178: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope local_ip extraction to Geneve interfaces only.

Line 207 currently extracts every local_ip in ovs-vsctl show, including non-Geneve interfaces. This can report false failures/passes for this rule’s intent.

Suggested diff
-    _OVS_LOCAL_IP_RE = re.compile(r'local_ip="([^"]+)"')
+    _OVS_LOCAL_IP_RE = re.compile(r'local_ip="([^"]+)"')
@@
-        local_ips = list(set(self._OVS_LOCAL_IP_RE.findall(ovs_output)))
+        local_ips: List[str] = []
+        in_geneve_interface = False
+        for raw_line in ovs_output.splitlines():
+            line = raw_line.strip()
+            if line.startswith("type:"):
+                in_geneve_interface = line.split(":", 1)[1].strip() == "geneve"
+                continue
+            if in_geneve_interface and line.startswith("options:"):
+                match = self._OVS_LOCAL_IP_RE.search(line)
+                if match:
+                    local_ips.append(match.group(1))
+
+        local_ips = sorted(set(local_ips))

As per coding guidelines, Verify the rule actually validates functionality, not just connectivity or presence.

Also applies to: 207-209

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` at line 178, The
current _OVS_LOCAL_IP_RE captures any local_ip in ovs-vsctl output; restrict it
to Geneve interfaces only by changing the pattern used in _OVS_LOCAL_IP_RE (and
the code that uses it at the local_ip extraction site around lines 207-209) so
it only matches a local_ip that belongs to a Geneve interface (e.g., require the
interface/type context such as an interface name starting with "geneve" or a
preceding/type field indicating "geneve" in the same stanza). Update the regex
and extraction logic (referencing _OVS_LOCAL_IP_RE) so non-Geneve interfaces are
ignored.

24-27: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add supported_profiles for DPF-scoped rules.

These validators are introduced as DPF-focused checks, but both classes currently execute for all profiles. Please declare supported_profiles = {"gpu"} to avoid non-target profile noise/failures.

Suggested diff
 class DpuBondLacpHealth(Rule):
@@
     objective_hosts = [Objectives.ALL_NODES]
+    supported_profiles = {"gpu"}
     unique_name = "dpu_bond_lacp_health"
@@
 class OvnGeneveTunnelLocalIp(Rule):
@@
     objective_hosts = [Objectives.ALL_NODES]
+    supported_profiles = {"gpu"}
     unique_name = "ovn_geneve_tunnel_local_ip"

As per coding guidelines, If a rule is profile-specific, it must declare supported_profiles = {"profile-name"}.

Also applies to: 173-176

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/in_cluster_checks/rules/network/dpf_validations.py` around lines 24 - 27,
Add the class attribute supported_profiles = {"gpu"} to the rule class that
defines objective_hosts, unique_name = "dpu_bond_lacp_health", title = "Verify
LACP bond health on DPU ports" so it only runs for the GPU/DPF profile; also add
the same supported_profiles = {"gpu"} to the other DPF-scoped rule class
referenced around lines 173-176 (the other rule with similar
objective/unique_name) so both validators are limited to the "gpu" profile.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/rules/network/test_dpf_validations.py`:
- Around line 318-325: The skip scenario `_scenario_skip_node_ip` is defined but
not executed; update the test harness that collects/iterates scenarios (the list
or param for the test that calls `run_rule()`, e.g., RULE_SCENARIOS or the
parametrized test function) to include `_scenario_skip_node_ip` so the
missing-node-IP SKIP path is exercised; likewise add the analogous scenario
defined around lines 353-367 into the same scenarios list/param set so both
edge-case SKIP paths are covered by the test that calls `run_rule()`.

---

Duplicate comments:
In `@src/in_cluster_checks/rules/network/dpf_validations.py`:
- Line 178: The current _OVS_LOCAL_IP_RE captures any local_ip in ovs-vsctl
output; restrict it to Geneve interfaces only by changing the pattern used in
_OVS_LOCAL_IP_RE (and the code that uses it at the local_ip extraction site
around lines 207-209) so it only matches a local_ip that belongs to a Geneve
interface (e.g., require the interface/type context such as an interface name
starting with "geneve" or a preceding/type field indicating "geneve" in the same
stanza). Update the regex and extraction logic (referencing _OVS_LOCAL_IP_RE) so
non-Geneve interfaces are ignored.
- Around line 24-27: Add the class attribute supported_profiles = {"gpu"} to the
rule class that defines objective_hosts, unique_name = "dpu_bond_lacp_health",
title = "Verify LACP bond health on DPU ports" so it only runs for the GPU/DPF
profile; also add the same supported_profiles = {"gpu"} to the other DPF-scoped
rule class referenced around lines 173-176 (the other rule with similar
objective/unique_name) so both validators are limited to the "gpu" profile.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 94e26658-9dc3-4fcc-9e67-d96bc4d11886

📥 Commits

Reviewing files that changed from the base of the PR and between 0f78ea6 and a2f6e26.

📒 Files selected for processing (4)
  • src/in_cluster_checks/domains/network_domain.py
  • src/in_cluster_checks/rules/network/dpf_validations.py
  • tests/domains/test_network_domain.py
  • tests/rules/network/test_dpf_validations.py
✅ Files skipped from review due to trivial changes (1)
  • tests/domains/test_network_domain.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/in_cluster_checks/domains/network_domain.py

Comment thread tests/rules/network/test_dpf_validations.py Outdated
@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch 5 times, most recently from 08f078b to d7ad2e3 Compare May 19, 2026 17:28

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/rules/network/test_dpf_validations.py (1)

213-213: ⚡ Quick win

Move Status import to module scope.

Please avoid imports inside test methods; import Status once at the top-level.

Proposed fix
 import pytest
 
 from in_cluster_checks.rules.network.dpf_validations import DpuBondLacpHealth, OvnGeneveTunnelLocalIp
+from in_cluster_checks.utils.enums import Status
 from tests.pytest_tools.test_operator_base import CmdOutput
 from tests.pytest_tools.test_rule_base import RuleScenarioParams, RuleTestBase
@@
     def test_scenario_skip(self, scenario_params, tested_object):
         """Test that non-LACP bonds result in SKIP."""
-        from in_cluster_checks.utils.enums import Status
-
         self._init_validation_object(tested_object, scenario_params)
@@
     def test_scenario_skip_geneve(self, scenario_params, tested_object):
         """Test that missing node IP results in SKIP."""
-        from in_cluster_checks.utils.enums import Status
-
         self._init_validation_object(tested_object, scenario_params)

As per coding guidelines, Always place all imports at the top of the file; never add imports in the middle of functions or methods.

Also applies to: 382-382

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/rules/network/test_dpf_validations.py` at line 213, The inline imports
of Status (from in_cluster_checks.utils.enums import Status) inside test
functions should be moved to module scope: add a single top-level import "from
in_cluster_checks.utils.enums import Status" alongside the other imports at the
top of tests/rules/network/test_dpf_validations.py, then remove the duplicate
inline imports inside the test methods (the ones currently importing Status).
Ensure all test functions that reference Status use the module-level name.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/in_cluster_checks/rules/network/dpf_validations.py`:
- Around line 224-237: The code in run_rule calls self._get_ovs_show() and then
indexes local_ips[0] without guarding for ovs_output being None or local_ips
being empty; update the logic in run_rule (around calls to _get_ovs_show and
_extract_geneve_local_ips) to first check if ovs_output is falsy and if so raise
UnExpectedSystemOutput with a clear message, then compute local_ips and if
local_ips is empty return RuleResult.failed (or raise UnExpectedSystemOutput per
guideline) instead of indexing; ensure you reference the _get_ovs_show,
_extract_geneve_local_ips, and geneve_local_ip usage and use
UnExpectedSystemOutput from core/exceptions to represent command failure cases.

---

Nitpick comments:
In `@tests/rules/network/test_dpf_validations.py`:
- Line 213: The inline imports of Status (from in_cluster_checks.utils.enums
import Status) inside test functions should be moved to module scope: add a
single top-level import "from in_cluster_checks.utils.enums import Status"
alongside the other imports at the top of
tests/rules/network/test_dpf_validations.py, then remove the duplicate inline
imports inside the test methods (the ones currently importing Status). Ensure
all test functions that reference Status use the module-level name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: edc957bf-6d47-41d4-a7f8-d2fc50fc71af

📥 Commits

Reviewing files that changed from the base of the PR and between a2f6e26 and d7ad2e3.

📒 Files selected for processing (5)
  • src/in_cluster_checks/domains/network_domain.py
  • src/in_cluster_checks/rules/network/dpf_validations.py
  • src/profiles/profiles.yaml
  • tests/domains/test_network_domain.py
  • tests/rules/network/test_dpf_validations.py
✅ Files skipped from review due to trivial changes (1)
  • src/profiles/profiles.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/domains/test_network_domain.py
  • src/in_cluster_checks/domains/network_domain.py

Comment thread src/in_cluster_checks/rules/network/dpf_validations.py
@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch 4 times, most recently from 8276327 to 240b955 Compare May 20, 2026 08:35
Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
if not local_ips:
return RuleResult.skip("No Geneve local_ip found in OVS output")

node_ip = self._get_node_ip()

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.

Why does _get_node_ip() read /run/nodeip-configuration/primary-ip instead of using self.get_host_ip() (Kubernetes InternalIP)?

Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
Comment thread src/in_cluster_checks/rules/network/dpf_validations.py Outdated
def run_rule(self) -> RuleResult:
"""Check that Geneve tunnel local_ip matches node InternalIP."""
ovs_output = self._get_ovs_show()
if ovs_output is None:

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.

Since the prerequisite already verified that _get_ovs_show() returns non-None output, this check is redundant.
Suggested refactor:

def _get_ovs_show(self) -> str:
    """Run ovs-vsctl show and return stdout."""
    return self.get_output_from_run_cmd(SafeCmdString("ovs-vsctl show"))

def is_prerequisite_fulfilled(self) -> PrerequisiteResult:
    try:
        stdout = self._get_ovs_show()
    except UnExpectedSystemOutput:
        return PrerequisiteResult.not_met("Cannot access OVS")
    
    if "geneve" not in stdout:
        return PrerequisiteResult.not_met("No Geneve tunnels configured")
    return PrerequisiteResult.met()

def run_rule(self) -> RuleResult:
    ovs_output = self._get_ovs_show()  # Raises UnExpectedSystemOutput if fails
    tunnel_info = self._extract_geneve_tunnel_info(ovs_output)
    # ...

supported_profiles = {"dpf"}
unique_name = "dpu_bond_lacp_health"
title = "Verify LACP bond health on DPU ports"
links = ["https://github.com/RedHatInsights/incluster-checks/wiki/DPF---LACP-bond-health-on-DPU-ports"]

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.

wiki page is empty

Comment thread src/in_cluster_checks/rules/network/dpf_validations.py
supported_profiles = {"dpf"}
unique_name = "ovn_geneve_tunnel_local_ip"
title = "Verify OVN Geneve tunnel local_ip matches node InternalIP"
links = ["https://github.com/RedHatInsights/incluster-checks/wiki/DPF---OVN-Geneve-tunnel-local_ip"]

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.

this isnt a valid wiki url

@tkarbach

tkarbach commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Hi @szigmon! 👋

I've made some improvements to your PR that would enhance code quality and robustness. The changes include:

  • Extract BondBase shared class for common bond prerequisite checking (reduces duplication with VerifyBondedInterfacesUp)
  • Improve parsing robustness in DpuBondLacpHealth by splitting by blank lines first (safer against malformed kernel output)
  • Migrate to file_utils methods instead of direct command execution (consistent with the rest of the codebase)

All 134 network tests pass ✅

To incorporate these changes, you can pull from my fork:

git remote add tkarbach https://github.com/tkarbach/incluster-checks.git
git fetch tkarbach pr-39
git reset --hard tkarbach/pr-39
git push origin dpf-lacp-bond-health --force-with-lease

Alternatively, you can review the changes at #65 and cherry-pick what you'd like.

Let me know if you'd prefer a different approach!

@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from 240b955 to 89e429c Compare June 9, 2026 11:13
@szigmon
szigmon requested a review from liatpele-redhat as a code owner June 9, 2026 11:13
@codecov-commenter

codecov-commenter commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.01198% with 10 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@ddf31c9). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...in_cluster_checks/rules/network/dpf_validations.py 93.58% 10 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main      #39   +/-   ##
=======================================
  Coverage        ?   86.79%           
=======================================
  Files           ?       59           
  Lines           ?     6756           
  Branches        ?        0           
=======================================
  Hits            ?     5864           
  Misses          ?      892           
  Partials        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from 71dc2c4 to ac39695 Compare June 11, 2026 09:55
@szigmon

szigmon commented Aug 9, 2026

Copy link
Copy Markdown
Author

Tested on OCP 4.22 cluster (3 VM nodes, no DPU hardware):

  • All 956 unit tests pass, pre-commit clean
  • OvnGeneveTunnelLocalIp with --profile dpf: PASS on live cluster — Geneve local_ip matches node primary IP
  • DpuBondLacpHealth with --profile dpf: correctly returns NA (no bond interfaces on VMs)
  • Profile filtering works — rules blocked without --profile dpf
  • Verified compatibility with openshift-dpf automation (v4.22)

Not tested: No bare metal nodes with BlueField DPUs available — DpuBondLacpHealth LACP parsing logic (slave down, churn, aggregator mismatch) was only validated via unit tests, not on real bond interfaces. Full e2e validation requires a cluster with DPU workers and LACP bonds.

@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from 9b6de8f to 9bf4049 Compare August 9, 2026 13:39
Add two new rules for OpenShift clusters with NVIDIA BlueField DPUs:
- DpuBondLacpHealth: verifies LACP bond health on DPU ports
- OvnGeneveTunnelLocalIp: verifies Geneve tunnel local_ip matches node IP

Includes BondBase extraction, dpf profile, and full test coverage.

Assisted-by: Claude Code (Claude Opus 4.6) <noreply@anthropic.com>
@szigmon
szigmon force-pushed the dpf-lacp-bond-health branch from 9bf4049 to c634fa6 Compare August 9, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants