kubernetes topology_hints_test 源码
kubernetes topology_hints_test 代码
文件路径:/pkg/kubelet/cm/devicemanager/topology_hints_test.go
/*
Copyright 2019 The Kubernetes 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 devicemanager
import (
"fmt"
"reflect"
"sort"
"testing"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
)
type mockAffinityStore struct {
hint topologymanager.TopologyHint
}
func (m *mockAffinityStore) GetAffinity(podUID string, containerName string) topologymanager.TopologyHint {
return m.hint
}
func (m *mockAffinityStore) GetPolicy() topologymanager.Policy {
return nil
}
func makeNUMADevice(id string, numa int) pluginapi.Device {
return pluginapi.Device{
ID: id,
Topology: &pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{{ID: int64(numa)}}},
}
}
func makeSocketMask(sockets ...int) bitmask.BitMask {
mask, _ := bitmask.NewBitMask(sockets...)
return mask
}
func TestGetTopologyHints(t *testing.T) {
tcases := getCommonTestCases()
for _, tc := range tcases {
m := ManagerImpl{
allDevices: NewResourceDeviceInstances(),
healthyDevices: make(map[string]sets.String),
allocatedDevices: make(map[string]sets.String),
podDevices: newPodDevices(),
sourcesReady: &sourcesReadyStub{},
activePods: func() []*v1.Pod { return []*v1.Pod{tc.pod} },
numaNodes: []int{0, 1},
}
for r := range tc.devices {
m.allDevices[r] = make(DeviceInstances)
m.healthyDevices[r] = sets.NewString()
for _, d := range tc.devices[r] {
m.allDevices[r][d.ID] = d
m.healthyDevices[r].Insert(d.ID)
}
}
for p := range tc.allocatedDevices {
for c := range tc.allocatedDevices[p] {
for r, devices := range tc.allocatedDevices[p][c] {
m.podDevices.insert(p, c, r, constructDevices(devices), nil)
m.allocatedDevices[r] = sets.NewString()
for _, d := range devices {
m.allocatedDevices[r].Insert(d)
}
}
}
}
hints := m.GetTopologyHints(tc.pod, &tc.pod.Spec.Containers[0])
for r := range tc.expectedHints {
sort.SliceStable(hints[r], func(i, j int) bool {
return hints[r][i].LessThan(hints[r][j])
})
sort.SliceStable(tc.expectedHints[r], func(i, j int) bool {
return tc.expectedHints[r][i].LessThan(tc.expectedHints[r][j])
})
if !reflect.DeepEqual(hints[r], tc.expectedHints[r]) {
t.Errorf("%v: Expected result to be %#v, got %#v", tc.description, tc.expectedHints[r], hints[r])
}
}
}
}
func TestTopologyAlignedAllocation(t *testing.T) {
tcases := []struct {
description string
resource string
request int
devices []pluginapi.Device
allocatedDevices []string
hint topologymanager.TopologyHint
getPreferredAllocationFunc func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error)
expectedPreferredAllocation []string
expectedAlignment map[int]int
}{
{
description: "Single Request, no alignment",
resource: "resource",
request: 1,
devices: []pluginapi.Device{
{ID: "Dev1"},
{ID: "Dev2"},
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: true,
},
expectedAlignment: map[int]int{},
},
{
description: "Request for 1, partial alignment",
resource: "resource",
request: 1,
devices: []pluginapi.Device{
{ID: "Dev1"},
makeNUMADevice("Dev2", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
expectedAlignment: map[int]int{1: 1},
},
{
description: "Single Request, socket 0",
resource: "resource",
request: 1,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAlignment: map[int]int{0: 1},
},
{
description: "Single Request, socket 1",
resource: "resource",
request: 1,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
expectedAlignment: map[int]int{1: 1},
},
{
description: "Request for 2, socket 0",
resource: "resource",
request: 2,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAlignment: map[int]int{0: 2},
},
{
description: "Request for 2, socket 1",
resource: "resource",
request: 2,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
expectedAlignment: map[int]int{1: 2},
},
{
description: "Request for 4, unsatisfiable, prefer socket 0",
resource: "resource",
request: 4,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
makeNUMADevice("Dev5", 0),
makeNUMADevice("Dev6", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAlignment: map[int]int{0: 3, 1: 1},
},
{
description: "Request for 4, unsatisfiable, prefer socket 1",
resource: "resource",
request: 4,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
makeNUMADevice("Dev5", 0),
makeNUMADevice("Dev6", 1),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
expectedAlignment: map[int]int{0: 1, 1: 3},
},
{
description: "Request for 4, multisocket",
resource: "resource",
request: 4,
devices: []pluginapi.Device{
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 2),
makeNUMADevice("Dev4", 3),
makeNUMADevice("Dev5", 0),
makeNUMADevice("Dev6", 1),
makeNUMADevice("Dev7", 2),
makeNUMADevice("Dev8", 3),
},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(1, 3),
Preferred: true,
},
expectedAlignment: map[int]int{1: 2, 3: 2},
},
{
description: "Request for 5, socket 0, preferred aligned accepted",
resource: "resource",
request: 5,
devices: func() []pluginapi.Device {
devices := []pluginapi.Device{}
for i := 0; i < 100; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 0))
}
for i := 100; i < 200; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 1))
}
return devices
}(),
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
getPreferredAllocationFunc: func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error) {
return &pluginapi.PreferredAllocationResponse{
ContainerResponses: []*pluginapi.ContainerPreferredAllocationResponse{
{DeviceIDs: []string{"Dev0", "Dev19", "Dev83", "Dev42", "Dev77"}},
},
}, nil
},
expectedPreferredAllocation: []string{"Dev0", "Dev19", "Dev83", "Dev42", "Dev77"},
expectedAlignment: map[int]int{0: 5},
},
{
description: "Request for 5, socket 0, preferred aligned accepted, unaligned ignored",
resource: "resource",
request: 5,
devices: func() []pluginapi.Device {
devices := []pluginapi.Device{}
for i := 0; i < 100; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 0))
}
for i := 100; i < 200; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 1))
}
return devices
}(),
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
getPreferredAllocationFunc: func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error) {
return &pluginapi.PreferredAllocationResponse{
ContainerResponses: []*pluginapi.ContainerPreferredAllocationResponse{
{DeviceIDs: []string{"Dev0", "Dev19", "Dev83", "Dev150", "Dev186"}},
},
}, nil
},
expectedPreferredAllocation: []string{"Dev0", "Dev19", "Dev83"},
expectedAlignment: map[int]int{0: 5},
},
{
description: "Request for 5, socket 1, preferred aligned accepted, bogus ignored",
resource: "resource",
request: 5,
devices: func() []pluginapi.Device {
devices := []pluginapi.Device{}
for i := 0; i < 100; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 1))
}
return devices
}(),
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
getPreferredAllocationFunc: func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error) {
return &pluginapi.PreferredAllocationResponse{
ContainerResponses: []*pluginapi.ContainerPreferredAllocationResponse{
{DeviceIDs: []string{"Dev0", "Dev19", "Dev83", "bogus0", "bogus1"}},
},
}, nil
},
expectedPreferredAllocation: []string{"Dev0", "Dev19", "Dev83"},
expectedAlignment: map[int]int{1: 5},
},
{
description: "Request for 5, multisocket, preferred accepted",
resource: "resource",
request: 5,
devices: func() []pluginapi.Device {
devices := []pluginapi.Device{}
for i := 0; i < 3; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 0))
}
for i := 3; i < 100; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 1))
}
return devices
}(),
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
getPreferredAllocationFunc: func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error) {
return &pluginapi.PreferredAllocationResponse{
ContainerResponses: []*pluginapi.ContainerPreferredAllocationResponse{
{DeviceIDs: []string{"Dev0", "Dev1", "Dev2", "Dev42", "Dev83"}},
},
}, nil
},
expectedPreferredAllocation: []string{"Dev0", "Dev1", "Dev2", "Dev42", "Dev83"},
expectedAlignment: map[int]int{0: 3, 1: 2},
},
{
description: "Request for 5, multisocket, preferred unaligned accepted, bogus ignored",
resource: "resource",
request: 5,
devices: func() []pluginapi.Device {
devices := []pluginapi.Device{}
for i := 0; i < 3; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 0))
}
for i := 3; i < 100; i++ {
id := fmt.Sprintf("Dev%d", i)
devices = append(devices, makeNUMADevice(id, 1))
}
return devices
}(),
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
getPreferredAllocationFunc: func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error) {
return &pluginapi.PreferredAllocationResponse{
ContainerResponses: []*pluginapi.ContainerPreferredAllocationResponse{
{DeviceIDs: []string{"Dev0", "Dev1", "Dev2", "Dev42", "bogus0"}},
},
}, nil
},
expectedPreferredAllocation: []string{"Dev0", "Dev1", "Dev2", "Dev42"},
expectedAlignment: map[int]int{0: 3, 1: 2},
},
}
for _, tc := range tcases {
m := ManagerImpl{
allDevices: NewResourceDeviceInstances(),
healthyDevices: make(map[string]sets.String),
allocatedDevices: make(map[string]sets.String),
endpoints: make(map[string]endpointInfo),
podDevices: newPodDevices(),
sourcesReady: &sourcesReadyStub{},
activePods: func() []*v1.Pod { return []*v1.Pod{} },
topologyAffinityStore: &mockAffinityStore{tc.hint},
}
m.allDevices[tc.resource] = make(DeviceInstances)
m.healthyDevices[tc.resource] = sets.NewString()
m.endpoints[tc.resource] = endpointInfo{}
for _, d := range tc.devices {
m.allDevices[tc.resource][d.ID] = d
m.healthyDevices[tc.resource].Insert(d.ID)
}
if tc.getPreferredAllocationFunc != nil {
m.endpoints[tc.resource] = endpointInfo{
e: &MockEndpoint{
getPreferredAllocationFunc: tc.getPreferredAllocationFunc,
},
opts: &pluginapi.DevicePluginOptions{GetPreferredAllocationAvailable: true},
}
}
allocated, err := m.devicesToAllocate("podUID", "containerName", tc.resource, tc.request, sets.NewString())
if err != nil {
t.Errorf("Unexpected error: %v", err)
continue
}
if len(allocated) != tc.request {
t.Errorf("%v. expected allocation size: %v but got: %v", tc.description, tc.request, len(allocated))
}
if !allocated.HasAll(tc.expectedPreferredAllocation...) {
t.Errorf("%v. expected preferred allocation: %v but not present in: %v", tc.description, tc.expectedPreferredAllocation, allocated.UnsortedList())
}
alignment := make(map[int]int)
if m.deviceHasTopologyAlignment(tc.resource) {
for d := range allocated {
if m.allDevices[tc.resource][d].Topology != nil {
alignment[int(m.allDevices[tc.resource][d].Topology.Nodes[0].ID)]++
}
}
}
if !reflect.DeepEqual(alignment, tc.expectedAlignment) {
t.Errorf("%v. expected alignment: %v but got: %v", tc.description, tc.expectedAlignment, alignment)
}
}
}
func TestGetPreferredAllocationParameters(t *testing.T) {
tcases := []struct {
description string
resource string
request int
allDevices []pluginapi.Device
allocatedDevices []string
reusableDevices []string
hint topologymanager.TopologyHint
expectedAvailable []string
expectedMustInclude []string
expectedSize int
}{
{
description: "Request for 1, socket 0, 0 already allocated, 0 reusable",
resource: "resource",
request: 1,
allDevices: []pluginapi.Device{
makeNUMADevice("Dev0", 0),
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 0),
},
allocatedDevices: []string{},
reusableDevices: []string{},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAvailable: []string{"Dev0", "Dev1", "Dev2", "Dev3"},
expectedMustInclude: []string{},
expectedSize: 1,
},
{
description: "Request for 4, socket 0, 2 already allocated, 2 reusable",
resource: "resource",
request: 4,
allDevices: []pluginapi.Device{
makeNUMADevice("Dev0", 0),
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 0),
makeNUMADevice("Dev5", 0),
makeNUMADevice("Dev6", 0),
makeNUMADevice("Dev7", 0),
},
allocatedDevices: []string{"Dev0", "Dev5"},
reusableDevices: []string{"Dev0", "Dev5"},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAvailable: []string{"Dev0", "Dev1", "Dev2", "Dev3", "Dev4", "Dev5", "Dev6", "Dev7"},
expectedMustInclude: []string{"Dev0", "Dev5"},
expectedSize: 4,
},
{
description: "Request for 4, socket 0, 4 already allocated, 2 reusable",
resource: "resource",
request: 4,
allDevices: []pluginapi.Device{
makeNUMADevice("Dev0", 0),
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 0),
makeNUMADevice("Dev5", 0),
makeNUMADevice("Dev6", 0),
makeNUMADevice("Dev7", 0),
},
allocatedDevices: []string{"Dev0", "Dev5", "Dev4", "Dev1"},
reusableDevices: []string{"Dev0", "Dev5"},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAvailable: []string{"Dev0", "Dev2", "Dev3", "Dev5", "Dev6", "Dev7"},
expectedMustInclude: []string{"Dev0", "Dev5"},
expectedSize: 4,
},
{
description: "Request for 6, multisocket, 2 already allocated, 2 reusable",
resource: "resource",
request: 6,
allDevices: []pluginapi.Device{
makeNUMADevice("Dev0", 0),
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
makeNUMADevice("Dev5", 1),
makeNUMADevice("Dev6", 1),
makeNUMADevice("Dev7", 1),
},
allocatedDevices: []string{"Dev1", "Dev6"},
reusableDevices: []string{"Dev1", "Dev6"},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAvailable: []string{"Dev0", "Dev1", "Dev2", "Dev3", "Dev4", "Dev5", "Dev6", "Dev7"},
expectedMustInclude: []string{"Dev0", "Dev1", "Dev2", "Dev3", "Dev6"},
expectedSize: 6,
},
{
description: "Request for 6, multisocket, 4 already allocated, 2 reusable",
resource: "resource",
request: 6,
allDevices: []pluginapi.Device{
makeNUMADevice("Dev0", 0),
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
makeNUMADevice("Dev5", 1),
makeNUMADevice("Dev6", 1),
makeNUMADevice("Dev7", 1),
},
allocatedDevices: []string{"Dev0", "Dev1", "Dev6", "Dev7"},
reusableDevices: []string{"Dev1", "Dev6"},
hint: topologymanager.TopologyHint{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
expectedAvailable: []string{"Dev1", "Dev2", "Dev3", "Dev4", "Dev5", "Dev6"},
expectedMustInclude: []string{"Dev1", "Dev2", "Dev3", "Dev6"},
expectedSize: 6,
},
}
for _, tc := range tcases {
m := ManagerImpl{
allDevices: NewResourceDeviceInstances(),
healthyDevices: make(map[string]sets.String),
allocatedDevices: make(map[string]sets.String),
endpoints: make(map[string]endpointInfo),
podDevices: newPodDevices(),
sourcesReady: &sourcesReadyStub{},
activePods: func() []*v1.Pod { return []*v1.Pod{} },
topologyAffinityStore: &mockAffinityStore{tc.hint},
}
m.allDevices[tc.resource] = make(DeviceInstances)
m.healthyDevices[tc.resource] = sets.NewString()
for _, d := range tc.allDevices {
m.allDevices[tc.resource][d.ID] = d
m.healthyDevices[tc.resource].Insert(d.ID)
}
m.allocatedDevices[tc.resource] = sets.NewString()
for _, d := range tc.allocatedDevices {
m.allocatedDevices[tc.resource].Insert(d)
}
actualAvailable := []string{}
actualMustInclude := []string{}
actualSize := 0
m.endpoints[tc.resource] = endpointInfo{
e: &MockEndpoint{
getPreferredAllocationFunc: func(available, mustInclude []string, size int) (*pluginapi.PreferredAllocationResponse, error) {
actualAvailable = append(actualAvailable, available...)
actualMustInclude = append(actualMustInclude, mustInclude...)
actualSize = size
return nil, nil
},
},
opts: &pluginapi.DevicePluginOptions{GetPreferredAllocationAvailable: true},
}
_, err := m.devicesToAllocate("podUID", "containerName", tc.resource, tc.request, sets.NewString(tc.reusableDevices...))
if err != nil {
t.Errorf("Unexpected error: %v", err)
continue
}
if !sets.NewString(actualAvailable...).Equal(sets.NewString(tc.expectedAvailable...)) {
t.Errorf("%v. expected available: %v but got: %v", tc.description, tc.expectedAvailable, actualAvailable)
}
if !sets.NewString(actualAvailable...).Equal(sets.NewString(tc.expectedAvailable...)) {
t.Errorf("%v. expected mustInclude: %v but got: %v", tc.description, tc.expectedMustInclude, actualMustInclude)
}
if actualSize != tc.expectedSize {
t.Errorf("%v. expected size: %v but got: %v", tc.description, tc.expectedSize, actualSize)
}
}
}
func TestGetPodDeviceRequest(t *testing.T) {
tcases := []struct {
description string
pod *v1.Pod
registeredDevices []string
expected map[string]int
}{
{
description: "empty pod",
pod: &v1.Pod{},
registeredDevices: []string{},
expected: map[string]int{},
},
{
description: "Init container requests device plugin resource",
pod: &v1.Pod{
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
},
},
},
},
},
},
registeredDevices: []string{"gpu"},
expected: map[string]int{"gpu": 2},
},
{
description: "Init containers request device plugin resource",
pod: &v1.Pod{
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("4"),
},
},
},
},
},
},
registeredDevices: []string{"gpu"},
expected: map[string]int{"gpu": 4},
},
{
description: "User container requests device plugin resource",
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
},
},
},
},
},
},
registeredDevices: []string{"gpu"},
expected: map[string]int{"gpu": 2},
},
{
description: "Init containers and user containers request the same amount of device plugin resources",
pod: &v1.Pod{
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
v1.ResourceName("nic"): resource.MustParse("2"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
v1.ResourceName("nic"): resource.MustParse("2"),
},
},
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("1"),
v1.ResourceName("nic"): resource.MustParse("1"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("1"),
v1.ResourceName("nic"): resource.MustParse("1"),
},
},
},
},
},
},
registeredDevices: []string{"gpu", "nic"},
expected: map[string]int{"gpu": 2, "nic": 2},
},
{
description: "Init containers request more device plugin resources than user containers",
pod: &v1.Pod{
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
v1.ResourceName("nic"): resource.MustParse("1"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("3"),
v1.ResourceName("nic"): resource.MustParse("2"),
},
},
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("1"),
v1.ResourceName("nic"): resource.MustParse("1"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("1"),
},
},
},
},
},
},
registeredDevices: []string{"gpu", "nic"},
expected: map[string]int{"gpu": 3, "nic": 2},
},
{
description: "User containers request more device plugin resources than init containers",
pod: &v1.Pod{
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
v1.ResourceName("nic"): resource.MustParse("1"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("2"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("2"),
v1.ResourceName("nic"): resource.MustParse("1"),
},
},
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("3"),
v1.ResourceName("nic"): resource.MustParse("2"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName(v1.ResourceCPU): resource.MustParse("1"),
v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
v1.ResourceName("gpu"): resource.MustParse("3"),
v1.ResourceName("nic"): resource.MustParse("2"),
},
},
},
},
},
},
registeredDevices: []string{"gpu", "nic"},
expected: map[string]int{"gpu": 6, "nic": 4},
},
}
for _, tc := range tcases {
m := ManagerImpl{
healthyDevices: make(map[string]sets.String),
}
for _, res := range tc.registeredDevices {
m.healthyDevices[res] = sets.NewString()
}
accumulatedResourceRequests := m.getPodDeviceRequest(tc.pod)
if !reflect.DeepEqual(accumulatedResourceRequests, tc.expected) {
t.Errorf("%v. expected alignment: %v but got: %v", tc.description, tc.expected, accumulatedResourceRequests)
}
}
}
func TestGetPodTopologyHints(t *testing.T) {
tcases := getCommonTestCases()
tcases = append(tcases, getPodScopeTestCases()...)
for _, tc := range tcases {
m := ManagerImpl{
allDevices: NewResourceDeviceInstances(),
healthyDevices: make(map[string]sets.String),
allocatedDevices: make(map[string]sets.String),
podDevices: newPodDevices(),
sourcesReady: &sourcesReadyStub{},
activePods: func() []*v1.Pod { return []*v1.Pod{tc.pod, {ObjectMeta: metav1.ObjectMeta{UID: "fakeOtherPod"}}} },
numaNodes: []int{0, 1},
}
for r := range tc.devices {
m.allDevices[r] = make(DeviceInstances)
m.healthyDevices[r] = sets.NewString()
for _, d := range tc.devices[r] {
//add `pluginapi.Device` with Topology
m.allDevices[r][d.ID] = d
m.healthyDevices[r].Insert(d.ID)
}
}
for p := range tc.allocatedDevices {
for c := range tc.allocatedDevices[p] {
for r, devices := range tc.allocatedDevices[p][c] {
m.podDevices.insert(p, c, r, constructDevices(devices), nil)
m.allocatedDevices[r] = sets.NewString()
for _, d := range devices {
m.allocatedDevices[r].Insert(d)
}
}
}
}
hints := m.GetPodTopologyHints(tc.pod)
for r := range tc.expectedHints {
sort.SliceStable(hints[r], func(i, j int) bool {
return hints[r][i].LessThan(hints[r][j])
})
sort.SliceStable(tc.expectedHints[r], func(i, j int) bool {
return tc.expectedHints[r][i].LessThan(tc.expectedHints[r][j])
})
if !reflect.DeepEqual(hints[r], tc.expectedHints[r]) {
t.Errorf("%v: Expected result to be %v, got %v", tc.description, tc.expectedHints[r], hints[r])
}
}
}
}
type topologyHintTestCase struct {
description string
pod *v1.Pod
devices map[string][]pluginapi.Device
allocatedDevices map[string]map[string]map[string][]string
expectedHints map[string][]topologymanager.TopologyHint
}
func getCommonTestCases() []topologyHintTestCase {
return []topologyHintTestCase{
{
description: "Single Request, no alignment",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("1"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
{ID: "Dev1"},
{ID: "Dev2"},
{ID: "Dev3", Topology: &pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{}}},
{ID: "Dev4", Topology: &pluginapi.TopologyInfo{Nodes: nil}},
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": nil,
},
},
{
description: "Single Request, only one with alignment",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("1"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
{ID: "Dev1"},
makeNUMADevice("Dev2", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "Single Request, one device per socket",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("1"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "Request for 2, one device per socket",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("2"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: true,
},
},
},
},
{
description: "Request for 2, 2 devices per socket",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("2"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "Request for 2, optimal on 1 NUMA node, forced cross-NUMA",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("2"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
},
},
allocatedDevices: map[string]map[string]map[string][]string{
"fakePod": {
"fakeOtherContainer": {
"testdevice": {"Dev1", "Dev2"},
},
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "2 device types, mixed configuration",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("2"),
v1.ResourceName("testdevice2"): resource.MustParse("1"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice1": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 1),
makeNUMADevice("Dev3", 0),
makeNUMADevice("Dev4", 1),
},
"testdevice2": {
makeNUMADevice("Dev1", 0),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice1": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
"testdevice2": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "Single device type, more requested than available",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("6"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {},
},
},
{
description: "Single device type, all already allocated to container",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("2"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
},
},
allocatedDevices: map[string]map[string]map[string][]string{
"fakePod": {
"fakeContainer": {
"testdevice": {"Dev1", "Dev2"},
},
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "Single device type, less already allocated to container than requested",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("4"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
},
allocatedDevices: map[string]map[string]map[string][]string{
"fakePod": {
"fakeContainer": {
"testdevice": {"Dev1", "Dev2"},
},
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {},
},
},
{
description: "Single device type, more already allocated to container than requested",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice"): resource.MustParse("2"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
},
allocatedDevices: map[string]map[string]map[string][]string{
"fakePod": {
"fakeContainer": {
"testdevice": {"Dev1", "Dev2", "Dev3", "Dev4"},
},
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice": {},
},
},
}
}
func getPodScopeTestCases() []topologyHintTestCase {
return []topologyHintTestCase{
{
description: "2 device types, user container only",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer1",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("2"),
},
},
},
{
Name: "fakeContainer2",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice2"): resource.MustParse("2"),
},
},
},
{
Name: "fakeContainer3",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("notRegistered"): resource.MustParse("2"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice1": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
"testdevice2": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice1": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
"testdevice2": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "2 device types, request resources for init containers and user container",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("1"),
v1.ResourceName("testdevice2"): resource.MustParse("1"),
},
},
},
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("1"),
v1.ResourceName("testdevice2"): resource.MustParse("2"),
},
},
},
},
Containers: []v1.Container{
{
Name: "fakeContainer1",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("1"),
v1.ResourceName("testdevice2"): resource.MustParse("1"),
},
},
},
{
Name: "fakeContainer2",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("1"),
v1.ResourceName("testdevice2"): resource.MustParse("1"),
},
},
},
{
Name: "fakeContainer3",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("notRegistered"): resource.MustParse("1"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice1": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
"testdevice2": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice1": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
"testdevice2": {
{
NUMANodeAffinity: makeSocketMask(0),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(1),
Preferred: true,
},
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
{
description: "2 device types, user container only, optimal on 1 NUMA node, forced cross-NUMA",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "fakePod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "fakeContainer1",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("1"),
v1.ResourceName("testdevice2"): resource.MustParse("1"),
},
},
},
{
Name: "fakeContainer2",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("testdevice1"): resource.MustParse("1"),
v1.ResourceName("testdevice2"): resource.MustParse("1"),
},
},
},
{
Name: "fakeContainer3",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceName("notRegistered"): resource.MustParse("1"),
},
},
},
},
},
},
devices: map[string][]pluginapi.Device{
"testdevice1": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
"testdevice2": {
makeNUMADevice("Dev1", 0),
makeNUMADevice("Dev2", 0),
makeNUMADevice("Dev3", 1),
makeNUMADevice("Dev4", 1),
},
},
allocatedDevices: map[string]map[string]map[string][]string{
"fakeOtherPod": {
"fakeOtherContainer": {
"testdevice1": {"Dev1", "Dev3"},
"testdevice2": {"Dev1", "Dev3"},
},
},
},
expectedHints: map[string][]topologymanager.TopologyHint{
"testdevice1": {
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
"testdevice2": {
{
NUMANodeAffinity: makeSocketMask(0, 1),
Preferred: false,
},
},
},
},
}
}
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