Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/ccoveille/go-safecast v1.8.2
github.com/kubevirt/device-plugin-manager v1.18.8
gopkg.in/yaml.v2 v2.4.0
gotest.tools/v3 v3.5.2
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down Expand Up @@ -287,6 +287,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY=
Expand Down
103 changes: 103 additions & 0 deletions internal/pkg/api/device/device_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
Copyright 2025 The HAMi Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package device

import (
"errors"
"testing"

"gotest.tools/v3/assert"
)

func Test_DecodeNodeDevices(t *testing.T) {
tests := []struct {
name string
args string
want struct {
di []*DeviceInfo
err error
}
}{
{
name: "args is invalid",
args: "a",
want: struct {
di []*DeviceInfo
err error
}{
di: []*DeviceInfo{},
err: errors.New("node annotations not decode successfully"),
},
},
{
name: "str is old format",
args: "GPU-ebe7c3f7-303d-558d-435e-99a160631fe4,10,7680,100,NVIDIA-Tesla P4,0,true:",
want: struct {
di []*DeviceInfo
err error
}{
di: []*DeviceInfo{
{
ID: "GPU-ebe7c3f7-303d-558d-435e-99a160631fe4",
Index: 0,
Count: 10,
Devmem: 7680,
Devcore: 100,
Type: "NVIDIA-Tesla P4",
Mode: "hami-core",
Numa: 0,
Health: true,
},
},
err: nil,
},
},
{
name: "str is new format",
args: "GPU-ebe7c3f7-303d-558d-435e-99a160631fe4,10,7680,100,NVIDIA-Tesla P4,0,true,1,hami-core:",
want: struct {
di []*DeviceInfo
err error
}{
di: []*DeviceInfo{
{
ID: "GPU-ebe7c3f7-303d-558d-435e-99a160631fe4",
Index: 1,
Count: 10,
Devmem: 7680,
Devcore: 100,
Type: "NVIDIA-Tesla P4",
Mode: "hami-core",
Numa: 0,
Health: true,
},
},
err: nil,
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got, err := DecodeNodeDevices(test.args)
assert.DeepEqual(t, test.want.di, got)
if err != nil {
assert.DeepEqual(t, test.want.err.Error(), err.Error())
}
})
}
}
8 changes: 6 additions & 2 deletions internal/pkg/api/device/nvidia/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,12 @@ func (dev *NvidiaGPUDevices) GetNodeDevices(n corev1.Node) ([]*device.DeviceInfo
}
nodedevices, err := device.UnMarshalNodeDevices(devEncoded)
if err != nil {
klog.ErrorS(err, "failed to decode node devices", "node", n.Name, "device annotation", devEncoded)
return []*device.DeviceInfo{}, err
klog.Infof("decode error. try to decode with old method. error %s", err.Error())
nodedevices, err = device.DecodeNodeDevices(devEncoded)
if err != nil {
klog.ErrorS(err, "failed to decode node devices", "node", n.Name, "device annotation", devEncoded)
return []*device.DeviceInfo{}, err
}
}
if len(nodedevices) == 0 {
klog.InfoS("no nvidia gpu device found", "node", n.Name, "device annotation", devEncoded)
Expand Down
115 changes: 114 additions & 1 deletion internal/pkg/api/device/nvidia/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -102,3 +102,116 @@ func TestAddResource(t *testing.T) {

})
}

func TestGetNodeDevices(t *testing.T) {
tests := []struct {
name string
setupNode func() corev1.Node
wantErr bool
wantDevices int
setupDev func() *NvidiaGPUDevices
}{
{
name: "no annotation",
setupNode: func() corev1.Node {
return corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-no-anno",
Annotations: map[string]string{},
},
}
},
wantErr: true,
wantDevices: 0,
setupDev: func() *NvidiaGPUDevices {
return &NvidiaGPUDevices{}
},
},
{
name: "invalid annotation format",
setupNode: func() corev1.Node {
return corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-bad-data",
Annotations: map[string]string{
RegisterAnnos: "invalid-data-format",
},
},
}
},
wantErr: true,
wantDevices: 0,
setupDev: func() *NvidiaGPUDevices {
return &NvidiaGPUDevices{}
},
},
{
name: "empty devices annotation",
setupNode: func() corev1.Node {
return corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-empty-devices",
Annotations: map[string]string{
RegisterAnnos: "",
},
},
}
},
wantErr: true,
wantDevices: 0,
setupDev: func() *NvidiaGPUDevices {
return &NvidiaGPUDevices{}
},
},
{
name: "old format",
setupNode: func() corev1.Node {
return corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-example",
Annotations: map[string]string{
RegisterAnnos: `GPU-f92d2cf4,10,81920,100,NVIDIA-NVIDIA A100-SXM4-80GB,1,true,6,hami-core:GPU-0d5a6e59,10,81920,100,NVIDIA-NVIDIA A100-SXM4-80GB,1,true,4,hami-core:GPU-da197561,10,81920,100,NVIDIA-NVIDIA A100-SXM4-80GB,1,true,5,hami-core:`,
},
},
}
},
wantErr: false,
wantDevices: 3,
setupDev: func() *NvidiaGPUDevices {
return &NvidiaGPUDevices{
config: NvidiaConfig{},
}
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

node := tt.setupNode()
dev := tt.setupDev()

devices, err := dev.GetNodeDevices(node)

if (err != nil) != tt.wantErr {
t.Errorf("GetNodeDevices() error = %v, wantErr %v", err, tt.wantErr)
return
}

if !tt.wantErr && len(devices) != tt.wantDevices {
t.Errorf("GetNodeDevices() returned %d devices, want %d", len(devices), tt.wantDevices)
}

if !tt.wantErr && len(devices) > 0 {
for _, d := range devices {
if d.Devmem == 0 {
t.Error("Devmem should not be zero")
}
if d.Devcore == 0 {
t.Error("Devcore should not be zero")
}
}
}
})
}
}
Loading