Open Virtual Network Reconnaissance (OVN Recon) is an OpenShift Console Plugin that provides a visualization of the Virtual and Node Network State in an OpenShift cluster.
Blog post: OVN Recon: Making OpenShift Networking Connections
- Physical topology view: Per-node graph of host interfaces, bridges, OVN bridge mappings, VRFs, CUDNs/UDNs, attachments, and NAD relationships.
- LLDP-aware interface context: Optional LLDP neighbors column in the node visualization, shown via toggle when LLDP is enabled and neighbor data is present.
- Logical topology views: Cluster-scoped (
/ovn-recon/ovn) and node-scoped (/ovn-recon/ovn/:name) ladder views of the logical OVN constructs — switches, routers, NAT, and routes per network — backed by the optional collector service. See readme-logical-visualization.md. - Operator-managed lifecycle: Recommended installation via OVN Recon Operator with automatic console plugin enablement and collector wiring.
OVN Recon visualizes the following Kubernetes Custom Resources:
- NodeNetworkState (NNS) - Represents the current network configuration of a node, including interfaces, bridges, and OVN bridge mappings. Provided by the nmstate operator.
- NodeNetworkConfigurationPolicy (NNCP) - Defines desired network configuration for nodes. Used to configure OVN bridge mappings and physical network interfaces.
- ClusterUserDefinedNetwork (CUDN) - Defines overlay networks that can be attached to pods. Part of OpenShift's OVN-Kubernetes secondary networks.
- UserDefinedNetwork (UDN) - Namespace-scoped network definitions used by secondary network workflows.
- NetworkAttachmentDefinition (NAD) - Multus CNI resource that references a CUDN and allows pods to attach to secondary networks.
- RouteAdvertisements - Cluster-scoped route advertisement policy objects used for VRF-to-network relationships.
- OvnRecon - Operator custom resource that controls plugin/collector deployment and logical topology feature gating.
The plugin watches these resources in real-time and renders interactive physical/logical topology views to show how interfaces, bridges, networks, routes, and attachments are connected.
- Overview:
/ovn-recon(the single Networking → OVN Recon menu entry): collector status, network counts, and a node table with per-node facts and links to every view./ovn-recon/node-network-statestill lands here so older bookmarks keep working. - Physical view:
/ovn-recon/node-network-state/:name - Cluster logical OVN view:
/ovn-recon/ovn(shown when the collector feature gate is enabled) - Node logical OVN view:
/ovn-recon/ovn/:name(shown when the collector feature gate is enabled)
In the physical node graph:
- LLDP neighbors are rendered in a dedicated optional column left of physical interfaces.
- The
Show LLDP neighborstoggle appears only when LLDP is enabled on at least one interface and LLDP neighbor payloads are present.
Warning
Experimental / No warranty. This project is experimental, community-maintained, and is not endorsed or supported by Red Hat. It is provided "as is" with no warranty. Installing or using it may affect your cluster; you assume all risk. Test in a non-production environment first. The authors are not responsible for any damage, downtime, or data loss.
- Click the ➕ icon at the top right of the OpenShift Console
- Select "Import YAML"
- Paste in this YAML: https://raw.githubusercontent.com/dlbewley/ovn-recon/refs/heads/main/manifests/catalogsource.yaml
- After a few moments navigate to "Ecosystem -> Software Catalog" (or Operator Hub in older versions)
- Search for "OVN Recon Operator"
- Click install and accept the defaults
- After a few moments click the tile to create a "ovnrecon" resource and accept the defaults
- After a few more moments a new "OVN Recon" menu will show up within the Networking menu.
Create the Bewley Operators catalog source
oc apply -f manifests/catalogsource.yamlCreate the operator subscription.
oc apply -k manifests/operator/baseCreate the ovnrecon resource instance to enable console plugin and deploy OVN Recon application.
oc apply -k manifests/instance/baseConsole plugin will be automatically enabled.
For detailed operator deployment instructions, please see docs/OLM-BUNDLE-GUIDE.md.
The collector — and with it the logical OVN topology views — is enabled by
default: an OvnRecon resource with no spec.collector.enabled value
deploys the collector automatically. To opt out, disable it explicitly:
oc patch ovnrecon ovn-recon --type=merge -p '{
"spec": {
"collector": { "enabled": false }
}
}'You can also set collector image fields (spec.collector.image.repository|tag|pullPolicy) as needed. See OPERATOR.md for full API details.
The OvnRecon spec stays as sparse as you wrote it: the CRD declares no defaults, the
operator applies them at reconcile time, and the values it actually resolved (images,
enabled flags, cache backing) are published under status.effective:
oc get ovnrecon ovn-recon -o jsonpath='{.status.effective}' | jqThe collector caches each node's OVN snapshot on disk. Requests are served from cache immediately — a stale entry is returned at once while a background refresh revalidates it — and a startup sweep warms any missing entries, so page loads never block on live collection. Refresh is demand-driven: an idle collector runs no probes. When live probing fails, cached data keeps serving in preference to fixtures.
Caching is on by default, and by default it is PVC-backed: the operator
creates and owns a claim (managed mode) so cached snapshots survive pod
restarts — which is exactly when stale-serve matters, such as a restart
during an OVN outage. If the cluster cannot provision the claim (no default
StorageClass, or the claim stays unbound), the collector falls back to
EmptyDir automatically and the operator records a Warning event on the
OvnRecon resource (oc describe ovnrecon shows it). Everything is
configurable under spec.collector.cache:
spec:
collector:
cache:
enabled: true # default
ttlSeconds: 120 # freshness window; minimum 30
storage:
mode: auto # default; or EmptyDir | PVC
managed: true # default; operator creates and owns the claim
# claimName: collector-cache # bring your own claim
# size: 1Gi # managed claim size
# storageClassName: "" # managed claim class; empty = cluster defaultmode: auto(default) uses a PVC — the managed claim, orclaimName— and falls back to EmptyDir with a Warning event when the claim cannot be provisioned. The fallback reverses on its own once the claim binds.mode: EmptyDirnever creates or mounts a PVC, even withmanaged: true; the cache re-warms after each pod restart. An existing managed claim is kept (dormant) so switching back keeps its contents.mode: PVCrequires persistent storage and never falls back — provisioning problems are surfaced instead of papered over.- Managed claims (
managed: true, the default) are created and owned by the operator: default name<collector>-cache, RWO,sizedefault1Gi(generous only because some provisioners enforce minimums; the cache itself needs a few MiB). The claim is garbage-collected with theOvnReconresource and removed if you setmanaged: false; it survives a collector disable so the cache stays warm across toggles. - To use a pre-created claim instead, set
managed: falsewithclaimName. With a non-RWX claim (managed claims included) the operator switches the collector to aRecreaterollout (a brief gap per upgrade, instead of risking a volume-attach deadlock); a pre-created RWX claim keeps zero-gap rolling updates.
Create the namespace, consoleplugin, service, and deployment from manifests/manual/base without an operator.
oc apply -k manifests/manual/baseNote
When installing via the Operator, this step is not necessary.
Patch the Console Operator config to enable the plugin. Use a JSON patch to append to the list of plugins instead of replacing it:
oc patch console.operator.openshift.io cluster --type=json \
--patch '[{"op": "add", "path": "/spec/plugins/-", "value": "ovn-recon"}]'The OpenShift console will reload to apply the changes. You should see a notification that the console has been updated.
For troubleshooting steps, please see docs/TROUBLESHOOTING.md.
For detailed build and developer deployment instructions, please see docs/BUILDING.md.



