-
Notifications
You must be signed in to change notification settings - Fork 613
feat(scheduler): add node nouse gpuuuid function #1206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,15 +18,31 @@ package scheduler | |||||||||||||||||
|
|
||||||||||||||||||
| import ( | ||||||||||||||||||
| "fmt" | ||||||||||||||||||
| "strings" | ||||||||||||||||||
| "sync" | ||||||||||||||||||
|
|
||||||||||||||||||
| corev1 "k8s.io/api/core/v1" | ||||||||||||||||||
| "k8s.io/klog/v2" | ||||||||||||||||||
|
|
||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device/cambricon" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device/hygon" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device/kunlun" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device/metax" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device/mthreads" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/device/nvidia" | ||||||||||||||||||
| "github.com/Project-HAMi/HAMi/pkg/scheduler/policy" | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| var vendorNoUseAnnoKeyMap = map[string][]string{ | ||||||||||||||||||
| nvidia.GPUNoUseUUID: {nvidia.NvidiaGPUDevice}, | ||||||||||||||||||
| cambricon.MLUNoUseUUID: {cambricon.CambriconMLUDevice}, | ||||||||||||||||||
| hygon.DCUNoUseUUID: {hygon.HygonDCUDevice}, | ||||||||||||||||||
| mthreads.MthreadsNoUseUUID: {mthreads.MthreadsGPUDevice}, | ||||||||||||||||||
| metax.MetaxNoUseUUID: {metax.MetaxGPUDevice, metax.MetaxSGPUDevice}, | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The constants
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a gemini problem. You can find those constants in pkg/device/metax/protocol.go and pkg/device/metax/sdevice.go easily. Because metax contributors don't put all constants in one files. |
||||||||||||||||||
| kunlun.KunlunNoUseUUID: {kunlun.KunlunGPUDevice}, | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+38
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this map works, it introduces a tight coupling between the scheduler package and all specific device packages ( A more decoupled and maintainable approach would be to have each device implementation provide this information. For example, you could add a method to the // In pkg/device/devices.go
type Devices interface {
// ...
GetNoUseAnnotation() (key string, vendors []string)
}The scheduler could then build this map dynamically at startup by iterating over the registered device implementations. This would make the system more extensible. |
||||||||||||||||||
|
|
||||||||||||||||||
| type NodeUsage struct { | ||||||||||||||||||
| Node *corev1.Node | ||||||||||||||||||
| Devices policy.DeviceUsageList | ||||||||||||||||||
|
|
@@ -60,6 +76,51 @@ func (m *nodeManager) addNode(nodeID string, nodeInfo *device.NodeInfo) { | |||||||||||||||||
| } else { | ||||||||||||||||||
| m.nodes[nodeID] = nodeInfo | ||||||||||||||||||
| } | ||||||||||||||||||
| m.nodes[nodeID].Devices = rmDeviceByNodeAnnotation(m.nodes[nodeID]) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func rmDeviceByNodeAnnotation(nodeInfo *device.NodeInfo) map[string][]device.DeviceInfo { | ||||||||||||||||||
| if nodeInfo == nil { | ||||||||||||||||||
| return nil | ||||||||||||||||||
| } | ||||||||||||||||||
| vendorWithDisableGPUUUIDMap := make(map[string]map[string]bool) | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function if nodeInfo == nil {
return nil
}
vendorWithDisableGPUUUIDMap := make(map[string]map[string]bool) |
||||||||||||||||||
| if nodeInfo.Node != nil && nodeInfo.Node.Annotations != nil { | ||||||||||||||||||
| for annoKey, vendors := range vendorNoUseAnnoKeyMap { | ||||||||||||||||||
| klog.V(5).Infof("Current annokey is %s, and vendor is %v", annoKey, vendors) | ||||||||||||||||||
| if value, ok := nodeInfo.Node.Annotations[annoKey]; ok { | ||||||||||||||||||
| disableGPUUUIDList := strings.Split(value, ",") | ||||||||||||||||||
| klog.V(5).Infof("Disable gpu uuid list is: %v", disableGPUUUIDList) | ||||||||||||||||||
| for _, disableGPUUUID := range disableGPUUUIDList { | ||||||||||||||||||
| if id := strings.TrimSpace(disableGPUUUID); id != "" { | ||||||||||||||||||
| for _, vendor := range vendors { | ||||||||||||||||||
| if vendorWithDisableGPUUUIDMap[vendor] == nil { | ||||||||||||||||||
| vendorWithDisableGPUUUIDMap[vendor] = make(map[string]bool) | ||||||||||||||||||
| } | ||||||||||||||||||
|
ZhengW22 marked this conversation as resolved.
|
||||||||||||||||||
| vendorWithDisableGPUUUIDMap[vendor][id] = true | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
ZhengW22 marked this conversation as resolved.
|
||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| if len(vendorWithDisableGPUUUIDMap) == 0 { | ||||||||||||||||||
| return nodeInfo.Devices | ||||||||||||||||||
| } | ||||||||||||||||||
| newDeviceMap := make(map[string][]device.DeviceInfo) | ||||||||||||||||||
| for deviceName, deviceList := range nodeInfo.Devices { | ||||||||||||||||||
| newDeviceList := make([]device.DeviceInfo, 0, len(deviceList)) | ||||||||||||||||||
| for _, d := range deviceList { | ||||||||||||||||||
| if disableGPUUUIDMap, ok := vendorWithDisableGPUUUIDMap[d.DeviceVendor]; ok { | ||||||||||||||||||
| if disabled := disableGPUUUIDMap[d.ID]; disabled { | ||||||||||||||||||
| klog.V(5).Infof("Disable gpu uuid is : %s", d.ID) | ||||||||||||||||||
| continue | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+137
to
+140
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This conditional check can be simplified. In Go, when checking a boolean value from a map, you can directly use the result of the map access in an
Suggested change
|
||||||||||||||||||
| } | ||||||||||||||||||
| newDeviceList = append(newDeviceList, d) | ||||||||||||||||||
| } | ||||||||||||||||||
| newDeviceMap[deviceName] = newDeviceList | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+132
to
+145
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for filtering devices is correct. However, for better performance and readability, the check for whether a vendor has devices to disable can be hoisted out of the inner loop. Since newDeviceMap := make(map[string][]device.DeviceInfo, len(nodeInfo.Devices))
for deviceName, deviceList := range nodeInfo.Devices {
disableGPUUUIDMap, ok := vendorWithDisableGPUUUIDMap[deviceName]
if !ok {
newDeviceMap[deviceName] = deviceList
continue
}
newDeviceList := make([]device.DeviceInfo, 0, len(deviceList))
for _, d := range deviceList {
if disabled := disableGPUUUIDMap[d.ID]; disabled {
klog.V(5).Infof("Disable gpu uuid is : %s", d.ID)
continue
}
newDeviceList = append(newDeviceList, d)
}
newDeviceMap[deviceName] = newDeviceList
} |
||||||||||||||||||
| return newDeviceMap | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| func (m *nodeManager) rmNodeDevices(nodeID string, deviceVendor string) { | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,13 +18,18 @@ package scheduler | |
|
|
||
| import ( | ||
| "fmt" | ||
| "reflect" | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "github.com/Project-HAMi/HAMi/pkg/device" | ||
| "github.com/Project-HAMi/HAMi/pkg/device/metax" | ||
| "github.com/Project-HAMi/HAMi/pkg/device/nvidia" | ||
| "github.com/Project-HAMi/HAMi/pkg/scheduler/config" | ||
|
|
||
| "gotest.tools/v3/assert" | ||
| corev1 "k8s.io/api/core/v1" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| func Test_addNode_ListNodes(t *testing.T) { | ||
|
|
@@ -322,3 +327,105 @@ func Test_rmNodeDevices(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| func Test_rmDeviceByNodeAnnotation(t *testing.T) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test suite for For example: {
name: "Test remove device with whitespace in annotation",
args: args{
nodeInfo: &device.NodeInfo{
Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{nvidia.GPUNoUseUUID: " " + id1 + " "}}},
Devices: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}},
},
},
want: []device.DeviceInfo{},
},
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current tests don't cover cases where the comma-separated UUID list in the annotation contains spaces (e.g., |
||
| id1 := "60151478-4709-4242-a8c1-a944252d194b" | ||
| id2 := "33c00a52-72ab-4b61-a7ce-43107588835b" | ||
| type args struct { | ||
| nodeInfo *device.NodeInfo | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| want map[string][]device.DeviceInfo | ||
| }{ | ||
| { | ||
| name: "Test space condition", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{nvidia.GPUNoUseUUID: strings.Join([]string{id1 + " ", " " + id2 + " "}, ",")}}}, | ||
| Devices: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}, {DeviceVendor: nvidia.NvidiaGPUDevice, ID: id2}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{}}, | ||
| }, | ||
| { | ||
| name: "Test remove one device", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{nvidia.GPUNoUseUUID: id1}}}, | ||
| Devices: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{}}, | ||
| }, | ||
| { | ||
| name: "Test remove two devices", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{nvidia.GPUNoUseUUID: strings.Join([]string{id1, id2}, ",")}}}, | ||
| Devices: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}, {DeviceVendor: nvidia.NvidiaGPUDevice, ID: id2}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{}}, | ||
| }, | ||
| { | ||
| name: "Test remove one device and keep one device", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{nvidia.GPUNoUseUUID: strings.Join([]string{id2}, ",")}}}, | ||
| Devices: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}, {DeviceVendor: nvidia.NvidiaGPUDevice, ID: id2}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}}}, | ||
| }, | ||
| { | ||
| name: "Test no removing device, case1", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"test-key": ""}}}, | ||
| Devices: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}}}, | ||
| }, | ||
| { | ||
| name: "Test no removing device, case2", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{nvidia.GPUNoUseUUID: id2}}}, | ||
| Devices: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{nvidia.NvidiaGPUDevice: []device.DeviceInfo{{DeviceVendor: nvidia.NvidiaGPUDevice, ID: id1}}}, | ||
| }, | ||
| { | ||
| name: "Test removing metax device, case1", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{metax.MetaxNoUseUUID: id1}}}, | ||
| // Devices: []device.DeviceInfo{{DeviceVendor: metax.MetaxGPUDevice, ID: id1}}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| Devices: map[string][]device.DeviceInfo{metax.MetaxGPUDevice: []device.DeviceInfo{{DeviceVendor: metax.MetaxGPUDevice, ID: id1}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{metax.MetaxGPUDevice: []device.DeviceInfo{}}, | ||
| }, | ||
| { | ||
| name: "Test removing metax sgpu device", | ||
| args: args{ | ||
| nodeInfo: &device.NodeInfo{ | ||
| Node: &corev1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{metax.MetaxNoUseUUID: id1}}}, | ||
| Devices: map[string][]device.DeviceInfo{metax.MetaxSGPUDevice: {{DeviceVendor: metax.MetaxSGPUDevice, ID: id1}}}, | ||
| }, | ||
| }, | ||
| want: map[string][]device.DeviceInfo{metax.MetaxSGPUDevice: []device.DeviceInfo{}}, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if got := rmDeviceByNodeAnnotation(tt.args.nodeInfo); !reflect.DeepEqual(got, tt.want) { | ||
| t.Errorf("rmDeviceByNodeAnnotation() = %v, want %v", got, tt.want) | ||
| } | ||
|
Comment on lines
+456
to
+458
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency with other tests in this file (e.g., got := rmDeviceByNodeAnnotation(tt.args.nodeInfo)
assert.DeepEqual(t, tt.want, got) |
||
| }) | ||
| } | ||
| } | ||
|
Comment on lines
+331
to
+461
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test cases for
|
||
Uh oh!
There was an error while loading. Please reload this page.