Skip to content

fix(plugin): honor OvnPort/MAC from CNI args.cni in StdinData - #542

Merged
kubevirt-prow[bot] merged 2 commits into
k8snetworkplumbingwg:mainfrom
fivetime:fix/multus-cni-args-ovn-port
Jul 30, 2026
Merged

fix(plugin): honor OvnPort/MAC from CNI args.cni in StdinData#542
kubevirt-prow[bot] merged 2 commits into
k8snetworkplumbingwg:mainfrom
fivetime:fix/multus-cni-args-ovn-port

Conversation

@fivetime

@fivetime fivetime commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Fixes a silent integration bug between ovs-cni and multus-cni that prevents the ovnPort feature from working when invoked through the standard multus annotation path.

Background. ovs-cni's plugin reads OvnPort (and MAC) only from the CNI_ARGS environment variable via cnitypes.LoadArgs(args.Args, &EnvArgs{}). multus-cni, however, takes the cni-args map from a pod's k8s.v1.cni.cncf.io/networks[].cni-args annotation and injects it into the delegate plugin's StdinData under args.cni.<Key>, not via CNI_ARGS. As a result:

  • the veth is added to the bridge,
  • external_ids:iface-id is never set,
  • ovn-controller never binds the OVN logical switch port to the chassis,
  • the pod has no L2/L3 connectivity through OVN.

There is no warning anywhere in the stack: multus passes its own IgnoreUnknown=1 to delegates, so even if a user tries the lower-case cni-args.ovnPort key the unknown-args check is suppressed and the request appears to succeed.

Fix. Add an Args field to NetConf capturing the CNI 0.4.0+ args.cni map from StdinData, and have the veth/sriov/vdpa CmdAdd / CmdDel / CmdCheck paths fall back to it (via a new common.ApplyConfArgsFallback helper) when CNI_ARGS did not supply OvnPort or MAC. CNI_ARGS keeps priority, so callers that already pass values via env (e.g. kubevirt virt-launcher) are unaffected.

This PR supersedes #529. That PR was filed before the vhost_vdpa refactor (#427) split the plugin into per-backend packages; this rebased version applies the same fix in all three backends and addresses the automated review feedback on #529:

  • PluginArgs.Cni is map[string]json.RawMessage instead of map[string]string, so an unrelated array/object entry under args.cni (for example the IPAM-style ips array) does not break NetConf unmarshal — values are decoded lazily only for the keys the fallback consumes.
  • Key matching is case-insensitive — pod annotations written as ovnPort, OvnPort, or ovnport all resolve.
  • The fallback helper accepts nil pointers for either output, so CmdDel / CmdCheck (which only care about ovnPort) need no dummy variables.

Special notes for your reviewer:

End-to-end verified on a real Neutron + OVN cluster (multus-cni 4.2.4, OVN 24.x, Open vSwitch 3.x):

  1. Created Neutron ports on a geneve tenant network (provider:network_type=geneve) and a flat provider network (provider:network_type=flat, mapped to br-ex via OVN bridge_mappings).
  2. Two pods attached to each network using the standard annotation:
    k8s.v1.cni.cncf.io/networks: |
      [{"name":"ovn-tenant","mac":"fa:16:3e:...","ips":["192.168.1.8/24"],
        "cni-args":{"OvnPort":"<neutron-port-uuid>"}}]
  3. Before this patch (v0.39.0): veths land on br-int with empty external_ids, OVN SB port_binding.chassis=[], ping 100 % loss. The fix only worked when an operator manually ran ovs-vsctl set interface <veth> external_ids:iface-id=<uuid>.
  4. After this patch: external_ids:iface-id and ovn-installed=true show up automatically, OVN SB binds both LSPs to the local chassis (up=true), and ping between pods works for both overlay (geneve) and underlay (flat localnet) — 0 % loss, RTT 0.1–7 ms.

The change is intentionally minimal: a nil-guarded helper plus nine one-line call sites (three backends x ADD/DEL/CHECK). CNI_ARGS still wins when both paths are present, so downstream consumers like kubevirt are not affected.

Release note:

Fix `ovnPort` / `MAC` not being honored when passed via the multus-cni
`cni-args` pod annotation. ovs-cni now also reads these values from
`args.cni.<Key>` in the CNI conf StdinData, in addition to the existing
`CNI_ARGS` env var path. Matching is case-insensitive; CNI_ARGS retains
priority.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for CNI 0.4.0+ per-invocation arguments, enabling automatic fallback for network parameters like mac and ovnPort when not provided via environment/config.
  • Improvements

    • Improved consistency of mac/ovnPort initialization across veth, SR-IOV, and vDPA workflows, including DEL/CHECK paths.
    • Updated documentation to describe how to supply mac and ovnPort via cni-args/CNI_ARGS, including case-insensitive matching and handling of unrecognized/invalid entries.

