diff --git a/README.md b/README.md index eec6b96e45..ad87d60f85 100644 --- a/README.md +++ b/README.md @@ -157,18 +157,6 @@ OVNKubernetes supports the following configuration options, all of which are opt * `egressIPConfig`: holds the configuration for EgressIP options. * `reachabilityTotalTimeoutSeconds`: Set EgressIP node reachability total timeout in seconds, 0 means disable reachability check and the default is 1 second. -#### DPU Host Mode Support - -OVN-Kubernetes supports specialized hardware deployments such as DPU (Data Processing Unit) hosts through the `OVN_NODE_MODE` environment variable. In `dpu-host` mode, certain features are automatically disabled on those nodes regardless of cluster-wide configuration: - -- Egress IP and related features (egress firewall, egress QoS, egress service) -- Multicast support -- Multi-external gateway support -- Multi-network policies and admin network policies -- Network segmentation features - -This per-node feature enforcement is implemented through conditional logic in the startup scripts, allowing the same cluster configuration to work across heterogeneous node types. For detailed information about node modes and the technical implementation, see `docs/ovn_node_mode.md`. - These configuration flags are only in the Operator configuration object. Example from the `manifests/cluster-network-03-config.yml` file: diff --git a/bindata/network/ovn-kubernetes/common/008-script-lib.yaml b/bindata/network/ovn-kubernetes/common/008-script-lib.yaml index 4eb3ad4b40..5059a16f6b 100644 --- a/bindata/network/ovn-kubernetes/common/008-script-lib.yaml +++ b/bindata/network/ovn-kubernetes/common/008-script-lib.yaml @@ -539,16 +539,17 @@ data: echo "I$(date "+%m%d %H:%M:%S.%N") - starting ovnkube-node" - # enable egress ip, egress firewall, egress qos, egress service - egress_features_enable_flag="--enable-egress-ip=true --enable-egress-firewall=true --enable-egress-qos=true --enable-egress-service=true" - init_ovnkube_controller="--init-ovnkube-controller ${K8S_NODE}" - multi_external_gateway_enable_flag="--enable-multi-external-gateway=true" - gateway_interface=br-ex - # enable multicast - enable_multicast_flag="--enable-multicast" + if [ "{{.OVN_NODE_MODE}}" == "dpu-host" ]; then + // this is required for the dpu-host mode to configure right gateway interface + // https://github.com/ovn-kubernetes/ovn-kubernetes/pull/5327/files + gateway_interface=derive-from-mgmt-port + else + gateway_interface=br-ex + fi + + init_ovnkube_controller="--init-ovnkube-controller ${K8S_NODE}" - # Use OVN_NODE_MODE environment variable, default to "full" if not set OVN_NODE_MODE=${OVN_NODE_MODE:-full} # We check only dpu-host mode and not smart-nic mode here as currently we do not support it yet # Once we support it, we will need to check for it here and add relevant code. @@ -557,17 +558,10 @@ data: # https://github.com/ovn-kubernetes/ovn-kubernetes/pull/5327/files gateway_interface="derive-from-mgmt-port" ovnkube_node_mode="--ovnkube-node-mode dpu-host" - # disable egress ip for dpu-host mode as it is not supported - egress_features_enable_flag="" - - # disable multicast for dpu-host mode as it is not supported - enable_multicast_flag="" # disable init-ovnkube-controller for dpu-host mode as it is not supported init_ovnkube_controller="" - # disable multi-external-gateway for dpu-host mode as it is not supported - multi_external_gateway_enable_flag="" fi if [ "{{.OVN_GATEWAY_MODE}}" == "shared" ]; then @@ -613,12 +607,12 @@ data: fi multi_network_enabled_flag= - if [[ "{{.OVN_MULTI_NETWORK_ENABLE}}" == "true" && "${OVN_NODE_MODE}" != "dpu-host" ]]; then + if [[ "{{.OVN_MULTI_NETWORK_ENABLE}}" == "true" ]]; then multi_network_enabled_flag="--enable-multi-network" fi network_segmentation_enabled_flag= - if [[ "{{.OVN_NETWORK_SEGMENTATION_ENABLE}}" == "true" && "${OVN_NODE_MODE}" != "dpu-host" ]]; then + if [[ "{{.OVN_NETWORK_SEGMENTATION_ENABLE}}" == "true" ]]; then multi_network_enabled_flag="--enable-multi-network" network_segmentation_enabled_flag="--enable-network-segmentation" fi @@ -639,12 +633,12 @@ data: fi multi_network_policy_enabled_flag= - if [[ "{{.OVN_MULTI_NETWORK_POLICY_ENABLE}}" == "true"&& "${OVN_NODE_MODE}" != "dpu-host" ]]; then + if [[ "{{.OVN_MULTI_NETWORK_POLICY_ENABLE}}" == "true" ]]; then multi_network_policy_enabled_flag="--enable-multi-networkpolicy" fi admin_network_policy_enabled_flag= - if [[ "{{.OVN_ADMIN_NETWORK_POLICY_ENABLE}}" == "true" && "${OVN_NODE_MODE}" != "dpu-host" ]]; then + if [[ "{{.OVN_ADMIN_NETWORK_POLICY_ENABLE}}" == "true" ]]; then admin_network_policy_enabled_flag="--enable-admin-network-policy" fi @@ -733,7 +727,7 @@ data: ${admin_network_policy_enabled_flag} \ ${dns_name_resolver_enabled_flag} \ ${network_observability_enabled_flag} \ - ${enable_multicast_flag} \ + --enable-multicast \ --zone ${K8S_NODE} \ --enable-interconnect \ --acl-logging-rate-limit "{{.OVNPolicyAuditRateLimit}}" \ @@ -746,7 +740,5 @@ data: ${ovn_v4_masquerade_subnet_opt} \ ${ovn_v6_masquerade_subnet_opt} \ ${ovn_v4_transit_switch_subnet_opt} \ - ${ovn_v6_transit_switch_subnet_opt} \ - ${egress_features_enable_flag} \ - ${multi_external_gateway_enable_flag} + ${ovn_v6_transit_switch_subnet_opt} } diff --git a/bindata/network/ovn-kubernetes/managed/004-config.yaml b/bindata/network/ovn-kubernetes/managed/004-config.yaml index 1903f3dc52..3b893fe266 100644 --- a/bindata/network/ovn-kubernetes/managed/004-config.yaml +++ b/bindata/network/ovn-kubernetes/managed/004-config.yaml @@ -33,9 +33,16 @@ data: dns-service-name="dns-default" [ovnkubernetesfeature] + enable-egress-ip=true + enable-egress-firewall=true + enable-egress-qos=true + enable-egress-service=true {{- if .ReachabilityNodePort }} egressip-node-healthcheck-port={{.ReachabilityNodePort}} {{- end }} +{{- if .OVN_MULTI_NETWORK_ENABLE }} + enable-multi-network=true +{{- end }} {{- if .OVN_NETWORK_SEGMENTATION_ENABLE }} {{- if not .OVN_MULTI_NETWORK_ENABLE }} enable-multi-network=true @@ -45,6 +52,13 @@ data: {{- if .OVN_PRE_CONF_UDN_ADDR_ENABLE }} enable-preconfigured-udn-addresses=true {{- end }} +{{- if .OVN_MULTI_NETWORK_POLICY_ENABLE }} + enable-multi-networkpolicy=true +{{- end }} +{{- if .OVN_ADMIN_NETWORK_POLICY_ENABLE }} + enable-admin-network-policy=true +{{- end }} + enable-multi-external-gateway=true {{- if .DNS_NAME_RESOLVER_ENABLE }} enable-dns-name-resolver=true {{- end }} @@ -133,6 +147,13 @@ data: enable-preconfigured-udn-addresses=true {{- end }} {{- end }} +{{- if .OVN_MULTI_NETWORK_POLICY_ENABLE }} + enable-multi-networkpolicy=true +{{- end }} +{{- if .OVN_ADMIN_NETWORK_POLICY_ENABLE }} + enable-admin-network-policy=true +{{- end }} + enable-multi-external-gateway=true {{- if .DNS_NAME_RESOLVER_ENABLE }} enable-dns-name-resolver=true {{- end }} diff --git a/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml b/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml index 262923675d..5313e0c68f 100644 --- a/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml +++ b/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml @@ -193,13 +193,8 @@ spec: # will rollout control plane pods as well network_segmentation_enabled_flag= multi_network_enabled_flag= - if [[ "{{.OVN_MULTI_NETWORK_ENABLE}}" == "true" ]]; then - multi_network_enabled_flag="--enable-multi-network" - fi if [[ "{{.OVN_NETWORK_SEGMENTATION_ENABLE}}" == "true" ]]; then - if [[ "{{.OVN_MULTI_NETWORK_ENABLE}}" != "true" ]]; then - multi_network_enabled_flag="--enable-multi-network" - fi + multi_network_enabled_flag="--enable-multi-network" network_segmentation_enabled_flag="--enable-network-segmentation" fi @@ -213,18 +208,6 @@ spec: preconfigured_udn_addresses_enable_flag="--enable-preconfigured-udn-addresses" fi - # Enable multi-network policy if configured (control-plane always full mode) - multi_network_policy_enabled_flag= - if [[ "{{.OVN_MULTI_NETWORK_POLICY_ENABLE}}" == "true" ]]; then - multi_network_policy_enabled_flag="--enable-multi-networkpolicy" - fi - - # Enable admin network policy if configured (control-plane always full mode) - admin_network_policy_enabled_flag= - if [[ "{{.OVN_ADMIN_NETWORK_POLICY_ENABLE}}" == "true" ]]; then - admin_network_policy_enabled_flag="--enable-admin-network-policy" - fi - echo "I$(date "+%m%d %H:%M:%S.%N") - ovnkube-control-plane - start ovnkube --init-cluster-manager ${K8S_NODE}" exec /usr/bin/ovnkube \ --enable-interconnect \ @@ -248,15 +231,7 @@ spec: ${multi_network_enabled_flag} \ ${network_segmentation_enabled_flag} \ ${route_advertisements_enable_flag} \ - ${preconfigured_udn_addresses_enable_flag} \ - --enable-egress-ip=true \ - --enable-egress-firewall=true \ - --enable-egress-qos=true \ - --enable-egress-service=true \ - --enable-multicast \ - --enable-multi-external-gateway=true \ - ${multi_network_policy_enabled_flag} \ - ${admin_network_policy_enabled_flag} + ${preconfigured_udn_addresses_enable_flag} volumeMounts: - mountPath: /run/ovnkube-config/ name: ovnkube-config diff --git a/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml b/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml index 717c3e6877..2e9ce2245d 100644 --- a/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml +++ b/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml @@ -36,13 +36,19 @@ data: dns-service-name="dns-default" [ovnkubernetesfeature] - + enable-egress-ip=true + enable-egress-firewall=true + enable-egress-qos=true + enable-egress-service=true {{- if .ReachabilityTotalTimeoutSeconds }} egressip-reachability-total-timeout={{.ReachabilityTotalTimeoutSeconds}} {{- end }} {{- if .ReachabilityNodePort }} egressip-node-healthcheck-port={{.ReachabilityNodePort}} {{- end }} +{{- if .OVN_MULTI_NETWORK_ENABLE }} + enable-multi-network=true +{{- end }} {{- if .OVN_NETWORK_SEGMENTATION_ENABLE }} {{- if not .OVN_MULTI_NETWORK_ENABLE }} enable-multi-network=true @@ -55,6 +61,10 @@ data: {{- if .OVN_MULTI_NETWORK_POLICY_ENABLE }} enable-multi-networkpolicy=true {{- end }} +{{- if .OVN_ADMIN_NETWORK_POLICY_ENABLE }} + enable-admin-network-policy=true +{{- end }} + enable-multi-external-gateway=true {{- if .DNS_NAME_RESOLVER_ENABLE }} enable-dns-name-resolver=true {{- end }} diff --git a/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml b/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml index df8a7e029c..32bfd812ef 100644 --- a/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml +++ b/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml @@ -144,13 +144,8 @@ spec: # will rollout control plane pods as well network_segmentation_enabled_flag= multi_network_enabled_flag= - if [[ "{{.OVN_MULTI_NETWORK_ENABLE}}" == "true" ]]; then - multi_network_enabled_flag="--enable-multi-network" - fi if [[ "{{.OVN_NETWORK_SEGMENTATION_ENABLE}}" == "true" ]]; then - if [[ "{{.OVN_MULTI_NETWORK_ENABLE}}" != "true" ]]; then - multi_network_enabled_flag="--enable-multi-network" - fi + multi_network_enabled_flag="--enable-multi-network" network_segmentation_enabled_flag="--enable-network-segmentation" fi @@ -163,18 +158,6 @@ spec: if [[ "{{.OVN_PRE_CONF_UDN_ADDR_ENABLE}}" == "true" ]]; then preconfigured_udn_addresses_enable_flag="--enable-preconfigured-udn-addresses" fi - - # Enable multi-network policy if configured (control-plane always full mode) - multi_network_policy_enabled_flag= - if [[ "{{.OVN_MULTI_NETWORK_POLICY_ENABLE}}" == "true" ]]; then - multi_network_policy_enabled_flag="--enable-multi-networkpolicy" - fi - - # Enable admin network policy if configured (control-plane always full mode) - admin_network_policy_enabled_flag= - if [[ "{{.OVN_ADMIN_NETWORK_POLICY_ENABLE}}" == "true" ]]; then - admin_network_policy_enabled_flag="--enable-admin-network-policy" - fi if [ "{{.OVN_GATEWAY_MODE}}" == "shared" ]; then gateway_mode_flags="--gateway-mode shared" @@ -206,15 +189,7 @@ spec: ${network_segmentation_enabled_flag} \ ${gateway_mode_flags} \ ${route_advertisements_enable_flag} \ - ${preconfigured_udn_addresses_enable_flag} \ - --enable-egress-ip=true \ - --enable-egress-firewall=true \ - --enable-egress-qos=true \ - --enable-egress-service=true \ - --enable-multicast \ - --enable-multi-external-gateway=true \ - ${multi_network_policy_enabled_flag} \ - ${admin_network_policy_enabled_flag} + ${preconfigured_udn_addresses_enable_flag} volumeMounts: - mountPath: /run/ovnkube-config/ name: ovnkube-config diff --git a/docs/architecture.md b/docs/architecture.md index 6a2339eba8..289e06a3c0 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -141,12 +141,6 @@ The Network operator needs to make sure that the input configuration doesn't cha The persisted configuration must **make all defaults explicit**. This protects against inadvertent code changes that could destabilize an existing cluster. -### Per-Node Configuration - -For certain specialized deployments (e.g., DPU host nodes), some features need to be disabled on a per-node basis even when enabled cluster-wide. Since ConfigMap values cannot be reliably overridden per-node, the CNO implements per-node feature enforcement through conditional logic in the startup scripts. - -The `OVN_NODE_MODE` environment variable is injected into `ovnkube-node` pods and consumed by the startup script (`008-script-lib.yaml`) to conditionally enable or disable features based on the node's operational mode. This ensures that unsupported features are deterministically disabled on specialized hardware regardless of cluster-wide configuration. - ## Egress Router **Input:** `EgressRouter.network.operator.openshift.io` diff --git a/docs/operands.md b/docs/operands.md index 755ff81e52..1d129a51fe 100644 --- a/docs/operands.md +++ b/docs/operands.md @@ -93,26 +93,6 @@ configuration object (which in turn is copied there from the configuration) is "`OVNKubernetes`". If the specified network type is not "`OVNKubernetes`", the CNO will not render any network plugin. -### OVN-Kubernetes Node Modes - -OVN-Kubernetes supports different node operational modes through the `OVN_NODE_MODE` -environment variable. This allows per-node feature enforcement, particularly for -specialized hardware like DPU (Data Processing Unit) hosts where certain features -must be disabled. - -The startup script (`008-script-lib.yaml`) contains conditional logic that adjusts -feature enablement based on the node mode: - -- **`full` mode (default)**: All features enabled as configured -- **`dpu-host` mode**: Certain features like egress IP, multicast, multi-network - policies, and admin network policies are automatically disabled regardless of - cluster-wide configuration - -This approach was necessary because ConfigMap values (`004-config.yaml`) cannot be -reliably overridden on a per-node basis, but startup script logic can be conditional. - -For detailed information, see `docs/ovn_node_mode.md`. - ## Multus Multus is deployed as long as `.spec.disableMultiNetwork` is not set. diff --git a/docs/ovn_node_mode.md b/docs/ovn_node_mode.md deleted file mode 100644 index a0dea848e0..0000000000 --- a/docs/ovn_node_mode.md +++ /dev/null @@ -1,100 +0,0 @@ -## OVN node modes and per-node feature enforcement - -This change introduces `OVN_NODE_MODE` as an environment variable injected into the `ovnkube-node` Pod. The value is consumed by the startup script rendered from `bindata/network/ovn-kubernetes/common/008-script-lib.yaml` to tailor behavior per node mode at runtime. - -### Why move flags from the config map into the script? - -- The INI-based config (`004-config.yaml`) is rendered cluster-wide. Those values are not reliably overridable on a per-node or per-mode basis. -- In DPU host mode, some features are not supported and must be deterministically disabled on those nodes even if the cluster-wide config enables them. -- Moving the enablement logic to the entrypoint script allows per-node enforcement using `OVN_NODE_MODE`, preventing unsupported features from being turned on by cluster defaults. - -### Behavior by mode - -- `full` (default): - - `gateway_interface=br-ex` - - `init_ovnkube_controller="--init-ovnkube-controller ${K8S_NODE}"` - - `enable_multicast_flag="--enable-multicast"` - - `egress_features_enable_flag="--enable-egress-ip=true --enable-egress-firewall=true --enable-egress-qos=true --enable-egress-service=true"` - - `multi_external_gateway_enable_flag="--enable-multi-external-gateway=true"` - -- `dpu-host`: - - `gateway_interface="derive-from-mgmt-port"` - - `ovnkube_node_mode="--ovnkube-node-mode dpu-host"` - - `init_ovnkube_controller=""` (disabled) - - `enable_multicast_flag=""` (disabled) - - `egress_features_enable_flag=""` (egress IP and related features disabled) - - `multi_external_gateway_enable_flag=""` (multi-external gateway disabled) - - Multi-network, network segmentation, and multi-network policy/admin network policy are gated and not enabled in this mode. - -### Manifests - -- `ovnkube-node.yaml` (managed and self-hosted) now inject `OVN_NODE_MODE` into the Pod env so the script can apply mode-aware logic. -- `ovnkube-control-plane.yaml` (managed and self-hosted) have feature flags moved from ConfigMap to inline script logic. -- `004-config.yaml` drops hard-coded feature enables that conflict with per-node enforcement. - -**Note**: Control-plane components always run in "full" mode since they don't run on DPU hosts and need all features enabled for cluster coordination. Always-enabled features (egress, multicast, multi-external-gateway) are added directly to the command line, while conditional features use script variables. - -### Implementation Details - -#### Environment Variable Injection - -The `OVN_NODE_MODE` environment variable is injected into `ovnkube-node` pods through the DaemonSet specification in both managed and self-hosted variants: - -- `bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml` -- `bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml` - -The value is typically derived from node labels or annotations that identify the node's hardware type. - -#### Script Logic Flow - -The startup script (`008-script-lib.yaml`) implements the following conditional logic: - -```bash -if [[ "${OVN_NODE_MODE}" != "dpu-host" ]]; then - # Enable features for full mode - egress_ip_enable_flag="--enable-egress-ip=true --enable-egress-firewall=true --enable-egress-qos=true --enable-egress-service=true" - enable_multicast_flag="--enable-multicast" - # ... other feature flags -else - # DPU host mode - disable features - egress_ip_enable_flag="" - enable_multicast_flag="" - gateway_interface="derive-from-mgmt-port" - ovnkube_node_mode="--ovnkube-node-mode dpu-host" -fi -``` - -#### Feature Flag Mapping - -The following table shows how cluster-wide configuration translates to per-node enforcement: - -| Feature | ConfigMap (004-config.yaml) | Script Variable | DPU Host Behavior | -|---------|----------------------------|-----------------|-------------------| -| Egress IP | `enable-egress-ip=true` | `egress_features_enable_flag` | Force disabled | -| Multicast | `enable-multicast=true` | `enable_multicast_flag` | Force disabled | -| Multi External Gateway | `enable-multi-external-gateway=true` | `multi_external_gateway_enable_flag` | Force disabled | -| Multi-network | `enable-multi-network=true` | `multi_network_enabled_flag` | Conditionally disabled | -| Admin Network Policy | `enable-admin-network-policy=true` | `admin_network_policy_enabled_flag` | Conditionally disabled | -| Network Segmentation | `enable-network-segmentation=true` | `network_segmentation_enabled_flag` | Conditionally disabled | - -### Testing - -- Unit tests assert that the rendered script contains the correct assignments for `gateway_interface`, `init_ovnkube_controller`, `enable_multicast_flag`, `egress_features_enable_flag`, and `ovnkube_node_mode` across modes. -- The comprehensive test `TestOVNKubernetesScriptLibCombined` validates all conditional logic paths and feature flag assignments for node scripts. -- The test `TestOVNKubernetesControlPlaneFlags` validates that control-plane scripts have: - - Always-enabled features added directly to the command line (egress, multicast, multi-external-gateway) - - Conditional features handled via script variables (multi-network, network policies, etc.) - - Correct multi-network enablement logic (OVN_MULTI_NETWORK_ENABLE or OVN_NETWORK_SEGMENTATION_ENABLE) -- Tests verify both positive cases (features enabled in full mode) and negative cases (features disabled in DPU host mode). - -### Migration Notes - -When upgrading clusters that previously relied on ConfigMap-based feature control: - -1. Existing ConfigMap values in `004-config.yaml` have been removed for features that require per-node control -2. The startup scripts (both node and control-plane) now contain the authoritative feature enablement logic -3. Control-plane components automatically enable all features (always run in "full" mode) -4. DPU host nodes will automatically have incompatible features disabled regardless of previous ConfigMap settings -5. No manual intervention is required - the migration is handled automatically during the upgrade process - - diff --git a/pkg/network/ovn_kubernetes_test.go b/pkg/network/ovn_kubernetes_test.go index 8109ab1166..3b7f00a871 100644 --- a/pkg/network/ovn_kubernetes_test.go +++ b/pkg/network/ovn_kubernetes_test.go @@ -36,6 +36,7 @@ import ( cnofake "github.com/openshift/cluster-network-operator/pkg/client/fake" "github.com/openshift/cluster-network-operator/pkg/hypershift" "github.com/openshift/cluster-network-operator/pkg/names" + "github.com/openshift/cluster-network-operator/pkg/render" ) var ( @@ -297,7 +298,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -334,7 +341,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=local @@ -384,8 +397,14 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-reachability-total-timeout=3 egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=local @@ -437,8 +456,14 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-reachability-total-timeout=0 egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=local @@ -490,7 +515,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=local @@ -542,7 +573,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -583,7 +620,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -627,7 +670,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -664,7 +713,12 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-external-gateway=true [gateway] mode=shared @@ -702,8 +756,15 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true enable-multi-networkpolicy=true +enable-admin-network-policy=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -742,8 +803,14 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true enable-network-segmentation=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -780,7 +847,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-admin-network-policy=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -819,7 +892,13 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true +enable-multi-external-gateway=true enable-dns-name-resolver=true [gateway] @@ -857,9 +936,15 @@ dns-service-namespace="openshift-dns" dns-service-name="dns-default" [ovnkubernetesfeature] +enable-egress-ip=true +enable-egress-firewall=true +enable-egress-qos=true +enable-egress-service=true egressip-node-healthcheck-port=9107 +enable-multi-network=true enable-network-segmentation=true enable-preconfigured-udn-addresses=true +enable-multi-external-gateway=true [gateway] mode=shared @@ -3905,92 +3990,6 @@ func extractOVNScriptLib(g *WithT, objs []*uns.Unstructured) string { return "" } -// renderControlPlaneWithOverrides renders using the full render path and returns -// the embedded startup script from the control-plane container. -func renderControlPlaneWithOverrides(t *testing.T, variant string, overrides map[string]interface{}) string { - g := NewGomegaWithT(t) - crd := OVNKubernetesConfig.DeepCopy() - config := &crd.Spec - fillDefaults(config, nil) - - bs := fakeBootstrapResult() - bs.OVN = bootstrap.OVNBootstrapResult{ - ControlPlaneReplicaCount: 1, - OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{Enabled: false}, - ConfigOverrides: toStringMap(overrides), - }, - } - - featureGatesCNO := getDefaultFeatureGates() - fakeClient := cnofake.NewFakeClient() - objs, _, err := renderOVNKubernetes(config, bs, manifestDirOvn, fakeClient, featureGatesCNO) - g.Expect(err).NotTo(HaveOccurred()) - - var script string - for _, obj := range objs { - if obj.GetKind() == "Deployment" && obj.GetName() == "ovnkube-control-plane" && obj.GetNamespace() == "openshift-ovn-kubernetes" { - containers, found, err := uns.NestedSlice(obj.Object, "spec", "template", "spec", "containers") - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(found).To(BeTrue()) - for _, c := range containers { - cm := c.(map[string]interface{}) - if name, ok := cm["name"]; ok && (name == "ovnkube-cluster-manager" || name == "ovnkube-control-plane") { - command, found, err := uns.NestedSlice(cm, "command") - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(found).To(BeTrue()) - g.Expect(len(command)).To(BeNumerically(">", 2)) - script = command[2].(string) - break - } - } - } - } - g.Expect(script).NotTo(BeEmpty()) - return script -} - -// renderScriptLibWithOverrides renders using the full render path and returns -// the ovnkube script-lib content. -func renderScriptLibWithOverrides(t *testing.T, overrides map[string]interface{}) string { - g := NewGomegaWithT(t) - crd := OVNKubernetesConfig.DeepCopy() - config := &crd.Spec - fillDefaults(config, nil) - - bs := fakeBootstrapResult() - bs.OVN = bootstrap.OVNBootstrapResult{ - ControlPlaneReplicaCount: 1, - OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{Enabled: false}, - ConfigOverrides: toStringMap(overrides), - }, - } - featureGatesCNO := getDefaultFeatureGates() - fakeClient := cnofake.NewFakeClient() - objs, _, err := renderOVNKubernetes(config, bs, manifestDirOvn, fakeClient, featureGatesCNO) - g.Expect(err).NotTo(HaveOccurred()) - return extractOVNScriptLib(g, objs) -} - -// helper to convert map[string]interface{} -> map[string]string for overrides -func toStringMap(in map[string]interface{}) map[string]string { - if in == nil { - return nil - } - out := make(map[string]string, len(in)) - for k, v := range in { - out[k] = fmt.Sprintf("%v", v) - } - return out -} - // checkDaemonsetAnnotation check that all the daemonset have the annotation with the // same key and value func checkDaemonsetAnnotation(g *WithT, objs []*uns.Unstructured, key, value string) bool { @@ -4248,226 +4247,94 @@ func TestRenderOVNKubernetes_AdvertisedUDNIsolationModeOverride(t *testing.T) { }) } -func TestOVNKubernetesControlPlaneFlags(t *testing.T) { +func TestOVNKubernetesScriptLibGatewayInterface(t *testing.T) { g := NewGomegaWithT(t) testCases := []struct { - name string - variant string - overrides map[string]interface{} - mustContain []string - mustNotContain []string + name string + ovnNodeMode string + expectedGatewayInterface string }{ { - name: "self-hosted control-plane: always-enabled features", - variant: "self-hosted", - overrides: map[string]interface{}{ - "OVN_OBSERVABILITY_ENABLE": "false", - "OVN_MULTI_NETWORK_POLICY_ENABLE": "false", - "OVN_ADMIN_NETWORK_POLICY_ENABLE": "false", - }, - mustContain: []string{ - "--enable-egress-ip=true", - "--enable-egress-firewall=true", - "--enable-egress-qos=true", - "--enable-egress-service=true", - "--enable-multicast", - "--enable-multi-external-gateway=true", - }, - mustNotContain: []string{ - "egress_features_enable_flag=", - "enable_multicast_flag=", - "multi_external_gateway_enable_flag=", - }, - }, - - { - name: "self-hosted control-plane: conditional features enabled", - variant: "self-hosted", - overrides: map[string]interface{}{ - "OVN_MULTI_NETWORK_POLICY_ENABLE": "true", - "OVN_ADMIN_NETWORK_POLICY_ENABLE": "true", - }, - mustContain: []string{ - "--enable-egress-ip=true", - "--enable-multicast", - "--enable-multi-external-gateway=true", - "multi_network_policy_enabled_flag=\"--enable-multi-networkpolicy\"", - "admin_network_policy_enabled_flag=\"--enable-admin-network-policy\"", - }, - mustNotContain: []string{ - "network_observability_enabled_flag=", - }, - }, - { - name: "self-hosted control-plane: multi-network enabled", - variant: "self-hosted", - overrides: map[string]interface{}{ - "OVN_MULTI_NETWORK_ENABLE": "true", - }, - mustContain: []string{ - "--enable-egress-ip=true", - "--enable-multicast", - "--enable-multi-external-gateway=true", - "multi_network_enabled_flag=\"--enable-multi-network\"", - }, - mustNotContain: []string{}, - }, - { - name: "self-hosted control-plane: network segmentation enabled (auto-enables multi-network)", - variant: "self-hosted", - overrides: map[string]interface{}{ - "OVN_NETWORK_SEGMENTATION_ENABLE": "true", - "OVN_MULTI_NETWORK_ENABLE": "false", - }, - mustContain: []string{ - "--enable-egress-ip=true", - "--enable-multicast", - "--enable-multi-external-gateway=true", - "multi_network_enabled_flag=\"--enable-multi-network\"", - "network_segmentation_enabled_flag=\"--enable-network-segmentation\"", - }, - mustNotContain: []string{}, + name: "dpu-host mode uses derive-from-mgmt-port", + ovnNodeMode: "dpu-host", + expectedGatewayInterface: "derive-from-mgmt-port", }, { - name: "self-hosted control-plane: both multi-network and segmentation enabled", - variant: "self-hosted", - overrides: map[string]interface{}{ - "OVN_NETWORK_SEGMENTATION_ENABLE": "true", - "OVN_MULTI_NETWORK_ENABLE": "true", - }, - mustContain: []string{ - "--enable-egress-ip=true", - "--enable-multicast", - "--enable-multi-external-gateway=true", - "multi_network_enabled_flag=\"--enable-multi-network\"", - "network_segmentation_enabled_flag=\"--enable-network-segmentation\"", - }, - mustNotContain: []string{}, + name: "non-dpu-host mode uses br-ex", + ovnNodeMode: "full", + expectedGatewayInterface: "br-ex", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - script := renderControlPlaneWithOverrides(t, tc.variant, tc.overrides) - for _, s := range tc.mustContain { - g.Expect(script).To(ContainSubstring(s), "Expected to find: %s", s) - } - for _, s := range tc.mustNotContain { - g.Expect(script).NotTo(ContainSubstring(s), "Expected NOT to find: %s", s) - } - }) - } -} - -func TestOVNKubernetesScriptLibCombined(t *testing.T) { - g := NewGomegaWithT(t) + // Create render data + data := render.MakeRenderData() + data.Data["OVN_NODE_MODE"] = tc.ovnNodeMode + data.Data["OVN_GATEWAY_MODE"] = "shared" + + // Set all required template variables for 008-script-lib.yaml + data.Data["ReleaseVersion"] = "4.15.0" + data.Data["OVNPolicyAuditDestination"] = "null" + data.Data["OVNPolicyAuditSyslogFacility"] = "local0" + data.Data["OVN_LOG_PATTERN_CONSOLE"] = "%D{%Y-%m-%dT%H:%M:%S.###Z}|%05N|%c%T|%p|%m" + data.Data["NorthdThreads"] = "1" + data.Data["OVNPolicyAuditMaxFileSize"] = "50" + data.Data["OVNPolicyAuditMaxLogFiles"] = "5" + data.Data["OVN_NB_INACTIVITY_PROBE"] = "60000" + data.Data["OVN_NORTHD_BACKOFF_MS"] = "300" + data.Data["PlatformType"] = "AWS" + data.Data["OVN_CONTROLLER_INACTIVITY_PROBE"] = "30000" + data.Data["GenevePort"] = "8061" + data.Data["OVNHybridOverlayVXLANPort"] = "" + data.Data["OVN_MULTI_NETWORK_ENABLE"] = "false" + data.Data["OVN_NETWORK_SEGMENTATION_ENABLE"] = "false" + data.Data["OVN_ROUTE_ADVERTISEMENTS_ENABLE"] = "false" + data.Data["OVN_OBSERVABILITY_ENABLE"] = "false" + data.Data["OVN_MULTI_NETWORK_POLICY_ENABLE"] = "false" + data.Data["OVN_ADMIN_NETWORK_POLICY_ENABLE"] = "false" + data.Data["DNS_NAME_RESOLVER_ENABLE"] = "false" + data.Data["IP_FORWARDING_MODE"] = "Restricted" + data.Data["NETWORK_NODE_IDENTITY_ENABLE"] = "false" + data.Data["NodeIdentityCertDuration"] = "24h" + data.Data["V4JoinSubnet"] = "" + data.Data["V6JoinSubnet"] = "" + data.Data["V4MasqueradeSubnet"] = "" + data.Data["V6MasqueradeSubnet"] = "" + data.Data["V4TransitSwitchSubnet"] = "" + data.Data["V6TransitSwitchSubnet"] = "" + data.Data["OVNPolicyAuditRateLimit"] = "20" + data.Data["IsNetworkTypeLiveMigration"] = false + data.Data["OVNIPsecEnable"] = false + data.Data["OVNIPsecEncap"] = "Auto" + data.Data["OVN_PRE_CONF_UDN_ADDR_ENABLE"] = false + data.Data["AdvertisedUDNIsolationMode"] = "" + + // Render the script-lib template + scriptLibPath := "../../bindata/network/ovn-kubernetes/common/008-script-lib.yaml" + objs, err := render.RenderTemplate(scriptLibPath, &data) + g.Expect(err).NotTo(HaveOccurred(), "Template rendering should succeed for %s", tc.name) + g.Expect(objs).To(HaveLen(1), "Should render exactly one object") + + // Verify it's a ConfigMap with the expected name + obj := objs[0] + g.Expect(obj.GetKind()).To(Equal("ConfigMap")) + g.Expect(obj.GetName()).To(Equal("ovnkube-script-lib")) + + // Extract the script content from the ConfigMap + scriptData, found, err := uns.NestedString(obj.Object, "data", "ovnkube-lib.sh") + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(found).To(BeTrue(), "Should find ovnkube-lib.sh in ConfigMap data") - renderScript := func(overrides map[string]interface{}) string { - return renderScriptLibWithOverrides(t, overrides) - } + // Validate gateway interface assignment + expectedGatewayAssignment := fmt.Sprintf("gateway_interface=%s", tc.expectedGatewayInterface) + g.Expect(scriptData).To(ContainSubstring(expectedGatewayAssignment), + "Script should contain correct gateway interface assignment for %s mode", tc.ovnNodeMode) - testCases := []struct { - name string - overrides map[string]interface{} - mustContain []string - mustNotContain []string - }{ - { - name: "dpu-host gating and egress/policy disable", - overrides: map[string]interface{}{ - "OVN_NODE_MODE": "dpu-host", - "OVN_MULTI_NETWORK_ENABLE": "true", - "OVN_NETWORK_SEGMENTATION_ENABLE": "true", - "OVN_MULTI_NETWORK_POLICY_ENABLE": "true", - "OVN_ADMIN_NETWORK_POLICY_ENABLE": "true", - }, - mustContain: []string{ - "gateway_interface=\"derive-from-mgmt-port\"", - "init_ovnkube_controller=\"\"", - "enable_multicast_flag=\"\"", - "egress_features_enable_flag=\"\"", - "multi_external_gateway_enable_flag=\"\"", - "ovnkube_node_mode=\"--ovnkube-node-mode dpu-host\"", - "multi_network_enabled_flag=", - "network_segmentation_enabled_flag=", - "multi_network_policy_enabled_flag=", - "admin_network_policy_enabled_flag=", - }, - mustNotContain: []string{}, - }, - { - name: "full mode with multi-network features enabled", - overrides: map[string]interface{}{ - "OVN_NODE_MODE": "full", - "OVN_MULTI_NETWORK_ENABLE": "true", - "OVN_NETWORK_SEGMENTATION_ENABLE": "true", - "OVN_MULTI_NETWORK_POLICY_ENABLE": "true", - "OVN_ADMIN_NETWORK_POLICY_ENABLE": "true", - }, - mustContain: []string{ - "gateway_interface=br-ex", - "init_ovnkube_controller=\"--init-ovnkube-controller ${K8S_NODE}\"", - "enable_multicast_flag=\"--enable-multicast\"", - "egress_features_enable_flag=\"--enable-egress-ip=true --enable-egress-firewall=true --enable-egress-qos=true --enable-egress-service=true\"", - "multi_external_gateway_enable_flag=\"--enable-multi-external-gateway=true\"", - "multi_network_enabled_flag=\"--enable-multi-network\"", - "network_segmentation_enabled_flag=\"--enable-network-segmentation\"", - "multi_network_policy_enabled_flag=\"--enable-multi-networkpolicy\"", - "admin_network_policy_enabled_flag=\"--enable-admin-network-policy\"", - }, - mustNotContain: []string{}, - }, - { - name: "non-mode-gated features enabled", - overrides: map[string]interface{}{ - "OVN_NODE_MODE": "full", - "OVN_ROUTE_ADVERTISEMENTS_ENABLE": "true", - "OVN_PRE_CONF_UDN_ADDR_ENABLE": "true", - "OVN_OBSERVABILITY_ENABLE": "true", - "DNS_NAME_RESOLVER_ENABLE": "true", - "NETWORK_NODE_IDENTITY_ENABLE": "true", - }, - mustContain: []string{ - "route_advertisements_enable_flag=\"--enable-route-advertisements\"", - "preconfigured_udn_addresses_enable_flag=\"--enable-preconfigured-udn-addresses\"", - "network_observability_enabled_flag=\"--enable-observability\"", - "dns_name_resolver_enabled_flag=\"--enable-dns-name-resolver\"", - "ip_forwarding_flag=\"--disable-forwarding\"", - "--bootstrap-kubeconfig=/var/lib/kubelet/kubeconfig", - }, - mustNotContain: []string{}, - }, - { - name: "full mode: multi-network features disabled", - overrides: map[string]interface{}{ - "OVN_NODE_MODE": "full", - "OVN_MULTI_NETWORK_ENABLE": "false", - "OVN_NETWORK_SEGMENTATION_ENABLE": "false", - "OVN_MULTI_NETWORK_POLICY_ENABLE": "false", - "OVN_ADMIN_NETWORK_POLICY_ENABLE": "false", - }, - mustContain: []string{ - "multi_network_enabled_flag=", - "network_segmentation_enabled_flag=", - "multi_network_policy_enabled_flag=", - "admin_network_policy_enabled_flag=", - }, - mustNotContain: []string{}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - script := renderScript(tc.overrides) - for _, s := range tc.mustContain { - g.Expect(script).To(ContainSubstring(s)) - } - for _, s := range tc.mustNotContain { - g.Expect(script).NotTo(ContainSubstring(s)) - } - // Ensure gateway flags use the variable rather than a hardcoded iface - g.Expect(script).To(ContainSubstring("--gateway-interface ${gateway_interface}")) + // Validate that gateway_mode_flags uses the variable + g.Expect(scriptData).To(ContainSubstring("--gateway-interface ${gateway_interface}"), + "Script should use gateway_interface variable in gateway_mode_flags") }) } }