Skip to content

Commit d1c101c

Browse files
committed
MGMT-21314: CNO enable advanced gateway detection in ovnkube in dpu host mode
Adding required gateway value for ovnk in dpu-host mode This commit enables usage of ovn-kubernetes/ovn-kubernetes#5327
1 parent 027d46e commit d1c101c

File tree

4 files changed

+333
-10
lines changed

4 files changed

+333
-10
lines changed

bindata/network/ovn-kubernetes/common/008-script-lib.yaml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -512,12 +512,19 @@ data:
512512

513513
echo "I$(date "+%m%d %H:%M:%S.%N") - starting ovnkube-node"
514514

515-
516-
if [ "{{.OVN_NODE_MODE}}" == "dpu-host" ]; then
517-
// this is required for the dpu-host mode to configure right gateway interface
518-
// https://github.com/ovn-kubernetes/ovn-kubernetes/pull/5327/files
519-
gateway_interface=derive-from-mgmt-port
515+
# Use OVN_NODE_MODE environment variable, default to "full" if not set
516+
OVN_NODE_MODE=${OVN_NODE_MODE:-full}
517+
# We check only dpu-host mode and not smart-nic mode here as currently we do not support it yet
518+
# Once we support it, we will need to check for it here and add relevant code.
519+
if [ "${OVN_NODE_MODE}" == "dpu-host" ]; then
520+
# this is required for the dpu-host mode to configure right gateway interface
521+
# https://github.com/ovn-kubernetes/ovn-kubernetes/pull/5327/files
522+
gateway_interface="derive-from-mgmt-port"
523+
ovnkube_node_mode="--ovnkube-node-mode dpu-host"
520524
else
525+
# init-ovnkube-controller is not supported in dpu-host mode
526+
# as there is no databases to connect to
527+
init_ovnkube_controller="--init-ovnkube-controller ${K8S_NODE}"
521528
gateway_interface=br-ex
522529
fi
523530

@@ -656,17 +663,15 @@ data:
656663
fi
657664

658665
exec /usr/bin/ovnkube \
659-
--init-ovnkube-controller "${K8S_NODE}" \
666+
${init_ovnkube_controller} \
660667
--init-node "${K8S_NODE}" \
661668
--config-file=/run/ovnkube-config/ovnkube.conf \
662669
--ovn-empty-lb-events \
663670
--loglevel "${log_level}" \
664671
--inactivity-probe="${OVN_CONTROLLER_INACTIVITY_PROBE}" \
665672
${gateway_mode_flags} \
666673
${node_mgmt_port_netdev_flags} \
667-
{{- if eq .OVN_NODE_MODE "dpu-host" }}
668-
--ovnkube-node-mode dpu-host \
669-
{{- end }}
674+
${ovnkube_node_mode} \
670675
--metrics-bind-address "127.0.0.1:${metrics_port}" \
671676
--ovn-metrics-bind-address "127.0.0.1:${ovn_metrics_port}" \
672677
--metrics-enable-pprof \

bindata/network/ovn-kubernetes/managed/ovnkube-node.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,8 @@ spec:
390390
value: "{{.OVN_CONTROLLER_INACTIVITY_PROBE}}"
391391
- name: OVN_KUBE_LOG_LEVEL
392392
value: "4"
393+
- name: OVN_NODE_MODE
394+
value: "{{.OVN_NODE_MODE}}"
393395
{{ if .NetFlowCollectors }}
394396
- name: NETFLOW_COLLECTORS
395397
value: "{{.NetFlowCollectors}}"

bindata/network/ovn-kubernetes/self-hosted/ovnkube-node.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@ spec:
534534
value: "{{.OVN_CONTROLLER_INACTIVITY_PROBE}}"
535535
- name: OVN_KUBE_LOG_LEVEL
536536
value: "4"
537+
- name: OVN_NODE_MODE
538+
value: "{{.OVN_NODE_MODE}}"
537539
{{ if .NetFlowCollectors }}
538540
- name: NETFLOW_COLLECTORS
539541
value: "{{.NetFlowCollectors}}"