multus-cni propagates per-pod cni-args from the
`k8s.v1.cni.cncf.io/networks[].cni-args` pod annotation by injecting
them into the delegate plugin's StdinData under `args.cni.<Key>`,
NOT via the CNI_ARGS environment variable. ovs-cni reads OvnPort and
MAC only from CNI_ARGS via cnitypes.LoadArgs, so values set through
the standard multus annotation path silently never reach the plugin:
the veth is added to the bridge but external_ids:iface-id is never
set, ovn-controller never binds the LSP to a chassis, and the pod has
no L2/L3 connectivity through OVN.

Add an Args field to NetConf that captures the CNI 0.4.0+ `args.cni`
map from StdinData, and have the veth/sriov/vdpa CmdAdd/CmdDel/CmdCheck
paths fall back to it (via a new common.ApplyConfArgsFallback helper)
when CNI_ARGS did not supply OvnPort or MAC. CNI_ARGS keeps priority,
so callers that already pass values via env (e.g. kubevirt
virt-launcher) are unaffected.

Design choices reflect review feedback on the original patch:

* PluginArgs.Cni uses map[string]json.RawMessage rather than
  map[string]string, so an unrelated array/object entry under
  args.cni (for example the IPAM-style `ips` array) does not break
  NetConf unmarshal — values are decoded lazily only for the keys
  the fallback consumes.
* Key matching is case-insensitive: pod annotations written as
  `ovnPort`, `OvnPort`, or `ovnport` all resolve.
* The helper accepts nil pointers for either output, so CmdDel and
  CmdCheck (which only care about ovnPort) need no dummy variables.

End-to-end verified on a Neutron+OVN cluster with multus-cni 4.2.4:
created Neutron ports on a geneve tenant network and a flat provider
network, attached pods via the `cni-args.OvnPort` annotation,
confirmed external_ids:iface-id is set automatically, ovn-controller
binds both LSPs (chassis populated, up=true, ovn-installed=true), and
L2 ping between pods on the same network works for both overlay and
underlay.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Simon Zhou <jp.zdm2008@gmail.com>
@kubevirt-bot kubevirt-bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. labels Jun 15, 2026
@kubevirt-bot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: fivetime
Once this PR has been reviewed and has the lgtm label, please assign alonakaplan 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

@kubevirt-bot kubevirt-bot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds typed CNI args.cni passthrough, implements fallback resolution for empty mac and ovnPort values, integrates it into sriov, vdpa, and veth commands, and documents per-pod argument configuration.

Changes

CNI Args Fallback

Layer / File(s) Summary
Data contract and fallback implementation
pkg/types/types.go, pkg/common/common.go
NetConf gains Args *PluginArgs; PluginArgs preserves args.cni values as json.RawMessage. ApplyConfArgsFallback uses case-insensitive matching and tolerant JSON string decoding to populate empty mac and ovnPort pointers.
Plugin command integration
pkg/sriov/plugin.go, pkg/vdpa/plugin.go, pkg/veth/plugin.go
CmdAdd applies the fallback for mac and ovnPort; CmdDel and CmdCheck apply it for ovnPort after environment extraction.
Per-pod argument documentation
README.md, docs/cni-plugin.md
Documents mac and ovnPort through CNI_ARGS and Multus cni-args, including case-insensitive matching and ignored invalid or unknown entries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Plugin as Plugin command
  participant Fallback as ApplyConfArgsFallback
  participant Args as NetConf.Args.Cni
  participant Decoder as decodeArgString

  Plugin->>Fallback: Resolve empty mac or ovnPort
  Fallback->>Args: Match CNI argument key
  Args-->>Fallback: Return json.RawMessage
  Fallback->>Decoder: Decode JSON string
  Decoder-->>Fallback: Return value or invalid result
  Fallback-->>Plugin: Populate resolved pointer
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: honoring ovnPort/MAC from args.cni in StdinData.
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.
✨ 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.

@kubevirt-bot

Copy link
Copy Markdown

Hi @fivetime. Thanks for your PR.

I'm waiting for a k8snetworkplumbingwg member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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.

@SchSeba SchSeba left a comment

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.

LGTM can you please extend the readme documentation to explain this new feature.

also another point that I am not sure here is if we want to have the strings.ToLower or be specific on the parameter name

@SchSeba

SchSeba commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

/ok-to-test

@kubevirt-prow kubevirt-prow Bot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jul 20, 2026
Explain, in docs/cni-plugin.md and the README, how the per-invocation
mac and ovnPort parameters are supplied — either via CNI_ARGS or, under
Multus, via a network's cni-args (delivered as args.cni in StdinData) —
and note that key matching is case-insensitive.

Addresses review feedback on k8snetworkplumbingwg#542.

Signed-off-by: Simon Zhou <jp.zdm2008@gmail.com>
@fivetime

Copy link
Copy Markdown
Contributor Author

@SchSeba thanks for the review!

I've pushed a follow-up commit (c2afce3) adding documentation for this feature — a new Per-Pod Arguments (mac / ovnPort) section in docs/cni-plugin.md (covering both the CNI_ARGS and the Multus cni-argsargs.cni delivery paths, with a pod-annotation example), plus a pointer to it from the README.

