diff --git a/bindata/network/ovn-kubernetes/common/008-script-lib.yaml b/bindata/network/ovn-kubernetes/common/008-script-lib.yaml index 8f66cbdf7e..83039343d1 100644 --- a/bindata/network/ovn-kubernetes/common/008-script-lib.yaml +++ b/bindata/network/ovn-kubernetes/common/008-script-lib.yaml @@ -508,6 +508,9 @@ data: # Ensure openflow_probe_flag is always defined openflow_probe_flag= + # Ensure dpu_lease_flags is always defined + dpu_lease_flags= + if [[ $# -ne 3 ]]; then echo "Expected three arguments but got $#" exit 1 @@ -551,6 +554,16 @@ data: # disable init-ovnkube-controller for dpu-host mode as it is not supported init_ovnkube_controller="" + dpu_lease_flags="" + if [[ -n "${OVNKUBE_NODE_LEASE_RENEW_INTERVAL}" ]]; then + dpu_lease_flags="--dpu-node-lease-renew-interval ${OVNKUBE_NODE_LEASE_RENEW_INTERVAL}" + fi + if [[ -n "${OVNKUBE_NODE_LEASE_DURATION}" ]]; then + dpu_lease_flags="$dpu_lease_flags --dpu-node-lease-duration ${OVNKUBE_NODE_LEASE_DURATION}" + fi + if [[ -n "${OVNKUBE_NODE_LEASE_NAMESPACE}" ]]; then + dpu_lease_flags="$dpu_lease_flags --dpu-node-lease-namespace ${OVNKUBE_NODE_LEASE_NAMESPACE}" + fi fi if [ "{{.OVN_GATEWAY_MODE}}" == "shared" ]; then @@ -702,5 +715,6 @@ data: ${ovn_v4_masquerade_subnet_opt} \ ${ovn_v6_masquerade_subnet_opt} \ ${ovn_v4_transit_switch_subnet_opt} \ - ${ovn_v6_transit_switch_subnet_opt} + ${ovn_v6_transit_switch_subnet_opt} \ + ${dpu_lease_flags} } diff --git a/bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml b/bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml index 741cf76122..fc722d9277 100644 --- a/bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml +++ b/bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml @@ -437,6 +437,16 @@ spec: - name: OVNKUBE_NODE_MGMT_PORT_DP_RESOURCE_NAME value: {{ .MgmtPortResourceName }} {{ end }} + {{ if or (eq .OVN_NODE_MODE "dpu-host") (eq .OVN_NODE_MODE "dpu") }} + - name: OVNKUBE_NODE_LEASE_RENEW_INTERVAL + value: "{{.DpuNodeLeaseRenewInterval}}" + - name: OVNKUBE_NODE_LEASE_DURATION + value: "{{.DpuNodeLeaseDuration}}" + - name: OVNKUBE_NODE_LEASE_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{ end }} {{ if .HTTP_PROXY }} - name: "HTTP_PROXY" value: "{{ .HTTP_PROXY}}" diff --git a/bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml b/bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml index 2d0b811ba5..87136b325d 100644 --- a/bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml +++ b/bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml @@ -472,6 +472,16 @@ spec: - name: OVNKUBE_NODE_MGMT_PORT_DP_RESOURCE_NAME value: {{ .MgmtPortResourceName }} {{ end }} + {{ if or (eq .OVN_NODE_MODE "dpu-host") (eq .OVN_NODE_MODE "dpu") }} + - name: OVNKUBE_NODE_LEASE_RENEW_INTERVAL + value: "{{.DpuNodeLeaseRenewInterval}}" + - name: OVNKUBE_NODE_LEASE_DURATION + value: "{{.DpuNodeLeaseDuration}}" + - name: OVNKUBE_NODE_LEASE_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{ end }} - name: K8S_NODE valueFrom: fieldRef: diff --git a/hack/hardware-offload-config.yaml b/hack/hardware-offload-config.yaml index 0805bcad89..1c8f2645fe 100644 --- a/hack/hardware-offload-config.yaml +++ b/hack/hardware-offload-config.yaml @@ -12,3 +12,5 @@ data: dpu-mode-label: "network.operator.openshift.io/dpu=" smart-nic-mode-label: "network.operator.openshift.io/smart-nic=" mgmt-port-resource-name: "openshift.io/mgmtvf" + dpu-node-lease-renew-interval: "10" + dpu-node-lease-duration: "40" diff --git a/pkg/bootstrap/types.go b/pkg/bootstrap/types.go index 1eec6429fe..2df2153acc 100644 --- a/pkg/bootstrap/types.go +++ b/pkg/bootstrap/types.go @@ -30,18 +30,20 @@ type OVNHyperShiftBootstrapResult struct { } type OVNConfigBoostrapResult struct { - GatewayMode string - HyperShiftConfig *OVNHyperShiftBootstrapResult - DisableUDPAggregation bool - DpuHostModeLabel string - DpuHostModeNodes []string - DpuHostModeValue string - DpuModeLabel string - DpuModeNodes []string - SmartNicModeLabel string - SmartNicModeNodes []string - SmartNicModeValue string - MgmtPortResourceName string + GatewayMode string + HyperShiftConfig *OVNHyperShiftBootstrapResult + DisableUDPAggregation bool + DpuHostModeLabel string + DpuHostModeNodes []string + DpuHostModeValue string + DpuModeLabel string + DpuModeNodes []string + SmartNicModeLabel string + SmartNicModeNodes []string + SmartNicModeValue string + MgmtPortResourceName string + DpuNodeLeaseRenewInterval int + DpuNodeLeaseDuration int // ConfigOverrides contains the overrides for the OVN Kubernetes configuration // This is used to set the hidden OVN Kubernetes configuration in the cluster // It is a map of key-value pairs where the key is the configuration option and the diff --git a/pkg/network/kube_proxy_test.go b/pkg/network/kube_proxy_test.go index fdd74a5567..c5742194c8 100644 --- a/pkg/network/kube_proxy_test.go +++ b/pkg/network/kube_proxy_test.go @@ -374,10 +374,12 @@ func TestFillKubeProxyDefaults(t *testing.T) { var FakeKubeProxyBootstrapResult = bootstrap.BootstrapResult{ OVN: bootstrap.OVNBootstrapResult{ OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, }, }, } diff --git a/pkg/network/ovn_kubernetes.go b/pkg/network/ovn_kubernetes.go index 0ba767b963..84ab010257 100644 --- a/pkg/network/ovn_kubernetes.go +++ b/pkg/network/ovn_kubernetes.go @@ -64,6 +64,11 @@ const OVN_NODE_SELECTOR_DEFAULT_DPU = "network.operator.openshift.io/dpu=" const OVN_NODE_SELECTOR_DEFAULT_SMART_NIC = "network.operator.openshift.io/smart-nic=" const OVN_NODE_IDENTITY_CERT_DURATION = "24h" +// Default DPU health check lease configuration. +// Setting either to 0 disables the health check. +const DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT = 10 +const DPU_NODE_LEASE_DURATION_DEFAULT = 40 + // gRPC healthcheck port. See: https://github.com/openshift/enhancements/pull/1209 const OVN_EGRESSIP_HEALTHCHECK_PORT = "9107" @@ -218,6 +223,8 @@ func renderOVNKubernetes(conf *operv1.NetworkSpec, bootstrapResult *bootstrap.Bo data.Data["SmartNicModeLabel"] = bootstrapResult.OVN.OVNKubernetesConfig.SmartNicModeLabel data.Data["SmartNicModeValue"] = bootstrapResult.OVN.OVNKubernetesConfig.SmartNicModeValue data.Data["MgmtPortResourceName"] = bootstrapResult.OVN.OVNKubernetesConfig.MgmtPortResourceName + data.Data["DpuNodeLeaseRenewInterval"] = strconv.Itoa(bootstrapResult.OVN.OVNKubernetesConfig.DpuNodeLeaseRenewInterval) + data.Data["DpuNodeLeaseDuration"] = strconv.Itoa(bootstrapResult.OVN.OVNKubernetesConfig.DpuNodeLeaseDuration) data.Data["OVN_CONTROLLER_INACTIVITY_PROBE"] = os.Getenv("OVN_CONTROLLER_INACTIVITY_PROBE") controller_inactivity_probe := os.Getenv("OVN_CONTROLLER_INACTIVITY_PROBE") if len(controller_inactivity_probe) == 0 { @@ -927,10 +934,12 @@ func findCommonNode(nodeLists ...[]string) (bool, string) { // if it exists, otherwise returns default configuration for OCP clusters using OVN-Kubernetes func bootstrapOVNConfig(conf *operv1.Network, kubeClient cnoclient.Client, hc *hypershift.HyperShiftConfig, infraStatus *bootstrap.InfraStatus) (*bootstrap.OVNConfigBoostrapResult, error) { ovnConfigResult := &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, } if conf.Spec.DefaultNetwork.OVNKubernetesConfig.GatewayConfig == nil { bootstrapOVNGatewayConfig(conf, kubeClient.ClientFor("").CRClient()) @@ -976,6 +985,33 @@ func bootstrapOVNConfig(conf *operv1.Network, kubeClient cnoclient.Client, hc *h if exists { ovnConfigResult.MgmtPortResourceName = mgmtPortresourceName } + + if val, exists := cm.Data["dpu-node-lease-renew-interval"]; exists { + parsed, err := strconv.Atoi(val) + if err == nil && parsed >= 0 { + ovnConfigResult.DpuNodeLeaseRenewInterval = parsed + } else { + klog.Warningf("Invalid dpu-node-lease-renew-interval %q, using default %d", val, DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT) + } + } + if val, exists := cm.Data["dpu-node-lease-duration"]; exists { + parsed, err := strconv.Atoi(val) + if err == nil && parsed >= 0 { + ovnConfigResult.DpuNodeLeaseDuration = parsed + } else { + klog.Warningf("Invalid dpu-node-lease-duration %q, using default %d", val, DPU_NODE_LEASE_DURATION_DEFAULT) + } + } + + // Setting either value to 0 disables the DPU health check. + // When both are non-zero, duration must be greater than interval. + if ovnConfigResult.DpuNodeLeaseRenewInterval != 0 && ovnConfigResult.DpuNodeLeaseDuration != 0 && + ovnConfigResult.DpuNodeLeaseDuration <= ovnConfigResult.DpuNodeLeaseRenewInterval { + klog.Warningf("dpu-node-lease-duration (%d) must be greater than dpu-node-lease-renew-interval (%d), using defaults", + ovnConfigResult.DpuNodeLeaseDuration, ovnConfigResult.DpuNodeLeaseRenewInterval) + ovnConfigResult.DpuNodeLeaseRenewInterval = DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT + ovnConfigResult.DpuNodeLeaseDuration = DPU_NODE_LEASE_DURATION_DEFAULT + } } // We want to see if there are any nodes that are labeled for specific modes such as Full/SmartNIC/DPU Host/DPU diff --git a/pkg/network/ovn_kubernetes_dpu_host_test.go b/pkg/network/ovn_kubernetes_dpu_host_test.go index 3b8aef7bf8..c87d726725 100644 --- a/pkg/network/ovn_kubernetes_dpu_host_test.go +++ b/pkg/network/ovn_kubernetes_dpu_host_test.go @@ -1,6 +1,7 @@ package network import ( + "strconv" "testing" "github.com/ghodss/yaml" @@ -160,6 +161,8 @@ func createTestRenderData(ovnNodeMode string) render.RenderData { data.Data["SmartNicModeValue"] = "" data.Data["DpuModeLabel"] = "" data.Data["MgmtPortResourceName"] = "" + data.Data["DpuNodeLeaseRenewInterval"] = strconv.Itoa(DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT) + data.Data["DpuNodeLeaseDuration"] = strconv.Itoa(DPU_NODE_LEASE_DURATION_DEFAULT) data.Data["HTTP_PROXY"] = "" data.Data["HTTPS_PROXY"] = "" data.Data["NO_PROXY"] = "" @@ -210,6 +213,126 @@ func getMatchExpression(g *WithT, ds *appsv1.DaemonSet, label string) (corev1.No return corev1.NodeSelectorOpDoesNotExist, "" } +// TestOVNKubernetesLeaseEnvVars tests that DPU lease env vars are set +// for DPU and DPU-host modes but not for full mode +func TestOVNKubernetesLeaseEnvVars(t *testing.T) { + templates := []struct { + name string + templatePath string + }{ + { + name: "managed", + templatePath: "../../bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml", + }, + { + name: "self-hosted", + templatePath: "../../bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml", + }, + } + + testCases := []struct { + name string + ovnNodeMode string + expectSet bool + }{ + { + name: "full mode should not have lease env vars", + ovnNodeMode: "full", + expectSet: false, + }, + { + name: "dpu-host mode should have lease env vars", + ovnNodeMode: "dpu-host", + expectSet: true, + }, + { + name: "dpu mode should have lease env vars", + ovnNodeMode: "dpu", + expectSet: true, + }, + } + + // Env vars with literal values + leaseEnvVars := map[string]string{ + "OVNKUBE_NODE_LEASE_RENEW_INTERVAL": strconv.Itoa(DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT), + "OVNKUBE_NODE_LEASE_DURATION": strconv.Itoa(DPU_NODE_LEASE_DURATION_DEFAULT), + } + // Env vars using fieldRef (no literal value) + leaseFieldRefEnvVars := []string{ + "OVNKUBE_NODE_LEASE_NAMESPACE", + } + + for _, template := range templates { + for _, tc := range testCases { + testName := template.name + "_" + tc.name + t.Run(testName, func(t *testing.T) { + g := NewGomegaWithT(t) + + data := createTestRenderData(tc.ovnNodeMode) + + objs, err := render.RenderTemplate(template.templatePath, &data) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(objs).To(HaveLen(1)) + + yamlBytes, err := yaml.Marshal(objs[0]) + g.Expect(err).NotTo(HaveOccurred()) + + ds := &appsv1.DaemonSet{} + err = yaml.Unmarshal(yamlBytes, ds) + g.Expect(err).NotTo(HaveOccurred()) + + // Check env vars with literal values + for envName, expectedValue := range leaseEnvVars { + found := false + for _, container := range ds.Spec.Template.Spec.Containers { + for _, env := range container.Env { + if env.Name == envName { + found = true + g.Expect(env.Value).To(Equal(expectedValue), + "%s should be set to %s", envName, expectedValue) + } + } + } + + if tc.expectSet { + g.Expect(found).To(BeTrue(), + "%s should be set for %s mode", envName, tc.ovnNodeMode) + } else { + g.Expect(found).To(BeFalse(), + "%s should not be set for %s mode", envName, tc.ovnNodeMode) + } + } + + // Check env vars using fieldRef + for _, envName := range leaseFieldRefEnvVars { + found := false + for _, container := range ds.Spec.Template.Spec.Containers { + for _, env := range container.Env { + if env.Name == envName { + found = true + g.Expect(env.ValueFrom).NotTo(BeNil(), + "%s should use valueFrom", envName) + g.Expect(env.ValueFrom.FieldRef).NotTo(BeNil(), + "%s should use fieldRef", envName) + g.Expect(env.ValueFrom.FieldRef.FieldPath).To(Equal("metadata.namespace"), + "%s fieldRef should reference metadata.namespace", envName) + } + } + } + + if tc.expectSet { + g.Expect(found).To(BeTrue(), + "%s should be set for %s mode", envName, tc.ovnNodeMode) + } else { + g.Expect(found).To(BeFalse(), + "%s should not be set for %s mode", envName, tc.ovnNodeMode) + } + } + }) + } + } +} + // TestOVNKubernetesNodeSelectorOperator tests that the node selector operator works correctly with label values of different Full/SmartNIC/DPU modes func TestOVNKubernetesNodeSelectorOperator(t *testing.T) { templates := []struct { diff --git a/pkg/network/ovn_kubernetes_test.go b/pkg/network/ovn_kubernetes_test.go index 68d5920e29..248079434e 100644 --- a/pkg/network/ovn_kubernetes_test.go +++ b/pkg/network/ovn_kubernetes_test.go @@ -95,10 +95,12 @@ func TestRenderOVNKubernetes(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -219,10 +221,12 @@ func TestRenderOVNKubernetesIPv6(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -240,10 +244,12 @@ func TestRenderOVNKubernetesIPv6(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -1046,10 +1052,12 @@ logfile-maxage=0`, bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: tc.controlPlaneReplicaCount, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -2207,10 +2215,12 @@ metadata: ControlPlaneUpdateStatus: controlPlaneStatus, NodeUpdateStatus: nodeStatus, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -2512,10 +2522,12 @@ func TestRenderOVNKubernetesEnableIPsec(t *testing.T) { IPFamilyMode: names.IPFamilySingleStack, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -2739,10 +2751,12 @@ func TestRenderOVNKubernetesEnableIPsecForHostedControlPlane(t *testing.T) { IPFamilyMode: names.IPFamilySingleStack, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -2840,10 +2854,12 @@ func TestRenderOVNKubernetesIPsecUpgradeWithMachineConfig(t *testing.T) { IsOVNIPsecActiveOrRollingOut: true, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -2952,10 +2968,12 @@ func TestRenderOVNKubernetesIPsecUpgradeWithNoMachineConfig(t *testing.T) { IsOVNIPsecActiveOrRollingOut: true, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -3100,10 +3118,12 @@ func TestRenderOVNKubernetesIPsecUpgradeWithHypershiftHostedCluster(t *testing.T IsOVNIPsecActiveOrRollingOut: true, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -3203,10 +3223,12 @@ func TestRenderOVNKubernetesDisableIPsec(t *testing.T) { IsOVNIPsecActiveOrRollingOut: true, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -3413,10 +3435,12 @@ func TestRenderOVNKubernetesEnableIPsecWithUserInstalledIPsecMachineConfigs(t *t IPFamilyMode: names.IPFamilySingleStack, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -3559,10 +3583,12 @@ func TestRenderOVNKubernetesDisableIPsecWithUserInstalledIPsecMachineConfigs(t * IsOVNIPsecActiveOrRollingOut: true, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -3698,10 +3724,12 @@ func TestRenderOVNKubernetesDualStackPrecedenceOverUpgrade(t *testing.T) { IPFamilyMode: names.IPFamilySingleStack, }, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -3941,10 +3969,12 @@ func TestRenderOVNKubernetesEnablePersistentIPs(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -4117,10 +4147,12 @@ func Test_renderOVNKubernetes(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -4252,10 +4284,12 @@ func TestRenderOVNKubernetes_AdvertisedUDNIsolationModeOverride(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -4293,10 +4327,12 @@ func TestRenderOVNKubernetes_OpenFlowProbeOverride(t *testing.T) { bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ ControlPlaneReplicaCount: 3, OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ - DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, - DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, - SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, - MgmtPortResourceName: "", + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ Enabled: false, }, @@ -4326,3 +4362,151 @@ func TestRenderOVNKubernetes_OpenFlowProbeOverride(t *testing.T) { g.Expect(ovnkubeScriptLib).To(ContainSubstring(`--openflow-probe="`)) }) } + +func TestDpuLeaseConfigRendering(t *testing.T) { + g := NewGomegaWithT(t) + + crd := OVNKubernetesConfig.DeepCopy() + config := &crd.Spec + errs := validateOVNKubernetes(config) + g.Expect(errs).To(HaveLen(0)) + fillDefaults(config, nil) + + bootstrapResult := fakeBootstrapResult() + bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ + ControlPlaneReplicaCount: 3, + OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: 15, + DpuNodeLeaseDuration: 60, + DpuHostModeNodes: []string{"dpu-host-node-1"}, + HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ + Enabled: false, + }, + }, + } + + featureGatesCNO := getDefaultFeatureGates() + fakeClient := cnofake.NewFakeClient() + objs, _, err := renderOVNKubernetes(config, bootstrapResult, manifestDirOvn, fakeClient, featureGatesCNO) + g.Expect(err).NotTo(HaveOccurred()) + + envVars := extractDaemonSetEnvVars(g, objs, "ovnkube-node-dpu-host", "ovnkube-controller") + g.Expect(envVars["OVNKUBE_NODE_LEASE_RENEW_INTERVAL"]).To(Equal("15")) + g.Expect(envVars["OVNKUBE_NODE_LEASE_DURATION"]).To(Equal("60")) + _, hasNamespace := envVars["OVNKUBE_NODE_LEASE_NAMESPACE"] + g.Expect(hasNamespace).To(BeTrue(), "OVNKUBE_NODE_LEASE_NAMESPACE env var should be present") +} + +func TestDpuLeaseConfigDefaults(t *testing.T) { + g := NewGomegaWithT(t) + + crd := OVNKubernetesConfig.DeepCopy() + config := &crd.Spec + errs := validateOVNKubernetes(config) + g.Expect(errs).To(HaveLen(0)) + fillDefaults(config, nil) + + bootstrapResult := fakeBootstrapResult() + bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ + ControlPlaneReplicaCount: 3, + OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: DPU_NODE_LEASE_RENEW_INTERVAL_DEFAULT, + DpuNodeLeaseDuration: DPU_NODE_LEASE_DURATION_DEFAULT, + DpuHostModeNodes: []string{"dpu-host-node-1"}, + HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ + Enabled: false, + }, + }, + } + + featureGatesCNO := getDefaultFeatureGates() + fakeClient := cnofake.NewFakeClient() + objs, _, err := renderOVNKubernetes(config, bootstrapResult, manifestDirOvn, fakeClient, featureGatesCNO) + g.Expect(err).NotTo(HaveOccurred()) + + envVars := extractDaemonSetEnvVars(g, objs, "ovnkube-node-dpu-host", "ovnkube-controller") + g.Expect(envVars["OVNKUBE_NODE_LEASE_RENEW_INTERVAL"]).To(Equal("10")) + g.Expect(envVars["OVNKUBE_NODE_LEASE_DURATION"]).To(Equal("40")) + _, hasNamespace := envVars["OVNKUBE_NODE_LEASE_NAMESPACE"] + g.Expect(hasNamespace).To(BeTrue(), "OVNKUBE_NODE_LEASE_NAMESPACE env var should be present") +} + +func TestDpuLeaseConfigZeroDisables(t *testing.T) { + g := NewGomegaWithT(t) + + crd := OVNKubernetesConfig.DeepCopy() + config := &crd.Spec + errs := validateOVNKubernetes(config) + g.Expect(errs).To(HaveLen(0)) + fillDefaults(config, nil) + + bootstrapResult := fakeBootstrapResult() + bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ + ControlPlaneReplicaCount: 3, + OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + DpuNodeLeaseRenewInterval: 0, + DpuNodeLeaseDuration: 0, + DpuHostModeNodes: []string{"dpu-host-node-1"}, + HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ + Enabled: false, + }, + }, + } + + featureGatesCNO := getDefaultFeatureGates() + fakeClient := cnofake.NewFakeClient() + objs, _, err := renderOVNKubernetes(config, bootstrapResult, manifestDirOvn, fakeClient, featureGatesCNO) + g.Expect(err).NotTo(HaveOccurred()) + + // Timer=0 should render as "0", not fall back to defaults + envVars := extractDaemonSetEnvVars(g, objs, "ovnkube-node-dpu-host", "ovnkube-controller") + g.Expect(envVars["OVNKUBE_NODE_LEASE_RENEW_INTERVAL"]).To(Equal("0")) + g.Expect(envVars["OVNKUBE_NODE_LEASE_DURATION"]).To(Equal("0")) +} + +// extractDaemonSetEnvVars finds a DaemonSet by name in the rendered objects and returns +// env vars for the specified container as a map. +func extractDaemonSetEnvVars(g *WithT, objs []*uns.Unstructured, dsName, containerName string) map[string]string { + envVars := map[string]string{} + for _, obj := range objs { + if obj.GetKind() != "DaemonSet" || obj.GetName() != dsName { + continue + } + 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 { + cmap := c.(map[string]interface{}) + name, _, _ := uns.NestedString(cmap, "name") + if name != containerName { + continue + } + envList, found, err := uns.NestedSlice(cmap, "env") + g.Expect(err).NotTo(HaveOccurred()) + if !found { + return envVars + } + for _, e := range envList { + emap := e.(map[string]interface{}) + eName, _, _ := uns.NestedString(emap, "name") + eVal, _, _ := uns.NestedString(emap, "value") + envVars[eName] = eVal + } + return envVars + } + } + g.Expect(true).To(BeFalse(), "could not find DaemonSet %s with container %s", dsName, containerName) + return envVars +}