OpenvSwitch Operator Application¶
The OpenvSwitch Operator application (openvswitch) deploys and manages
OVS instances on StarlingX worker nodes using a Kubernetes-native
operator pattern. It provides declarative configuration of OVS bridges, ports,
and per-node settings through CRDs.
The application consists of two main components:
- OVS Manager Operator
A Deployment that runs on control-plane nodes. It watches for OVS custom resources (
OVSBridge,OVSPort,OVSNodeConfig) and reconciles the desired state with the actual OVS configuration on each node via the OVS Agent.- OVS Agent Operator
A DaemonSet that runs on every node labeled with
ovs-node=enabled. Each pod contains three containers:ovs-agent-operator— the controller that receives instructions from the OVS Manager and configures the local OVS instance accordingly.ovsdb-server— the OVS database server managing the switch configuration database.ovs-vswitchd— the OVS forwarding daemon that implements the configured bridges and ports.
An init container (
ovs-init) bootstraps the OVS database on pod startup.
The application supports three CRD types:
OVSBridgeDefines a bridge with optional STP, MAC aging, and node selector. Targets all nodes matching a label selector.
OVSPortDefines a port on a named bridge with VLAN configuration, STP settings, and optional BFD-enabled interfaces.
OVSNodeConfigCombines bridge and port definitions for a specific node, allowing per-node customization of the OVS topology (generated by OVS Manager, not editable by the user).
The provided application uses kernel bridging underneath; therefore, all SR-IOV VF configurations must use netdevice drivers. The use of VFIO (DPDK) is not supported.
Install OpenvSwitch Operator Application¶
Before installing the application, ensure the target worker nodes are labeled
with the ovs-node=enabled label. The OVS Agent DaemonSet uses this label as
a node selector to determine where to schedule pods.
Label the target nodes:
~(keystone_admin)]$ system host-label-assign <hostname> ovs-node=enabled
Upload the application:
~(keystone_admin)]$ system application-upload openvswitch
Apply the application:
~(keystone_admin)]$ system application-apply openvswitch
Note
The ovs-node=enabled label must be assigned to at least one host before
applying the application. If no hosts have this label, the OVS Agent
DaemonSet will not schedule any pods.
Set up Helm Override Variables¶
The openvSwitch application supports Helm overrides for both the
ovs-agent and ovs-manager charts. Overrides are set using the
system helm-override-update command.
~(keystone_admin)]$ system helm-override-update openvswitch ovs-agent openvswitch \
--set <key>=<value>
The following table describes the available override variables for the
ovs-agent chart:
Parameter |
Default |
Description |
|---|---|---|
|
|
Comma-separated list of network-attachment-definition (NAD) JSON objects
to attach additional interfaces to the OVS pod. Each entry must have a
|
|
cpu: 10m, memory: 64Mi |
Resource requests for the OVS agent container. When using SR-IOV
interfaces, include the corresponding resource names (e.g.,
|
|
cpu: 500m, memory: 128Mi |
Resource limits for the OVS agent container. Must include matching SR-IOV resource names when using SR-IOV interfaces. |
|
|
Host port for the health probe endpoint (accessible at
|
|
|
Host port for the OVS status endpoint (accessible at
|
|
|
Enables a host-access network-attachment-definition that provides the OVS pod connectivity to the host network via a Linux bridge. |
|
|
The sysinv interface name of the SR-IOV PF to use for bridge failover
(e.g., |
|
|
Log level for the ovsdb-server container. Valid values: |
|
|
Log level for the ovs-vswitchd container. Valid values: |
Example: Attaching SR-IOV interfaces
~(keystone_admin)]$ system helm-override-update openvswitch ovs-agent openvswitch \
--set ovsAgent.networks='{"name":"ovs-bridge1"\,"interface":"port1"}\,{"name":"ovs-bridge2"\,"interface":"port2"}' \
--set ovsAgent.resources.requests.intel\\.com/pci_sriov_net_ovs_bridge1='1' \
--set ovsAgent.resources.limits.intel\\.com/pci_sriov_net_ovs_bridge1='1' \
--set ovsAgent.resources.requests.intel\\.com/pci_sriov_net_ovs_bridge2='1' \
--set ovsAgent.resources.limits.intel\\.com/pci_sriov_net_ovs_bridge2='1'
Alternatively, use a YAML file for complex overrides:
~(keystone_admin)]$ system helm-override-update openvswitch ovs-agent openvswitch \
--values /path/to/overrides.yaml
Where overrides.yaml contains:
ovsAgent:
networks: '{"name":"ovs-bridge1","interface":"port1"},
{"name":"ovs-bridge2","interface":"port2"}'
resources:
requests:
intel.com/pci_sriov_net_ovs_bridge1: '1'
intel.com/pci_sriov_net_ovs_bridge2: '1'
limits:
intel.com/pci_sriov_net_ovs_bridge1: '1'
intel.com/pci_sriov_net_ovs_bridge2: '1'
All the referenced NetworkAttachmentDefinitions need to be created previously to applying to the system. After updating overrides, re-apply the application:
~(keystone_admin)]$ system application-apply openvswitch
Uninstall OpenvSwitch Operator Application¶
Follow the steps below to uninstall the OpenvSwitch Operator application:
Remove the application:
~(keystone_admin)]$ system application-remove openvswitch
Delete the application:
~(keystone_admin)]$ system application-delete openvswitch
Note
During removal, the application lifecycle hook automatically cleans up OVS webhook configurations and removes finalizers from OVS custom resources to ensure that resource deletion is not blocked.
Connect Internal Traffic to the OpenvSwitch Pod¶
The OVS pod runs in its own network namespace and, by default, is isolated from both the host platform network and other pods. Two mechanisms are available to connect traffic into and out of the OVS bridges running inside the pod: host platform access and pod-to-pod access via bridge CNI.
Host Platform Access via OpenvSwitch¶
Host platform access allows the host operating system to communicate directly
with the OVS bridge inside the ovs-agent pod. This is achieved through a
host-access network-attachment-definition that creates a Linux bridge (ovs0)
shared between the host and the pod via a veth pair.
When ovsAgent.hostAccess.enabled is set to true, the application:
Creates a
host-accessNetworkAttachmentDefinition in theopenvswitchnamespace using a bridge CNI plugin with bridge nameovs0.Attaches a
host0interface to the OVS pod connected to theovs0Linux bridge on the host.Activates bridge failover: a script manages the SR-IOV PF interface membership in the Linux bridge to prevent duplicate packets when the OVS pod veth is active.
The systemd service ovs-access-monitor starts to keep track of the ovs-agent pod health and configuration status, in case of failure it switches the
ovs0bridge membership connection from the veth device (created by the bridge-cni) to the selected pfInterface.The configuration status will be decided by two factors:
inside the virtual-switch, the host0’s ovsport needs to be attached to an ovsbridge.
This ovsbridge needs to have at least one extra ovsport configured, so it can send traffic to the outside interface (but it cannot identify if the selected ovsport is the correct one).
Prerequisites
The host must have an SR-IOV PF interface configured in sysinv with class
pci-sriovand typeethernet.An upper ethernet interface with
ovs_access=truemust be configured on top of the SR-IOV PF interface.The node must be labeled with
ovs-node=enabled.
Configuration steps
Configure the SR-IOV PF interface and the ovs-access upper interface on each target host using the StarlingX system commands.
~(keystone_admin)]$ system host-if-modify controller-0 eno8603 -c pci-sriov \ -n sriov0 -N 16 --vf-driver netdevice ~(keystone_admin)]$ system host-if-add controller-0 base0 ethernet sriov0 \ -c platform --ovs-access true
Set the Helm overrides to enable host access:
~(keystone_admin)]$ system helm-override-update openvswitch ovs-agent openvswitch \ --set ovsAgent.hostAccess.enabled=true \ --set ovsAgent.hostAccess.bridgeFailover.pfInterface=<sysinv_interface_name>
Replace
<sysinv_interface_name>with the name of the SR-IOV PF interface as configured in sysinv (e.g.,sriov0).Select a pci-sriov interface that will participate in a datanetwork to be used as one of the virtual-switch ports.
There is no need to add the host access’ bridge-cni to
ovsAgent.networks, this will be done automatically.In the example below, both the host access interface base0, and the sriov-vf interface pf1 are using sriov0 (the NetworkAttachmentDefinition
pf1nadneeds to be created prior to the application apply):~(keystone_admin)]$ system host-if-modify controller-0 eno8603 -c pci-sriov \ -n sriov0 -N 16 --vf-driver netdevice ~(keystone_admin)]$ system host-if-add controller-0 base0 ethernet sriov0 \ -c platform --ovs-access true ~(keystone_admin)]$ system datanetwork-add pf1dn vlan ~(keystone_admin)]$ system host-if-add controller-0 pf1 vf sriov0 -c pci-sriov \ -N 1 --vf-driver=netdevice ~(keystone_admin)]$ system interface-datanetwork-assign controller-0 pf1 pf1dn
Add the override values to match the provided configuration (note that the host access NetworkAttachmentDefinition does not need to be added to the
ovsAgent.networksfield, this will be done automatically, only the external virtual-switch ports are required).ovsAgent: hostAccess: enabled: true bridgeFailover: pfInterface: sriov0 networks: '{"name":"pf1nad","interface":"port1"}' resources: requests: intel.com/pci_sriov_net_pf1dn: '1' limits: intel.com/pci_sriov_net_pf1dn: '1'
Then update the Helm override.
~(keystone_admin)]$ system helm-override-update openvswitch ovs-agent openvswitch \ --values /path/to/overrides.yamlApply the application.
~(keystone_admin)]$ system application-apply openvswitch
Once the OVS pod is running, configure a port on the OVS bridge to connect to the
host0interface by creating anOVSNodeConfigorOVSPortcustom resource. For example:apiVersion: openvswitch.starlingx.io/v1 kind: OVSBridge metadata: name: br0 namespace: openvswitch spec: bridgeName: br0 rstpEnable: true macAgingTime: 300 macTableSize: 2048 --- apiVersion: openvswitch.starlingx.io/v1 kind: OVSPort metadata: name: host-access-port namespace: openvswitch spec: name: br0-host0 bridgeName: br0 vlanMode: access vlanTag: 705 stpEnable: false interfaces: - name: host0 # interface is available when ovsAgent.hostAccess.enabled=true --- apiVersion: openvswitch.starlingx.io/v1 kind: OVSPort metadata: name: external-access-port namespace: openvswitch spec: name: br0-port1 bridgeName: br0 vlanMode: trunk vlanTag: 705 stpEnable: false interfaces: - name: port1 # interface name comes from ovsAgent.networks
Apply the resource:
$ kubectl apply -f host-access-port.yaml
Optionally, platform networks can be added on top of the ovs-access interface.
~(keystone_admin)]$ system host-if-add controller-0 oam0 vlan base0 -V 705 -c platform ~(keystone_admin)]$ system interface-network-assign controller-0 base0 pxeboot ~(keystone_admin)]$ system interface-network-assign controller-0 oam0 oam ~(keystone_admin)]$ system interface-network-assign controller-0 oam0 mgmt
Pod Access via OpenvSwitch¶
Pod-to-pod connectivity through the OVS bridge is achieved by using a bridge
CNI network-attachment-definition on both the application pod and the
ovs-agent pod, pointing to the same Linux bridge name. This creates a
shared Layer 2 segment between the two pods.
How it works
Both the OVS pod and the application pod attach to the same bridge CNI. The bridge CNI creates a Linux bridge on the host and connects a veth interface from each pod to that bridge. Traffic from the application pod traverses the Linux bridge into the OVS pod, where it can be forwarded through OVS bridge ports to external networks.
Configuration steps
Create a NetworkAttachmentDefinition that uses the bridge CNI plugin. If
ovsAgent.hostAccess.enabled=truewas selected this new bridge-cni cannot use the config field “bridge” with the “ovs0” value as this is reserved for host access, see below:apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: name: ovs-bridge-access namespace: openvswitch spec: # cannot use {"bridge": "ovs0"} if ovsAgent.hostAccess.enabled=true config: '{ "cniVersion": "1.0.0", "name": "ovs-bridge-access", "type": "bridge", "bridge": "ovs-br1" }'
Reference this NAD in the
ovsAgent.networksHelm override so that the OVS pod attaches to the bridge:~(keystone_admin)]$ system helm-override-update openvswitch ovs-agent openvswitch \ --set ovsAgent.networks='{"name":"ovs-bridge-access"\,"interface":"pod-link0"}'In the application pod, add the same NAD annotation:
apiVersion: v1 kind: Pod metadata: name: my-app-pod annotations: k8s.v1.cni.cncf.io/networks: '[{"name":"ovs-bridge-access", "interface":"ovs-link0"}]' spec: containers: - name: app image: my-app-image
Apply the application to pick up the override change:
~(keystone_admin)]$ system application-apply openvswitch
Note
Pods with SR-IOV resources should prefer to connect directly to the desired interfaces rather than using the bridge CNI method. Direct SR-IOV attachment is more efficient as it bypasses the host Linux bridge and provides near-native I/O performance with lower latency.
Multi-node installations¶
In a multi-node deployment, the OVS Agent runs on every node that carries the
ovs-node=enabled label. Bridges and ports are distributed across nodes using
the OVSBridge resource’s nodeSelector field and per-node OVSPort
resources that reference interfaces available on specific hosts.
Label nodes
Each node that should participate in the OVS topology must be labeled. You can
use additional labels beyond ovs-node=enabled to create subgroups of nodes
for different bridge topologies:
~(keystone_admin)]$ system host-label-assign worker-0 ovs-node=enabled
~(keystone_admin)]$ system host-label-assign worker-1 ovs-node=enabled
~(keystone_admin)]$ system host-label-assign worker-0 ovs-role=fronthaul
~(keystone_admin)]$ system host-label-assign worker-1 ovs-role=fronthaul
The ovs-node=enabled label is required for the DaemonSet to schedule the
OVS Agent pod on a node. Additional labels (e.g., ovs-role=fronthaul) can be
used in the OVSBridge resource’s nodeSelector to target a subset of
OVS-enabled nodes.
Target bridges to specific nodes
The OVSBridge resource supports a nodeSelector field that controls which
nodes the bridge is created on. This uses the standard Kubernetes label selector
semantics (matchLabels and matchExpressions).
There are several common patterns for targeting bridges:
All OVS-enabled nodes — deploy the bridge everywhere the agent runs:
apiVersion: openvswitch.starlingx.io/v1
kind: OVSBridge
metadata:
name: br-fronthaul
namespace: openvswitch
spec:
bridgeName: br-fronthaul
rstpEnable: true
macAgingTime: 300
macTableSize: 2048
A subset of nodes by role — use additional custom labels to group nodes:
apiVersion: openvswitch.starlingx.io/v1
kind: OVSBridge
metadata:
name: br-fronthaul
namespace: openvswitch
spec:
bridgeName: br-fronthaul
rstpEnable: true
macAgingTime: 300
macTableSize: 2048
nodeSelector:
matchLabels:
ovs-node: enabled
ovs-role: fronthaul
This creates br-fronthaul only on nodes labeled with both
ovs-node=enabled and ovs-role=fronthaul.
Use matchExpressions for flexible selection — target nodes using set-based
operators (In, NotIn, Exists, DoesNotExist):
apiVersion: openvswitch.starlingx.io/v1
kind: OVSBridge
metadata:
name: br-backhaul
namespace: openvswitch
spec:
bridgeName: br-backhaul
rstpEnable: true
nodeSelector:
matchExpressions:
- key: ovs-node
operator: In
values: ["enabled"]
- key: ovs-role
operator: In
values: ["backhaul", "combined"]
This creates br-backhaul on any node labeled ovs-node=enabled whose
ovs-role is either backhaul or combined.
Multiple bridges on different node groups — deploy distinct bridge topologies to different sets of nodes:
# Bridge for fronthaul nodes only
apiVersion: openvswitch.starlingx.io/v1
kind: OVSBridge
metadata:
name: br-fronthaul
namespace: openvswitch
spec:
bridgeName: br-fronthaul
rstpEnable: true
nodeSelector:
matchLabels:
ovs-role: fronthaul
---
# Bridge for backhaul nodes only
apiVersion: openvswitch.starlingx.io/v1
kind: OVSBridge
metadata:
name: br-backhaul
namespace: openvswitch
spec:
bridgeName: br-backhaul
rstpEnable: true
nodeSelector:
matchLabels:
ovs-role: backhaul
Define per-node ports
OVSPort resources reference a bridge by name and are reconciled on every
node where that bridge exists. Since each node may have different physical
interfaces, use distinct OVSPort resources with node-specific naming to
distinguish configurations. The interface names in the OVSPort spec
correspond to the Multus interface names attached to the pod via
ovsAgent.networks.
The following example shows port configuration for a two-node fronthaul topology
where each node has an uplink (midhaul0), a cross-link to the peer
(protect0), and a downlink (fronthaul0):
# --- Ports for worker-0 ---
apiVersion: openvswitch.starlingx.io/v1
kind: OVSPort
metadata:
name: worker0-midhaul0
namespace: openvswitch
spec:
name: midhaul0
bridgeName: br-fronthaul
vlanMode: trunk
vlanTrunks:
- 706
stpEnable: true
stpPathCost: 100
interfaces:
- name: midhaul0
bfdEnable: true
bfdMinTx: 300
bfdMinRx: 300
bfdMult: 3
---
apiVersion: openvswitch.starlingx.io/v1
kind: OVSPort
metadata:
name: worker0-protect0
namespace: openvswitch
spec:
name: protect0
bridgeName: br-fronthaul
vlanMode: trunk
vlanTrunks:
- 706
stpEnable: true
stpPathCost: 200
interfaces:
- name: protect0
bfdEnable: true
bfdMinTx: 300
bfdMinRx: 300
bfdMult: 3
---
apiVersion: openvswitch.starlingx.io/v1
kind: OVSPort
metadata:
name: worker0-fronthaul0
namespace: openvswitch
spec:
name: fronthaul0
bridgeName: br-fronthaul
vlanMode: access
vlanTag: 706
stpEnable: false
interfaces:
- name: fronthaul0
# --- Ports for worker-1 ---
apiVersion: openvswitch.starlingx.io/v1
kind: OVSPort
metadata:
name: worker1-midhaul0
namespace: openvswitch
spec:
name: midhaul0
bridgeName: br-fronthaul
vlanMode: trunk
vlanTrunks:
- 706
stpEnable: true
stpPathCost: 100
interfaces:
- name: midhaul0
bfdEnable: true
bfdMinTx: 300
bfdMinRx: 300
bfdMult: 3
---
apiVersion: openvswitch.starlingx.io/v1
kind: OVSPort
metadata:
name: worker1-protect0
namespace: openvswitch
spec:
name: protect0
bridgeName: br-fronthaul
vlanMode: trunk
vlanTrunks:
- 706
stpEnable: true
stpPathCost: 200
interfaces:
- name: protect0
bfdEnable: true
bfdMinTx: 300
bfdMinRx: 300
bfdMult: 3
---
apiVersion: openvswitch.starlingx.io/v1
kind: OVSPort
metadata:
name: worker1-fronthaul0
namespace: openvswitch
spec:
name: fronthaul0
bridgeName: br-fronthaul
vlanMode: access
vlanTag: 706
stpEnable: false
interfaces:
- name: fronthaul0
Apply the resources:
$ kubectl apply -f br-fronthaul.yaml
$ kubectl apply -f worker0-ports.yaml
$ kubectl apply -f worker1-ports.yaml
Note
Port names in the OVSPort metadata must be unique across the cluster.
Use a node-specific prefix (e.g., worker0-, worker1-) to avoid
naming collisions. The spec.name field defines the actual port name
inside OVS and can be the same across nodes since each node runs its own
independent OVS instance.
Note
The interface names used in OVSPort specs (e.g., midhaul0,
protect0, fronthaul0) must match the interface field values
configured in the ovsAgent.networks Helm override. These are the Multus
interface names that appear inside the OVS Agent pod.