On strings.ToLower / case-insensitive matching — I'd lean toward keeping it, for two reasons:

  1. It targets the exact failure mode this PR fixes. The reason OvnPort didn't reach the plugin before was a silent mismatch — nothing errors out, the port just isn't wired. Case sensitivity is the same footgun one level up: a user writing ovnPort instead of OvnPort in their pod annotation gets no error and no effect, just a broken pod to debug. Being lenient here is consistent with the intent of the fix.
  2. This path is human-authored, unlike CNI_ARGS. I agree the asymmetry with the CNI_ARGS/EnvArgs path is worth calling out — but I think it actually justifies the difference: CNI_ARGS keys are produced by the runtime from fixed struct field names, whereas args.cni keys come straight from a hand-written k8s.v1.cni.cncf.io/networks[].cni-args annotation. Leniency is most valuable precisely on the path people type by hand.

On determinism: the loop takes the first non-empty match and returns, and a single annotation realistically won't carry two differently-cased spellings of the same key — but if you'd prefer, I'm happy to add an explicit guard (or restrict to a small documented canonical set: mac, ovnPort) so behavior is well-defined even in that edge case.

If you'd still rather have strict exact-match, that's an easy change — just let me know and I'll switch it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@docs/cni-plugin.md`:
- Around line 65-74: Update the earlier bridge description to state that ovnPort
may be supplied through either CNI_ARGS or Multus args.cni, and that bridge can
be omitted in both supported per-invocation paths. Keep the existing bridge and
ovnPort behavior descriptions unchanged otherwise.
- Around line 132-138: Update the CNI_ARGS documentation example so it is valid
copyable shell syntax: show CNI_ARGS assigned as part of an actual ovs-cni
invocation, or export it and then provide a commented invocation instruction; do
not leave the placeholder “...” as the command to execute.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b9bb36f-7960-4f50-be4b-50ecce6df93b

📥 Commits

Reviewing files that changed from the base of the PR and between e83f9f0 and c2afce3.

📒 Files selected for processing (2)
  • README.md
  • docs/cni-plugin.md

Comment thread docs/cni-plugin.md
Comment on lines +65 to +74
The following are *per-invocation* arguments rather than static network
configuration. They are not set in the `NetworkAttachmentDefinition` `config`
body, but passed per pod through CNI args (see
[Per-Pod Arguments](#per-pod-arguments-mac--ovnport)):

* `mac` (string, optional): MAC address to assign to the container interface.
* `ovnPort` (string, optional): value written to the OVS interface's
`external_ids:iface-id`, used to bind the port to an OVN logical switch port.
When set, `bridge` may be omitted (it is derived from the port).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Update the earlier bridge omission reference to include Multus cni-args.

The existing bridge description at Line 53 still says ovnPort must be set in CNI_ARGS, although this PR also supports args.cni from Multus. Update that reference so users do not incorrectly configure bridge when supplying ovnPort through the documented Multus path.

🤖 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 `@docs/cni-plugin.md` around lines 65 - 74, Update the earlier bridge
description to state that ovnPort may be supplied through either CNI_ARGS or
Multus args.cni, and that bridge can be omitted in both supported per-invocation
paths. Keep the existing bridge and ovnPort behavior descriptions unchanged
otherwise.

Comment thread docs/cni-plugin.md
Comment on lines +132 to +138
### 1. `CNI_ARGS` environment variable

When invoking the plugin directly, pass them as `CNI_ARGS`:

```shell
CNI_ARGS="mac=0a:58:0a:f4:00:07;ovnPort=my-logical-port" ...
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the CNI_ARGS example valid shell syntax.

CNI_ARGS="..." ... attempts to execute ... as a command when copied. Show an actual invocation or use export CNI_ARGS=... followed by a comment instructing the reader to invoke ovs-cni.

🤖 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 `@docs/cni-plugin.md` around lines 132 - 138, Update the CNI_ARGS documentation
example so it is valid copyable shell syntax: show CNI_ARGS assigned as part of
an actual ovs-cni invocation, or export it and then provide a commented
invocation instruction; do not leave the placeholder “...” as the command to
execute.

fivetime added a commit to fivetime/ovs-cni that referenced this pull request Jul 23, 2026
Explain, in docs/cni-plugin.md and the README, how the per-invocation
mac and ovnPort parameters are supplied — either via CNI_ARGS or, under
Multus, via a network's cni-args (delivered as args.cni in StdinData) —
and note that key matching is case-insensitive.

Addresses review feedback on k8snetworkplumbingwg#542.

Signed-off-by: Simon Zhou <jp.zdm2008@gmail.com>

@SchSeba SchSeba left a comment

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.

/lgtm
/approve

@kubevirt-prow kubevirt-prow Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 30, 2026
@kubevirt-prow

kubevirt-prow Bot commented Jul 30, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: fivetime, SchSeba

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

The pull request process is described 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

@kubevirt-prow kubevirt-prow Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 30, 2026
@kubevirt-prow
kubevirt-prow Bot merged commit 4229115 into k8snetworkplumbingwg:main Jul 30, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. lgtm Indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note Denotes a PR that will be considered when it comes time to generate release notes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants