VMI Network Interface Status#
vmi.Status.Interfaces is the authoritative per-interface data structure, holding IP addresses, MAC address, queue count, and link state for each network interface in a Virtual Machine Instance. It is populated on every reconciliation by NetStat.UpdateStatus() in pkg/network/setup/netstat.go.
UpdateStatus() Reconciliation Order#
UpdateStatus() merges four data sources in strict priority order:
- Domain spec β seeds each entry with MAC address, queue count, and link state from the libvirt domain XML
- Pod interface cache β overlays IPv4/IPv6 addresses written during network setup via
discover() - QEMU Guest Agent β enriches or overrides IPs with guest-OS-reported data; uses MAC-based matching
- Multus status β appends secondary interface names from Multus pod annotations
The primary (pod-network) interface is always placed at index 0 in vmi.Status.Interfaces to satisfy the VMI CRD wide-column definition .
Pod Interface Cache#
updateIfacesStatusFromPodCache() iterates the VMI interface spec and, for each entry, reads PodIfaceCacheData{PodIP, PodIPs} from an in-memory sync.Map (volatile cache), falling back to disk via cache.ReadPodInterfaceCache. Once an entry is loaded into the volatile cache it is never re-read from disk, making reads cheap for steady-state reconciliation. Absent/hotplugged-out interfaces are evicted from the volatile cache at the end of each UpdateStatus() call .
Guest Agent Interface Matching (MAC-based)#
ifacesStatusFromGuestAgent() iterates guest-agent-reported interfaces and matches each one to an existing status entry by MAC address via LookupInterfaceStatusByMac . Two outcomes:
- MAC match found β
updateVMIIfaceStatusWithGuestAgentData()enriches the existing entry and setsInfoSource = domain+ga. - No match β a new entry is appended with
InfoSource = guestAgent.
Guest-only interface limit: To prevent excessive internal guest interfaces (veth pairs, bridges, etc.) from filling the etcd database and blocking further VMI edits, guest-only (no-MAC-match) interfaces are capped at 10 . Interfaces defined in the VMI spec are never subject to this limit.
Family-Based IP Merging#
updateVMIIfaceStatusWithGuestAgentData() applies a per-address-family merge strategy rather than a simple replacement:
- IPs are split into IPv4 and IPv6 buckets via
splitIPByFamiliy(). - For each family independently: if the existing status has IPs and the guest agent also reports IPs, the existing (pod-cache) data wins. Guest-agent data only overrides when the existing bucket is empty, or when the guest agent reports no IPs for that family (which clears the bucket, signaling the interface is unreachable) .
- The final
IPfield is set toIPs[0]β IPv4 entries come before IPv6 in the merged slice .
Masquerade: Link-Local Address Filtering#
When an interface uses masquerade binding, the guest OS (via QEMU guest agent) may report IPv6 Link-Local Addresses (LLAs, fe80::/10). These addresses are host-internal and unreachable through the pod network's NAT rules. filterOutLinkLocalAddresses() strips them before the merge step whenever vmiIfaceSpec.Masquerade != nil .
InfoSource Tracking#
Each status entry carries an InfoSource field that records which data sources contributed to it:
| Value | Meaning |
|---|---|
domain | Data from libvirt domain spec only |
domain+ga | Domain spec + guest agent |
guestAgent | Guest agent only (no VMI spec match) |
multus | Multus pod annotation contributed |
The InfoSource field is used by virt-controller to decide what to preserve across reconciliation cycles .
Key Entry Points & Source Files#
| File | Purpose |
|---|---|
pkg/network/setup/netstat.go | Core reconciliation logic β NetStat, UpdateStatus(), all merge helpers |
pkg/network/cache | PodIfaceCacheData struct and disk cache read/write |
pkg/network/vmispec | Index/lookup helpers: LookupInterfaceStatusByMac, IndexInterfaceSpecByName, InfoSource* constants |
Related PRs: