Skip to content

[release-4.22] OCPBUGS-99290: e2e: fix broken checks and rework netqueue tests to avoid ARM ethtool blackout flakes - #1568

Open
openshift-cherrypick-robot wants to merge 5 commits into
openshift:release-4.22from
openshift-cherrypick-robot:cherry-pick-1557-to-release-4.22
Open

[release-4.22] OCPBUGS-99290: e2e: fix broken checks and rework netqueue tests to avoid ARM ethtool blackout flakes#1568
openshift-cherrypick-robot wants to merge 5 commits into
openshift:release-4.22from
openshift-cherrypick-robot:cherry-pick-1557-to-release-4.22

Conversation

@openshift-cherrypick-robot

Copy link
Copy Markdown

This is an automated cherry-pick of #1557

/assign oblau

oblau added 5 commits July 21, 2026 13:58
…n netqueue suite

strings.ContainsAny(s, chars) reports whether ANY character in chars
appears in s -- it does not check for a substring. Since device is a NIC
name like "enP2s2f0np0", this check was true for almost any tuned output
containing common letters, regardless of whether that specific device name
actually appeared in devices_udev_regex. Replace with strings.Contains in
the 4 tests that assert tuned picked up the configured device filter:
test_id 40543, 40545, 72051, 40668.

Every netqueue test body was wrapped in a guard like:
  if profile.Spec.Net.UserLevelNetworking != nil && *ULN && len(Devices) == 0 { ... }
This guard was true in the common case only because BeforeEach
unconditionally set Net whenever it was nil, and AfterEach unconditionally
reverted to the initial profile after every test. If the *initial* cluster
profile already had a non-nil Net with ULN=false or leftover Devices, the
guard evaluated false and the entire test body was skipped -- the test
reported PASS while verifying nothing. Guards removed from test_id 40308,
40543, 40545, 72051, and 40668.

Once the guard is gone, test_id 40308 and 40542 are identical bodies
(both call checkDeviceSetWithReservedCPU unconditionally). Dropping 40542
as a duplicate; 40308 covers the same case.

getReservedCPUSize re-parsed profile.Spec.CPU.Reserved into a cpuset on
every 5s poll, even though the value is static for the
whole test run. Compute it once as reservedCPUCount in the BeforeAll and
thread it through instead: checkDeviceSetWithReservedCPU's signature
changes from taking the whole *performancev2.PerformanceProfile to taking
reservedCPUCount int directly. getReservedCPUSize is removed.

AI Attribution: AIA Human-AI blend, Content edits, New content,
Human-initiated, Reviewed, opus 4.6 high v1.0
…BeforeAll/AfterAll pass

- BeforeEach (enable ULN) and AfterEach (revert to initial) never
  isolated tests: both call profiles.UpdateWithRetry with no wait
  after, so the next test's own update fires before tuned reconciles
  the previous one.

- Example: test A AfterEach reverts the profile, but test B
  immediately calls UpdateWithRetry with its own Devices before that
  revert lands -- B can still see A leftover devices_udev_regex
  even though "cleanup" ran in between.

- No test actually needs a clean starting profile: each one fully
  overwrites profile.Spec.Net itself or polls until its own expected
  state converges. The per-test revert bought nothing.

- Replaced with one BeforeAll (set baseline once) and one AfterAll
  (revert once, only if state changed). Side effect: total profile
  updates per run drop from 10-16 to 5-6.
…tead of per-test rediscovery

- checkDeviceSupport was called independently by every test, re-running
  ethtool discovery over the tuned pod each time. checkDeviceSetWithReservedCPU
  then polled that same rediscovery every 5s while waiting for convergence.
  A slow update and a broken feature produced the same timeout -> Skip.

- Split checkDeviceSupport into discoverMultiQueueNICs (per-node NIC
  enumeration, now via nodes.GetNodeInterfaces/node_inspector instead of
  tuned-pod exec) and getCombinedChannels (single NIC -> combined channel
  count). One does discovery, one does the ethtool read; each is reusable
  on its own.

- discoverMultiQueueNICs runs once in BeforeAll into baselineMultiQueueNICs
  (map[string]map[nodes.NodeInterface]int); BeforeAll Skips the whole suite
  upfront if none are found. Tests read from this baseline instead of
  rediscovering.

- checkDeviceSetWithReservedCPU -> waitForNICsToMatchReservedCPU: no longer
  mutates a caller map or rediscovers, just polls the baseline. Also fixes
  a real bug -- the old function returned success on the first NIC that
  matched reservedCPUCount, even for tests asserting all supported NICs
  converge.

- getRandomNodeDevice updated for the new map type, returns a NodeInterface
  instead of a bare string. Drops its defensive empty-name check --
  discoverMultiQueueNICs never inserts zero-value entries, unlike the old
  checkDeviceSupport.

- 40543/40545/72051/40668 now pull their target device from the baseline
  instead of calling checkDeviceSupport themselves. profile updates use
  `var err error` explicitly to avoid re-shadowing the Describe-scoped
  profile var now that device is a struct, not a string, everywhere.

AI Attribution: AIA Human-AI blend, Content edits, New content,
Human-initiated, Reviewed, opus 4.6 high v1.0
…line

- checkDeviceSupport split into discoverMultiQueueNICs (enumeration) and
  getCombinedChannels (per-NIC ethtool parsing). Discovery now runs once in
  BeforeAll into baselineMultiQueueNICs, Skipping the whole suite upfront if
  none are found, instead of each test re-discovering and skipping
  individually.

- checkDeviceSetWithReservedCPU -> waitForNICsToMatchReservedCPU: takes a
  NIC map to poll instead of rediscovering. Also fixes a bug where it
  returned success on the first matching NIC instead of waiting for all of
  them to converge.

- getRandomNodeDevice: updated for the new map type, returns a
  NodeInterface, drops its now-unneeded empty-name guard.

- Each test builds its own scoped NIC map (targetNICs/matchedNICs/
  expectedNICs) instead of polling the full baseline, so convergence checks
  validate only the NICs that test actually touches. Per-test
  checkDeviceSupport+Skip guards removed, BeforeAll already guarantees them.

- Skip-on-error replaced with Expect(err).ToNot(HaveOccurred()) throughout.

- profile.Spec.Net setup now only sets .Devices (UserLevelNetworking=true
  comes from the BeforeAll baseline).

- var err error added before some profile fetches so profile, err = ...
  assigns the Describe-scoped var instead of shadowing it.

- nodes.GetByName moved before scoped-map-building/profile update in every
  test, to keep the exec call out of the post-update ethtool blackout
  window.

- 40545: wildcard check now searches for the derived udev-regex form
  ("iface.*") instead of the literal device name, since tuned stores
  wildcards as regex in devices_udev_regex.

- 72051: pick device only from nodes with >=2 NICs, a single-NIC node can't
  demonstrate negation. Restored the negative assertion (pre/post
  combined-channel comparison on the negated NIC) lost when
  checkDeviceSupport was removed. Tuned-config check now searches for the
  negated pattern ("!iface") instead of the bare device name, to avoid a
  false positive from a leftover config.

- 40668: tuned-config check upgraded to a 3-way match (interface name +
  vendor ID + device ID); it previously only checked the interface name.

AI Attribution: AIA Human-AI blend, Content edits, New content,
Human-initiated, Reviewed, opus 4.6 high v1.0
…ests

- Converted remaining plain `//` comments to By() calls; unified the 4
  tests' tuned-config-check block (identical By text, no blank lines) --
  they run the same grep, differing only in what they search for.

- Added a By() before each test's final convergence check (previously
  unlabeled); all now end in "converged to reserved CPU count".

- Added testlog.Infof after each getRandomNodeDevice call, logging which
  NIC/node the run picked.

- 40545/40668: added missing By("Updating the performance profile"),
  matching 40543. 72051: split its stale "Enable ULN..." By into the same
  two-step shape as the other tests.

- 40668: removed a redundant duplicate nodes.GetByName call.

- AfterAll now logs the profile diff before reverting, mirroring
  BeforeAll's equivalent branch.

- Added a top-of-file comment on the ARM ethtool -L blackout window, and
  a doc comment on getRandomNodeDevice explaining its random-pick trick.

AI Attribution: AIA Human-AI blend, Content edits, New content,
Human-initiated, Reviewed, opus 4.6 high v1.0
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 48604aa9-47cd-425c-b556-441d8e5f9c7d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

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

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

@qodo-for-rh-openshift

Copy link
Copy Markdown

PR Summary by Qodo

Stabilize netqueues e2e checks by pre-discovering NICs and polling through TuneD blackouts

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Fix TuneD config assertions by matching full NIC names/regexes (no more ContainsAny false
 positives).
• Prevent silent PASS-by-skip by removing ineffective guards and de-duplicating the reserved-CPU
 test.
• Reduce ethtool blackout flakes by snapshotting multi-queue NICs upfront and polling until
 convergence.
Diagram

graph TD
  A["Ginkgo netqueues suite"] --> B["Discover multi-queue NICs"] --> C["Update PerformanceProfile.Net"] --> D["TuneD applies ethtool -L"] --> E["Poll combined channels"] --> F["Gomega assertions"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Wrap node exec in a generic retry/backoff helper
  • ➕ Centralizes blackout-handling for all tests that exec into nodes
  • ➕ Reduces per-suite bespoke polling code
  • ➖ Still needs suite-specific convergence conditions (which NICs, what ‘ready’ means)
  • ➖ Can mask real failures if retries are too permissive
2. Validate TuneD-applied state via TuneD/Kubernetes signals instead of ethtool polling
  • ➕ Avoids dependence on node reachability during transient blackouts
  • ➕ Potentially faster and less flaky if a reliable status source exists
  • ➖ May not prove the kernel/NIC state actually changed (tuned.conf/CR status can be ahead of reality)
  • ➖ Requires additional product surface (status/metrics) that may not exist or be stable
3. Use targeted NICs only (avoid touching br-ex uplink) via strict test device selection
  • ➕ Avoids triggering the blackout at the source, reducing cluster disturbance
  • ➕ Faster feedback if a safe NIC is available
  • ➖ Hard to guarantee across platforms/labs; can reduce coverage if only uplink NIC is multi-queue capable
  • ➖ Still needs robust discovery/selection logic

Recommendation: The PR’s approach (pre-discover multi-queue NICs before any profile mutation + poll until ethtool convergence) is the most direct way to both handle transient node unreachability and assert the real device state. Consider factoring the polling/exec retry patterns into shared test utilities later, but keeping the convergence logic local to this suite is reasonable for correctness.

Files changed (1) +287 / -244

Tests (1) +287 / -244
netqueues.goRework netqueues suite to avoid skipped checks and ethtool-blackout flakes +287/-244

Rework netqueues suite to avoid skipped checks and ethtool-blackout flakes

• Moves netqueue test setup to a single BeforeAll baseline: snapshot multi-queue NICs, compute reserved CPU count, and normalize Profile.Net to ULN=true without device filters. Replaces brittle/incorrect tuned.conf assertions (ContainsAny) with deterministic substring checks and rewrites tests to poll ethtool combined channels until convergence, including explicit verification that negated NICs remain unchanged. Cleans up teardown by reverting the profile only once in AfterAll (and only when spec differs) and removes a duplicate test body.

test/e2e/performanceprofile/functests/1_performance/netqueues.go

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@openshift-cherrypick-robot: Jira Issue OCPBUGS-97809 has been cloned as Jira Issue OCPBUGS-99290. Will retitle bug to link to clone.
/retitle [release-4.22] OCPBUGS-99290: e2e: fix broken checks and rework netqueue tests to avoid ARM ethtool blackout flakes

Details

In response to this:

This is an automated cherry-pick of #1557

/assign oblau

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci Bot changed the title [release-4.22] OCPBUGS-97809: e2e: fix broken checks and rework netqueue tests to avoid ARM ethtool blackout flakes [release-4.22] OCPBUGS-99290: e2e: fix broken checks and rework netqueue tests to avoid ARM ethtool blackout flakes Jul 21, 2026
@openshift-ci-robot openshift-ci-robot added jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. labels Jul 21, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@openshift-cherrypick-robot: This pull request references Jira Issue OCPBUGS-99290, which is valid. The bug has been moved to the POST state.

7 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state New, which is one of the valid states (NEW, ASSIGNED, POST)
  • release note type set to "Release Note Not Required"
  • dependent bug Jira Issue OCPBUGS-97809 is in the state ON_QA, which is one of the valid states (MODIFIED, ON_QA, VERIFIED)
  • dependent Jira Issue OCPBUGS-97809 targets the "5.0.0" version, which is one of the valid target versions: 5.0.0
  • bug has dependents

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

This is an automated cherry-pick of #1557

/assign oblau

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@qodo-for-rh-openshift

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Negated NIC regex mismatch 🐞 Bug ≡ Correctness
Description
In test_id:72051, the test expects tuned.conf to contain the literal negated pattern (e.g.,
"!ens5"), but TuneD renders negated InterfaceName filters as a negative-lookahead regex (e.g.,
"^INTERFACE=(?!ens5)"). This causes the Eventually() check to time out and fail even when the
generated tuned profile is correct.
Code

test/e2e/performanceprofile/functests/1_performance/netqueues.go[R270-276]

			Eventually(func() bool {
				out, err := pods.WaitForPodOutput(context.TODO(), testclient.K8sClient, tunedPod, tunedCmd)
				if err != nil {
					return false
				}
-				return strings.ContainsAny(string(out), device)
-			}, cluster.ComputeTestTimeout(2*time.Minute, RunningOnSingleNode), 5*time.Second).Should(BeTrue(), "could not get a tuned profile set with devices_udev_regex")
-
-			nodesDevices = make(map[string]map[string]int)
-			err = checkDeviceSetWithReservedCPU(context.TODO(), workerRTNodes, nodesDevices, *profile)
-			if err != nil {
-				Skip("Skipping Test: Unable to set Network queue size to reserved cpu count")
-			}
-
-			// After at least one NIC was configured, make sure that the selected NIC was NOT it
-
-			Expect(nodesDevices).To(HaveKey(node.Name))
-			Expect(nodesDevices[node.Name]).To(HaveKey(device))
-			Expect(nodesDevices[node.Name][device]).ToNot(Equal(getReservedCPUSize(profile.Spec.CPU)))
+				return strings.Contains(string(out), devicePattern)
+			}, cluster.ComputeTestTimeout(2*time.Minute, RunningOnSingleNode), 5*time.Second).Should(BeTrue(), "tuned config does not contain %q", devicePattern)
Relevance

⭐⭐⭐ High

PR #1557 already merged reworking netqueues tests and tuned-config checks; this mismatch fix aligns
with that intent.

PR-#1557

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The e2e test checks tuned.conf for the literal string "!<iface>", but the TuneD generator transforms
negated interface names into ^INTERFACE=(?!<iface>), so tuned.conf will not contain the literal
! pattern even when correct.

test/e2e/performanceprofile/functests/1_performance/netqueues.go[228-276]
pkg/performanceprofile/controller/performanceprofile/components/tuned/tuned.go[170-177]
pkg/performanceprofile/controller/performanceprofile/components/tuned/tuned_test.go[470-489]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The negative device filter check (`devicePattern := "!"+device.Name`) is asserted against tuned.conf via `strings.Contains(out, devicePattern)`, but TuneD does not emit `!<iface>` in `devices_udev_regex`; it emits a negative-lookahead regex like `^INTERFACE=(?!<iface>)`.

## Issue Context
TuneD’s net device regex rendering converts `!<iface>` into `^INTERFACE=(?!<iface>)` (see generator logic and its unit tests). The e2e test should validate the rendered `devices_udev_regex` format, not the input filter syntax.

## Fix Focus Areas
- test/e2e/performanceprofile/functests/1_performance/netqueues.go[215-288]

### Suggested change
In the negative-match test, replace the tuned.conf assertion:
- from: `strings.Contains(string(out), devicePattern)`
- to: check for the rendered regex, e.g.:
 - `expectedUdevRegex := "^INTERFACE=(?!" + device.Name + ")"`
 - `strings.Contains(string(out), expectedUdevRegex)`

(Optionally assert the full key: `devices_udev_regex=` + expectedUdevRegex to reduce false positives.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

tunedCmd := []string{"bash", "-c",
fmt.Sprintf("grep devices_udev_regex %s", tunedConfPath)}
tunedPod := nodes.TunedForNode(node, RunningOnSingleNode)
Eventually(func() bool {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Negated nic regex mismatch 🐞 Bug ≡ Correctness

In test_id:72051, the test expects tuned.conf to contain the literal negated pattern (e.g.,
"!ens5"), but TuneD renders negated InterfaceName filters as a negative-lookahead regex (e.g.,
"^INTERFACE=(?!ens5)"). This causes the Eventually() check to time out and fail even when the
generated tuned profile is correct.
Agent Prompt
## Issue description
The negative device filter check (`devicePattern := "!"+device.Name`) is asserted against tuned.conf via `strings.Contains(out, devicePattern)`, but TuneD does not emit `!<iface>` in `devices_udev_regex`; it emits a negative-lookahead regex like `^INTERFACE=(?!<iface>)`.

## Issue Context
TuneD’s net device regex rendering converts `!<iface>` into `^INTERFACE=(?!<iface>)` (see generator logic and its unit tests). The e2e test should validate the rendered `devices_udev_regex` format, not the input filter syntax.

## Fix Focus Areas
- test/e2e/performanceprofile/functests/1_performance/netqueues.go[215-288]

### Suggested change
In the negative-match test, replace the tuned.conf assertion:
- from: `strings.Contains(string(out), devicePattern)`
- to: check for the rendered regex, e.g.:
  - `expectedUdevRegex := "^INTERFACE=(?!" + device.Name + ")"`
  - `strings.Contains(string(out), expectedUdevRegex)`

(Optionally assert the full key: `devices_udev_regex=` + expectedUdevRegex to reduce false positives.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@openshift-ci
openshift-ci Bot requested review from MarSik and yanirq July 21, 2026 14:04
@openshift-ci

openshift-ci Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: openshift-cherrypick-robot
Once this PR has been reviewed and has the lgtm label, please assign jmencak for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@oblau

oblau commented Jul 22, 2026

Copy link
Copy Markdown
Member

/retest

2 similar comments
@oblau

oblau commented Jul 22, 2026

Copy link
Copy Markdown
Member

/retest

@oblau

oblau commented Jul 22, 2026

Copy link
Copy Markdown
Member

/retest

@oblau

oblau commented Jul 26, 2026

Copy link
Copy Markdown
Member

/verified by oblau

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Jul 26, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@oblau: This PR has been marked as verified by oblau.

Details

In response to this:

/verified by oblau

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@oblau

oblau commented Jul 26, 2026

Copy link
Copy Markdown
Member

/retest

1 similar comment
@oblau

oblau commented Jul 28, 2026

Copy link
Copy Markdown
Member

/retest

@openshift-ci

openshift-ci Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

@openshift-cherrypick-robot: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-gcp-pao f21c07f link true /test e2e-gcp-pao
ci/prow/e2e-hypershift-pao f21c07f link true /test e2e-hypershift-pao

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants