Skip to content

The triage ladder: sos-report, error strings and the checklists

S4·E402:10, and the restart that deletes the answer · A bridge call opened by the night shift, five days before acceptance

S4·E4Evaluate~30 minsources checked todaylab mutates hardwareverified against NVIDIA Network Operator v26.7.0 SOS-Report page and troubleshooting index re-fetched 2026-09-07; Spectrum-X verify-and-troubleshoot re-fetched 2026-09-07; rdma-system(8) and Kubernetes Topology Manager docs as cited

Builds on: RDMA in pods: shared plugin vs exclusive netns, NUMA alignment with Topology Manager and CPU Manager

Before you read: what do you already know?

3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.

After this lesson you can

  • Justify collecting the sos-report before making any change, using what the archive actually contains.
  • Run the five-command host ladder in order and stop at the first rung that fails.
  • Map a verbatim error string to its cause without re-deriving the mechanism each time.
  • Judge whether a symptom is a host problem or a Kubernetes problem and name the one command that decides it.

Episode 4 — 02:10, and the restart that deletes the answer

The situation · A bridge call opened by the night shift, five days before acceptance

The night-shift operator called it in at 02:10 because he is the one who notices things: a pod restarting on the node whose front panel he labelled rail0 and rail1 himself, with the label maker he brings to every rebuild. By the time you join, the operations lead has a sentence ready — “let us just restart the driver DaemonSet and see” — and he is not being reckless. It worked once, and acceptance is Friday.

The reason to stop him is not caution, it is evidence. The Network Operator ships its own collector, kubectl netop-sosreport, and what it gathers is the argument for running it first: every operator custom resource and CRD, deployments and DaemonSet specs, events and webhook configurations, node labels, conditions and allocatable resources — and current and previous container logs.[1] Previous-container logs are the whole case. If the answer is the shared RDMA device plugin exiting with Exiting.. can not change : incorrect RDMA subsystem network namespace, that string lives in a dead container’s log, and the node’s rdma system show mode is what proves it.[3][4] If speed is the objection, the documented fast form exists: --skip-diagnostics --log-lines 1000.[1]

The first thing an escalation breaks should never be the evidence.

What you need next is an order to work in, so that ten minutes buys a diagnosis instead of five opinions.

1Collect before you change

The Network Operator ships its own collector. The script is kubectl-netop_sosreport, invoked as kubectl netop-sosreport [OPTIONS], or standalone as ./network-operator-sosreport.sh [OPTIONS].[1] It requires kubectl in PATH, a valid kubeconfig, cluster-admin recommended, Bash 4.0+, Python 3.6+ for the HTML report, plus tar, gzip and sha256sum.[1]

What it gathers is the argument for running it first. It collects every Network Operator custom resource and CRD; operator deployments, pods, ConfigMaps, secret metadata, RBAC, events and webhook configurations; DaemonSet and Deployment specs, pod details, current and previous container logs, related ConfigMaps and Services; and node details with labels, annotations, conditions, status and allocatable resources.[1] The phrase that decides your first move is previous container logs. Restart the driver DaemonSet to “try something” and you have deleted the evidence of what it did last time.

Two documented invocations cover the field cases: --skip-diagnostics --log-lines 1000 when you need it fast, and --verbose --log-lines 10000 when you are preparing a support case.[1]

A documentation note worth carrying, because a stale bookmark wastes time: the 26.7.0 troubleshooting index resolves to the SOS-Report page, and the per-symptom material — troubleshooting pod failures, quick health checks, preparing for a support case — appears as example workflows inside that page, not as separate URLs.[2][1] Guessed per-symptom slugs return 404.

SymptomBlueField-3 on a Dell Pow…SymptomBring-up: card, rshim, BF…SymptomHost does not see the Blu…CheckIs the card enumerated o…
Symptom

Host does not see the BlueField

This is the DOCA/BlueField host-side tree, not the Kubernetes one — it walks the rungs of segment 2 (is the card there, is the driver bound) and stops where Kubernetes begins. Read it for shape: every leaf names a command that produces evidence, not an opinion. Building the Kubernetes half of this ladder is what the lab asks you to do.

2The host ladder: five commands, stop at the first failure

The collector runs its diagnostics inside the OFED driver pod on each node: lsmod | grep mlx, ibstat, ibv_devinfo, mst status, uname -r and dmesg (last 200 lines), plus ip link / ip addr.[1] That list is also the manual checklist, with one caveat — run by hand on the host, the middle three need OFED and MFT userspace installed there; inside the driver pod they always work. Run them in that order and stop at the first one that fails, because every later rung assumes the earlier ones passed.

Rung Question it answers If it fails
lsmod Is the driver loaded at all? Nothing else is meaningful. Go to the DOCA-OFED driver container and its build against the running kernel
ibstat Does the host see the HCA, and is the port Active? Cabling, link, or a card that enumerated but never came up
ibv_devinfo Does the verbs layer enumerate the device? Userspace library and device permissions, not the fabric
mst status Does the management interface see the card? Firmware tooling and PCIe, not Kubernetes
dmesg What did the kernel say about firmware and PCIe? Read it before forming any theory

Only after all five pass does the problem become a Kubernetes problem — and then it is one of a short list: the device plugin never registered, the NAD or annotation or namespace is wrong, the resource-name prefix does not match, the RDMA netns mode is wrong for the deployed design, or the MTU disagrees somewhere.[1][7][10]

On the Kubernetes side there is a rung above all of them that costs one command: the Network Operator acts only on a NicClusterPolicy named exactly nic-cluster-policy, and an instance with any other name is silently ignored.[13] “We installed the operator and nothing happened” is that sentence more often than it is anything else.

3Error strings, memorised

Some of this job is genuinely recall. These strings appear verbatim in logs and each maps to exactly one cause.

Exiting.. can not change : incorrect RDMA subsystem network namespace — the RDMA shared device plugin started while the node’s RDMA subsystem is in exclusive mode; the pod rdma-shared-dp-ds sits in CrashLoopBackOff.[3] Prove it with rdma system show, which displays the netns mode.[4] This is not a bug to work around: exclusive netns is required for per-pod RDMA isolation with SR-IOV plus rdma-cni, and it breaks the shared plugin, so the two models cannot run on one node.[12][3]

EBUSY from rdma system set netns exclusive — “if there are active network namespaces and if one or more RDMA devices exist, changing mode from shared to exclusive returns error code EBUSY.”[4] The mode must be set before namespaces exist. The man page also advises against changing the subsystem mode while RDMA traffic is running, even though it is supported.[4]

spectrum-x config version <name> not found — the profile ConfigMap is missing, lacks the watch label, or the name does not match spectrumXOptimized.version character for character.[5]

TopologyAffinityError — Topology Manager under restricted or single-numa-node could not satisfy alignment, so the pod is rejected and is not automatically retried onto a better node.[6]

nicdevice condition reasonsPartiallyApplied means some parameters were unsupported for that device and the message lists them; IncorrectSpec and SpecValidationFailed mean the NicConfigurationTemplate is invalid for that device; NonVolatileConfigUpdateFailed, RuntimeConfigUpdateFailed and FirmwareError mean the apply itself failed and the next stop is the nic-configuration-daemon log for that node.[5]

rdma system set netns
deployed design
node state when you set it
0 / 8 combinations revealed
Kubernetes node — click a box to explain itpod A netnsrdma link → mlx5_0 (same device as the other pod)pod B netnsrdma link → mlx5_0 (same device as the other pod)host netnsibstat · ibv_devinfo · rdma system showrdma-shared-dp-ds (device plugin)rdma/rdma_shared_device_a × rdmaHcaMax 63kernel RDMA subsystem (ib_core)netns shared — the set was refused (EBUSY)rdma system show → netns sharedmlx5_0 — physical HCA (ens1f0)RoCEv2 = IP + UDP 4791SR-IOV VFs — mlx5_2, mlx5_3 …unused by the shared plugin
Predict before you look

exclusive mode · Shared device plugin · pods already running. Does the node come up ok, does a pod CrashLoopBackOff, or does the command itself fail?

Kernel RDMA subsystem (ib_core) — netns mode

Current: “RDMA devices are shared among network namespaces.”

One switch, node-wide, two values. `rdma system show` prints the netns mode, privileged-qkey state and monitoring support.

“If there are active network namespaces and if one or more RDMA devices exist, changing mode from shared to exclusive returns error code EBUSY.”

Persist it with `options ib_core netns_mode=0` in /etc/modprobe.d/ib_core.conf rather than setting it at runtime; the runtime path is the one that hits EBUSY.

rdma system show
rdma system set netns exclusive
rdma system set netns shared
echo "options ib_core netns_mode=0" >> /etc/modprobe.d/ib_core.conf

FAE angle: ask for this one line of output before any other question about RDMA in pods. It tells you which half of the fork the customer is standing on.

rdma-system(8) · rdma-cni

This combination is the crash-loop. Change one variable at a time and predict the outcome before revealing it — including which of the two error strings you get.

4The two checklists that catch the rest

The resource-name checklist. One name has to agree in three places: what the device plugin advertises, what the NAD references, and what the pod requests. Four different defaults collide there — intel.com is the sriov-network-device-plugin’s default resourcePrefix, which is why NVIDIA’s own configs always set "resourcePrefix": "nvidia.com" explicitly; the RDMA shared device plugin defaults to rdma, producing names like rdma/rdma_shared_device_a; and OpenShift writes openshift.io.[7][8][9] A pod requesting the wrong prefix stays Pending forever with no CNI error at all, because scheduling never gets far enough to call CNI.[7]

Who configured the device plugin?
device plugin → node allocatableintel.com/hostdevkubectl describe node | grep -A20 AllocatableNetworkAttachmentDefinitionnvidia.com/hostdevannotation k8s.v1.cni.cncf.io/resourceNamepod specnvidia.com/hostdevresources.limits / resources.requests
step 1 / 4
pod/sriov-test

Hidden until step 4. Walk the three places first, then predict.

sriov-network-device-plugin (prefix omitted)

1. The device plugin advertises a name

Somebody hand-wrote the device-plugin ConfigMap and left resourcePrefix out.

resourcePrefix defaults to intel.com. The pool is advertised as intel.com/hostdev even on a 15b3 (NVIDIA) ConnectX.

Registration happens on the hardcoded socket /var/lib/kubelet/device-plugins/kubelet.sock; the plugin then serves its own gRPC socket in the same directory and implements GetDevicePluginOptions, ListAndWatch, Allocate, GetPreferredAllocation and PreStartContainer. ListAndWatch is also how an unhealthy device is signalled, and TopologyInfo in the same response is the NUMA hint Topology Manager later needs.

What this source writes
# ConfigMap sriovdp-config — no resourcePrefix key
{ "resourceList": [{
    "resourceName": "hostdev",
    "selectors": { "vendors": ["15b3"], "isRdma": true }
}]}
Advertised: intel.com/hostdev · allocatable 8
  • resourceName is required; resourcePrefix, deviceType and selectors are optional.
  • deviceType defaults to netDevice; the other values are accelerator and auxNetDevice.
  • Selectors for netDevice: vendors, devices, drivers, pciAddresses, acpiIndexes, pfNames, rootDevices, linkTypes, ddpProfiles, isRdma, needVhostNet, vdpaType.

FAE angle. An NVIDIA card advertising an intel.com resource is not broken hardware and not a mislabelled NIC — it is the plugin default. Do not chase the vendor id.

sriov-network-device-plugin · Kubernetes device plugins · Multus how-to · netop-sosreport
Same name, two other routes

vfio-pci for VMs. A SriovNetworkNodePolicy with deviceType: vfio-pci, numVfs: 8, nicSelector.vendor "15b3", pfNames [ens1f0] and deliberately isRdma: false. The VF is passed into the guest through the VFIO userspace interface, so the guest needs mlx5_core and the host needs intel_iommu=on iommu=pt (or amd_iommu=on iommu=pt). The resource-name mechanics are identical: the SriovNetwork references the same resourceName and generates the NAD.

DRA SR-IOV driver (Tech Preview). Dynamic Resource Allocation replaces the plain name match with a ResourceClaimTemplate: deviceClassName sriovnetwork.k8snetworkplumbingwg.io plus a CEL expression device.attributes["k8s.cni.cncf.io"].resourceName == "nvidia.com/sriov_resource". Devices are published as ResourceSlice objects carrying PCIe bus ID, NUMA node and vendor. Needs the dynamicResourceAllocation: true feature gate, is vanilla Kubernetes only (not OpenShift), and is not recommended for production.

KubeVirt SR-IOV integration · DRA SR-IOV driver

The plugin advertises one name and two other places assume another. Trace which of the three is wrong before reading the verdict.

The NAD checklist. Five items, all checkable in two minutes.[10][9][12]

  1. Is the NAD in the pod’s namespace? On OpenShift, Multus is pre-configured with namespace isolation enabled by default, so a NAD in namespace A cannot be referenced from namespace B.[9] Namespace-qualify as testns1/macvlan-conf-3 or with the JSON "namespace" key.[10]
  2. Is the annotation key exactly k8s.v1.cni.cncf.io/networks?[10]
  3. Is spec.config valid JSON? A NAD may instead have no spec and be backed by a file in /etc/cni/multus/net.d/ on the node — a half-migrated cluster has both and neither works.[10]
  4. Does the NAD’s resourceName match an advertised resource, prefix included?[7]
  5. For chained RDMA, does {"type":"rdma"} come after the sriov or ib-sriov plugin in the plugins array?[12]

The MTU checklist. Three MTUs must agree: switch port, host PF, pod interface. Calico’s own rule is to “set the workload endpoint MTU and the tunnel MTUs to the same value (so all paths have the same MTU)”, subtracting 20 for IP-in-IP, 50 for VXLAN IPv4, 70 for VXLAN IPv6, 60 for WireGuard IPv4 and 80 for WireGuard IPv6.[11] A mismatch on the secondary interface produces RDMA that connects and then stalls on large messages — which reads like a fabric problem and is reported as “training is slow”.[11]

5Running the ladder under pressure

Five broken clusters, one procedure

Symptom as reported: “the RDMA pod is Pending and the customer says the fabric is fine.”

  1. Collect. kubectl netop-sosreport --skip-diagnostics --log-lines 1000. Fast form, because the customer is on the call. The archive still contains the CRs, the DaemonSet specs, the node allocatable list and previous container logs.[1]
  2. Classify: host or Kubernetes? The pod is Pending, so CNI has never been invoked and no host rung can be the cause of this symptom. Skip the five host commands for now; they matter for a different symptom shape.
  3. Look at what the node advertises. kubectl describe node <node> | grep -A20 Allocatable. The list shows rdma/rdma_shared_device_a: 63 and no nvidia.com/ entry.[8]
  4. Look at what the pod requested. kubectl get pod <pod> -o jsonpath='{.spec.containers[0].resources}'. It requests nvidia.com/hostdev: 1.
  5. Verdict in one line. The node advertises the shared RDMA plugin’s resource; the pod asks for the SR-IOV device plugin’s resource. Nobody advertises nvidia.com/hostdev, so the pod will never schedule and no CNI error will ever appear.[7]
  6. Fix, and say which one you chose. Either change the pod to request the advertised name, or add an sriovDevicePlugin section to NicClusterPolicy that advertises nvidia.com/hostdev.[8] These are different designs, not equivalent fixes — the shared plugin is a scheduling counter, SR-IOV plus rdma-cni is isolation.[12]
  7. Confirm. Re-run step 3 and see the new name in Allocatable before re-submitting the pod.

Note what never happened: nobody restarted anything, nobody looked at a switch, and the whole diagnosis was three read-only commands after the collector.

Case notes, 02:40

How it ended

The archive is on disk before anything restarts. The previous-container log for rdma-shared-dp-ds carries the error string verbatim, and rdma system show returns exclusive — the whole case in two outputs, because the node is running the design that requires exclusive netns and the shared plugin was deployed beside it.[3][4] The operator prints one more label before he goes home.

Acceptance passes on the Friday. What is left is the account meeting: a deck due, procurement asking about switches, and the customer’s architect asking “which CNI do you support?” as though it had a one-word answer.

Lab

Mutating steps ahead. Needs a maintenance window, out-of-band access (BMC/iDRAC/rshim console) and a rollback path. Record the pre-flight inventory before changing anything. Never on a production host.

On the Dell-lab BlueField-3 or ConnectX host. This lab mutates node state; every mutating step names its rollback and you should run it on a host you can reboot.

  1. Pre-flight inventory. Record all of: uname -r, lsmod | grep -E "mlx5|ib_core", ibstat, ibv_devinfo, mst status, dmesg | tail -50, rdma system show, and ip -br link. Save the output to a file with a timestamp. This is both your baseline and your rollback reference. Then capture the cluster objects you are about to edit: kubectl -n nvidia-network-operator get cm -o yaml > baseline-cm.yaml and kubectl get nicclusterpolicy nic-cluster-policy -o yaml > baseline-ncp.yaml. Host output alone is not a rollback for a step that edits a ConfigMap.
  2. Run the ladder clean. Execute lsmod, ibstat, ibv_devinfo, mst status, dmesg in that order and write one line per rung recording what “healthy” looks like on this host. Expected: five passes and a written baseline you can compare against later. If ibstat or mst status is not found on the host, run the same rungs where the collector runs them: kubectl -n nvidia-network-operator exec ds/<mofed-ds> -- <cmd>.
  3. Break one thing you can revert — option A, module unload. Pick a module whose removal you have verified is reversible on this host (check its dependency list with lsmod first). Unload it, re-run the ladder, and record which rung caught it first. Expected: lsmod fails immediately and everything below it is meaningless. Rollback: modprobe <module> and re-run the ladder until it matches the step 2 baseline.
  4. Break one thing you can revert — option B, wrong selector. Edit the device-plugin config so ifNames selects an interface that does not exist. Expected: nothing is advertised; kubectl describe node | grep -A20 Allocatable loses the resource. Rollback: kubectl apply -f baseline-cm.yaml, then kubectl -n nvidia-network-operator rollout restart ds/<device-plugin-ds>, and re-run kubectl describe node <node> | grep -A20 Allocatable until the resource reappears with the count it had in step 1.
  5. Optional, netns mode. Record the current mode with rdma system show. If and only if the node is drained, run rdma system set netns exclusive, observe the shared device plugin’s crash loop and capture the exact error string. Rollback: rdma system set netns shared; if it returns EBUSY, reboot the node — that is the documented consequence of active namespaces, not a mistake you made.
  6. Collect the archive for real. kubectl netop-sosreport --verbose --log-lines 10000. Expected: an archive with a checksum. Open the node-diagnostics section and confirm it contains the same five command outputs you ran by hand.
  7. Restore and verify. Re-run every command from step 1 and diff against the saved baseline. Expected: no differences. Do not end the session until that is true.

Retrieval check

10 questions from memory. Answer before looking anything up; misses become flashcards.

Explain it to a Dell SE

Explain to a Dell L2 engineer, in five sentences, why the first action on an RDMA-in-Kubernetes escalation is to collect an archive rather than to fix anything.

14 flashcards for this lesson — 0 in deck. Spaced review lives at /review.

Sources

Facts in this lesson were checked against NVIDIA Network Operator v26.7.0 SOS-Report page and troubleshooting index re-fetched 2026-09-07; Spectrum-X verify-and-troubleshoot re-fetched 2026-09-07; rdma-system(8) and Kubernetes Topology Manager docs as cited. Dates are when each page was fetched.

  1. NVIDIA Network Operator v26.7.0 — SOS-Report Collection Script · fetched 2026-09-07
  2. NVIDIA Network Operator v26.7.0 — Troubleshooting (index) · fetched 2026-09-07
  3. Issue #87 — rdma-shared-dp-ds crash-loops under exclusive netns mode · fetched 2026-09-07
  4. rdma-system(8) — Linux manual page · fetched 2026-09-07
  5. NVIDIA Network Operator v26.7.0 — Spectrum-X Verify and Troubleshoot · fetched 2026-09-07
  6. Kubernetes — Control Topology Management Policies on a node · fetched 2026-09-07
  7. GitHub — k8snetworkplumbingwg/sriov-network-device-plugin · fetched 2026-09-07
  8. NVIDIA Network Operator v26.7.0 — Deployment Guide with Kubernetes · fetched 2026-09-07
  9. NVIDIA Network Operator v26.7.0 — Deployment Guide with OpenShift · fetched 2026-09-07
  10. Multus CNI — How to use · fetched 2026-09-07
  11. Calico — Configure MTU to maximize network performance · fetched 2026-09-07
  12. GitHub — k8snetworkplumbingwg/rdma-cni · fetched 2026-09-07
  13. GitHub — Mellanox/network-operator (NVIDIA Network Operator) · fetched 2026-09-07

The same idea elsewhere

Other lessons that cover this ground, sometimes from another course's angle.