pkg/network/ovn_kubernetes_test.go

Lines changed: 315 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4280,7 +4280,321 @@ func TestOVNKubernetesScriptLibGatewayInterface(t *testing.T) {
42804280
// Validate that gateway_mode_flags uses the variable
42814281
g.Expect(scriptData).To(ContainSubstring("--gateway-interface ${gateway_interface}"),
42824282
"Script should use gateway_interface variable in gateway_mode_flags")
4283-
4283+
4284+
})
4285+
}
4286+
}
4287+
4288+
func TestGetNodeListByLabel(t *testing.T) {
4289+
tests := []struct {
4290+
name string
4291+
labelSelector string
4292+
nodes []v1.Node
4293+
expectedNodes []string
4294+
expectError bool
4295+
errorOnList bool
4296+
}{
4297+
{
4298+
name: "nodes with specific label and value",
4299+
labelSelector: "feature.node.kubernetes.io/dpu-enabled=true",
4300+
nodes: []v1.Node{
4301+
{
4302+
ObjectMeta: metav1.ObjectMeta{
4303+
Name: "node1",
4304+
Labels: map[string]string{
4305+
"feature.node.kubernetes.io/dpu-enabled": "true",
4306+
},
4307+
},
4308+
},
4309+
{
4310+
ObjectMeta: metav1.ObjectMeta{
4311+
Name: "node2",
4312+
Labels: map[string]string{
4313+
"feature.node.kubernetes.io/dpu-enabled": "false",
4314+
},
4315+
},
4316+
},
4317+
{
4318+
ObjectMeta: metav1.ObjectMeta{
4319+
Name: "node3",
4320+
Labels: map[string]string{},
4321+
},
4322+
},
4323+
},
4324+
expectedNodes: []string{"node1"},
4325+
expectError: false,
4326+
},
4327+
{
4328+
name: "nodes with label key (any value)",
4329+
labelSelector: "feature.node.kubernetes.io/dpu-enabled",
4330+
nodes: []v1.Node{
4331+
{
4332+
ObjectMeta: metav1.ObjectMeta{
4333+
Name: "node1",
4334+
Labels: map[string]string{
4335+
"feature.node.kubernetes.io/dpu-enabled": "true",
4336+
},
4337+
},
4338+
},
4339+
{
4340+
ObjectMeta: metav1.ObjectMeta{
4341+
Name: "node2",
4342+
Labels: map[string]string{
4343+
"feature.node.kubernetes.io/dpu-enabled": "false",
4344+
},
4345+
},
4346+
},
4347+
{
4348+
ObjectMeta: metav1.ObjectMeta{
4349+
Name: "node3",
4350+
Labels: map[string]string{},
4351+
},
4352+
},
4353+
},
4354+
expectedNodes: []string{"node1", "node2"},
4355+
expectError: false,
4356+
},
4357+
{
4358+
name: "nodes with label key only (no equals)",
4359+
labelSelector: "feature.node.kubernetes.io/dpu-enabled",
4360+
nodes: []v1.Node{
4361+
{
4362+
ObjectMeta: metav1.ObjectMeta{
4363+
Name: "node1",
4364+
Labels: map[string]string{
4365+
"feature.node.kubernetes.io/dpu-enabled": "true",
4366+
},
4367+
},
4368+
},
4369+
{
4370+
ObjectMeta: metav1.ObjectMeta{
4371+
Name: "node2",
4372+
Labels: map[string]string{
4373+
"feature.node.kubernetes.io/dpu-enabled": "false",
4374+
},
4375+
},
4376+
},
4377+
{
4378+
ObjectMeta: metav1.ObjectMeta{
4379+
Name: "node3",
4380+
Labels: map[string]string{},
4381+
},
4382+
},
4383+
},
4384+
expectedNodes: []string{"node1", "node2"},
4385+
expectError: false,
4386+
},
4387+
{
4388+
name: "no nodes with matching label",
4389+
labelSelector: "feature.node.kubernetes.io/dpu-enabled=true",
4390+
nodes: []v1.Node{
4391+
{
4392+
ObjectMeta: metav1.ObjectMeta{
4393+
Name: "node1",
4394+
Labels: map[string]string{
4395+
"feature.node.kubernetes.io/dpu-enabled": "false",
4396+
},
4397+
},
4398+
},
4399+
{
4400+
ObjectMeta: metav1.ObjectMeta{
4401+
Name: "node2",
4402+
Labels: map[string]string{},
4403+
},
4404+
},
4405+
},
4406+
expectedNodes: []string{},
4407+
expectError: false,
4408+
},
4409+
{
4410+
name: "no nodes at all",
4411+
labelSelector: "feature.node.kubernetes.io/dpu-enabled=true",
4412+
nodes: []v1.Node{},
4413+
expectedNodes: []string{},
4414+
expectError: false,
4415+
},
4416+
{
4417+
name: "multiple nodes with different DPU labels",
4418+
labelSelector: "network.operator.openshift.io/dpu-host",
4419+
nodes: []v1.Node{
4420+
{
4421+
ObjectMeta: metav1.ObjectMeta{
4422+
Name: "dpu-host-1",
4423+
Labels: map[string]string{
4424+
"network.operator.openshift.io/dpu-host": "",
4425+
},
4426+
},
4427+
},
4428+
{
4429+
ObjectMeta: metav1.ObjectMeta{
4430+
Name: "dpu-host-2",
4431+
Labels: map[string]string{
4432+
"network.operator.openshift.io/dpu-host": "enabled",
4433+
},
4434+
},
4435+
},
4436+
{
4437+
ObjectMeta: metav1.ObjectMeta{
4438+
Name: "regular-node",
4439+
Labels: map[string]string{
4440+
"kubernetes.io/os": "linux",
4441+
},
4442+
},
4443+
},
4444+
},
4445+
expectedNodes: []string{"dpu-host-1", "dpu-host-2"},
4446+
expectError: false,
4447+
},
4448+
{
4449+
name: "nodes with empty label values",
4450+
labelSelector: "feature.node.kubernetes.io/empty-label",
4451+
nodes: []v1.Node{
4452+
{
4453+
ObjectMeta: metav1.ObjectMeta{
4454+
Name: "node-with-empty-value",
4455+
Labels: map[string]string{
4456+
"feature.node.kubernetes.io/empty-label": "",
4457+
},
4458+
},
4459+
},
4460+
{
4461+
ObjectMeta: metav1.ObjectMeta{
4462+
Name: "node-with-value",
4463+
Labels: map[string]string{
4464+
"feature.node.kubernetes.io/empty-label": "some-value",
4465+
},
4466+
},
4467+
},
4468+
{
4469+
ObjectMeta: metav1.ObjectMeta{
4470+
Name: "node-without-label",
4471+
Labels: map[string]string{
4472+
"other-label": "other-value",
4473+
},
4474+
},
4475+
},
4476+
},
4477+
expectedNodes: []string{"node-with-empty-value", "node-with-value"},
4478+
expectError: false,
4479+
},
4480+
{
4481+
name: "nodes with specific empty label value",
4482+
labelSelector: "feature.node.kubernetes.io/empty-label=",
4483+
nodes: []v1.Node{
4484+
{
4485+
ObjectMeta: metav1.ObjectMeta{
4486+
Name: "node-with-empty-value",
4487+
Labels: map[string]string{
4488+
"feature.node.kubernetes.io/empty-label": "",
4489+
},
4490+
},
4491+
},
4492+
{
4493+
ObjectMeta: metav1.ObjectMeta{
4494+
Name: "node-with-value",
4495+
Labels: map[string]string{
4496+
"feature.node.kubernetes.io/empty-label": "some-value",
4497+
},
4498+
},
4499+
},
4500+
{
4501+
ObjectMeta: metav1.ObjectMeta{
4502+
Name: "node-without-label",
4503+
Labels: map[string]string{
4504+
"other-label": "other-value",
4505+
},
4506+
},
4507+
},
4508+
},
4509+
expectedNodes: []string{"node-with-empty-value"},
4510+
expectError: false,
4511+
},
4512+
}
4513+
4514+
for _, tt := range tests {
4515+
t.Run(tt.name, func(t *testing.T) {
4516+
// Create fake client with test nodes - pass nodes to constructor
4517+
// so they're available in both the typed and controller-runtime clients
4518+
nodeObjs := make([]crclient.Object, len(tt.nodes))
4519+
for i, node := range tt.nodes {
4520+
nodeObjs[i] = &node
4521+
}
4522+
client := cnofake.NewFakeClient(nodeObjs...)
4523+
4524+
// Call function under test
4525+
result, err := getNodeListByLabel(client, tt.labelSelector)
4526+
4527+
// Verify results
4528+
if tt.expectError {
4529+
assert.Error(t, err)
4530+
assert.Nil(t, result)
4531+
} else {
4532+
assert.NoError(t, err)
4533+
assert.ElementsMatch(t, tt.expectedNodes, result)
4534+
}
42844535
})
42854536
}
42864537
}
4538+
4539+
func TestGetNodeListByLabel_RealWorldScenarios(t *testing.T) {
4540+
t.Run("OpenShift DPU node scenarios", func(t *testing.T) {
4541+
nodes := []v1.Node{
4542+
{
4543+
ObjectMeta: metav1.ObjectMeta{
4544+
Name: "master-1",
4545+
Labels: map[string]string{
4546+
"node-role.kubernetes.io/master": "",
4547+
"node-role.kubernetes.io/control-plane": "",
4548+
"network.operator.openshift.io/dpu-host": "",
4549+
"feature.node.kubernetes.io/dpu-enabled": "true",
4550+
},
4551+
},
4552+
},
4553+
{
4554+
ObjectMeta: metav1.ObjectMeta{
4555+
Name: "worker-1",
4556+
Labels: map[string]string{
4557+
"node-role.kubernetes.io/worker": "",
4558+
"network.operator.openshift.io/dpu": "",
4559+
"feature.node.kubernetes.io/dpu-enabled": "true",
4560+
},
4561+
},
4562+
},
4563+
{
4564+
ObjectMeta: metav1.ObjectMeta{
4565+
Name: "worker-2",
4566+
Labels: map[string]string{
4567+
"node-role.kubernetes.io/worker": "",
4568+
"kubernetes.io/os": "linux",
4569+
},
4570+
},
4571+
},
4572+
}
4573+
4574+
// Create fake client with test nodes - pass nodes to constructor
4575+
nodeObjs := make([]crclient.Object, len(nodes))
4576+
for i, node := range nodes {
4577+
nodeObjs[i] = &node
4578+
}
4579+
client := cnofake.NewFakeClient(nodeObjs...)
4580+
4581+
// Test various DPU-related label selectors
4582+
testCases := []struct {
4583+
selector string
4584+
expected []string
4585+
}{
4586+
{"feature.node.kubernetes.io/dpu-enabled=true", []string{"master-1", "worker-1"}},
4587+
{"feature.node.kubernetes.io/dpu-enabled", []string{"master-1", "worker-1"}},
4588+
{"network.operator.openshift.io/dpu-host", []string{"master-1"}},
4589+
{"network.operator.openshift.io/dpu", []string{"worker-1"}},
4590+
{"node-role.kubernetes.io/worker", []string{"worker-1", "worker-2"}},
4591+
{"nonexistent-label", []string{}},
4592+
}
4593+
4594+
for _, tc := range testCases {
4595+
result, err := getNodeListByLabel(client, tc.selector)
4596+
assert.NoError(t, err, "Failed for selector: %s", tc.selector)
4597+
assert.ElementsMatch(t, tc.expected, result, "Failed for selector: %s", tc.selector)
4598+
}
4599+
})
4600+
}

0 commit comments

Comments
 (0)