1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-14 11:57:51 +00:00
node-feature-discovery/pkg/resourcemonitor/noderesourcesaggregator_test.go
Artyom Lukianov 45062754fd resourcemonitor: aggregate and provide the memory and hugepages information
The Kuberenetes pod resource API now exposing the memory and hugepages information
for guaranteed pods. We can use this information to update NodeResourceTopology
resource with memory and hugepages data.

Signed-off-by: Artyom Lukianov <alukiano@redhat.com>
2021-11-04 10:17:10 +02:00

650 lines
16 KiB
Go

/*
Copyright 2021 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 resourcemonitor
import (
"encoding/json"
"log"
"sort"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/jaypipes/ghw"
. "github.com/smartystreets/goconvey/convey"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
v1 "k8s.io/kubelet/pkg/apis/podresources/v1"
topologyv1alpha1 "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha1"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
)
func TestResourcesAggregator(t *testing.T) {
fakeTopo := ghw.TopologyInfo{}
Convey("When recovering test topology from JSON data", t, func() {
err := json.Unmarshal([]byte(testTopology), &fakeTopo)
So(err, ShouldBeNil)
})
var resAggr ResourcesAggregator
Convey("When I aggregate the node resources fake data and no pod allocation", t, func() {
availRes := &v1.AllocatableResourcesResponse{
Devices: []*v1.ContainerDevices{
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netAAA-0"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netAAA-1"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netAAA-2"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netAAA-3"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netBBB-0"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netBBB-1"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
{
ResourceName: "fake.io/gpu",
DeviceIds: []string{"gpuAAA"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
},
// CPUId 0 and 1 are missing from the list below to simulate
// that they are not allocatable CPUs (kube-reserved or system-reserved)
CpuIds: []int64{
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
},
Memory: []*v1.ContainerMemory{
{
MemoryType: "memory",
Size_: 1024,
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
MemoryType: "memory",
Size_: 1024,
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
{
MemoryType: "hugepages-2Mi",
Size_: 1024,
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
},
}
memoryResourcesCapacity := utils.NumaMemoryResources{
0: map[corev1.ResourceName]int64{
corev1.ResourceMemory: 2048,
},
1: map[corev1.ResourceName]int64{
corev1.ResourceMemory: 2048,
corev1.ResourceName("hugepages-2Mi"): 2048,
},
}
resAggr = NewResourcesAggregatorFromData(&fakeTopo, availRes, memoryResourcesCapacity)
Convey("When aggregating resources", func() {
expected := topologyv1alpha1.ZoneList{
topologyv1alpha1.Zone{
Name: "node-0",
Type: "Node",
Costs: topologyv1alpha1.CostList{
topologyv1alpha1.CostInfo{
Name: "node-0",
Value: 10,
},
topologyv1alpha1.CostInfo{
Name: "node-1",
Value: 20,
},
},
Resources: topologyv1alpha1.ResourceInfoList{
topologyv1alpha1.ResourceInfo{
Name: "cpu",
Available: *resource.NewQuantity(11, resource.DecimalSI),
Allocatable: *resource.NewQuantity(11, resource.DecimalSI),
Capacity: *resource.NewQuantity(12, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "fake.io/net",
Available: *resource.NewQuantity(4, resource.DecimalSI),
Allocatable: *resource.NewQuantity(4, resource.DecimalSI),
Capacity: *resource.NewQuantity(4, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "memory",
Available: *resource.NewQuantity(1024, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1024, resource.DecimalSI),
Capacity: *resource.NewQuantity(2048, resource.DecimalSI),
},
},
},
topologyv1alpha1.Zone{
Name: "node-1",
Type: "Node",
Costs: topologyv1alpha1.CostList{
topologyv1alpha1.CostInfo{
Name: "node-0",
Value: 20,
},
topologyv1alpha1.CostInfo{
Name: "node-1",
Value: 10,
},
},
Resources: topologyv1alpha1.ResourceInfoList{
topologyv1alpha1.ResourceInfo{
Name: "cpu",
Available: *resource.NewQuantity(11, resource.DecimalSI),
Allocatable: *resource.NewQuantity(11, resource.DecimalSI),
Capacity: *resource.NewQuantity(12, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "fake.io/gpu",
Available: *resource.NewQuantity(1, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1, resource.DecimalSI),
Capacity: *resource.NewQuantity(1, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "fake.io/net",
Available: *resource.NewQuantity(2, resource.DecimalSI),
Allocatable: *resource.NewQuantity(2, resource.DecimalSI),
Capacity: *resource.NewQuantity(2, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "hugepages-2Mi",
Available: *resource.NewQuantity(1024, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1024, resource.DecimalSI),
Capacity: *resource.NewQuantity(2048, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "memory",
Available: *resource.NewQuantity(1024, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1024, resource.DecimalSI),
Capacity: *resource.NewQuantity(2048, resource.DecimalSI),
},
},
},
}
res := resAggr.Aggregate(nil) // no pods allocation
sort.Slice(res, func(i, j int) bool {
return res[i].Name < res[j].Name
})
for _, resource := range res {
sort.Slice(resource.Costs, func(x, y int) bool {
return resource.Costs[x].Name < resource.Costs[y].Name
})
}
for _, resource := range res {
sort.Slice(resource.Resources, func(x, y int) bool {
return resource.Resources[x].Name < resource.Resources[y].Name
})
}
log.Printf("result=%+v", res)
log.Printf("expected=%+v", expected)
log.Printf("diff=%s", cmp.Diff(res, expected))
So(cmp.Equal(res, expected), ShouldBeTrue)
})
})
Convey("When I aggregate the node resources fake data and some pod allocation", t, func() {
availRes := &v1.AllocatableResourcesResponse{
Devices: []*v1.ContainerDevices{
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netAAA"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
ResourceName: "fake.io/net",
DeviceIds: []string{"netBBB"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
{
ResourceName: "fake.io/gpu",
DeviceIds: []string{"gpuAAA"},
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
},
// CPUId 0 is missing from the list below to simulate
// that it not allocatable (kube-reserved or system-reserved)
CpuIds: []int64{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
},
Memory: []*v1.ContainerMemory{
{
MemoryType: "memory",
Size_: 1024,
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 0,
},
},
},
},
{
MemoryType: "memory",
Size_: 1024,
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
{
MemoryType: "hugepages-2Mi",
Size_: 1024,
Topology: &v1.TopologyInfo{
Nodes: []*v1.NUMANode{
{
ID: 1,
},
},
},
},
},
}
memoryResourcesCapacity := utils.NumaMemoryResources{
0: map[corev1.ResourceName]int64{
corev1.ResourceMemory: 2048,
},
1: map[corev1.ResourceName]int64{
corev1.ResourceMemory: 2048,
corev1.ResourceName("hugepages-2Mi"): 2048,
},
}
resAggr = NewResourcesAggregatorFromData(&fakeTopo, availRes, memoryResourcesCapacity)
Convey("When aggregating resources", func() {
podRes := []PodResources{
{
Name: "test-pod-0",
Namespace: "default",
Containers: []ContainerResources{
{
Name: "test-cnt-0",
Resources: []ResourceInfo{
{
Name: "cpu",
Data: []string{"5", "7"},
},
{
Name: "fake.io/net",
Data: []string{"netBBB"},
},
{
Name: "memory",
Data: []string{"512"},
NumaNodeIds: []int{1},
},
{
Name: "hugepages-2Mi",
Data: []string{"512"},
NumaNodeIds: []int{1},
},
},
},
},
},
}
expected := topologyv1alpha1.ZoneList{
topologyv1alpha1.Zone{
Name: "node-0",
Type: "Node",
Costs: topologyv1alpha1.CostList{
topologyv1alpha1.CostInfo{
Name: "node-0",
Value: 10,
},
topologyv1alpha1.CostInfo{
Name: "node-1",
Value: 20,
},
},
Resources: topologyv1alpha1.ResourceInfoList{
topologyv1alpha1.ResourceInfo{
Name: "cpu",
Available: *resource.NewQuantity(11, resource.DecimalSI),
Allocatable: *resource.NewQuantity(11, resource.DecimalSI),
Capacity: *resource.NewQuantity(12, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "fake.io/net",
Available: *resource.NewQuantity(1, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1, resource.DecimalSI),
Capacity: *resource.NewQuantity(1, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "memory",
Available: *resource.NewQuantity(1024, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1024, resource.DecimalSI),
Capacity: *resource.NewQuantity(2048, resource.DecimalSI),
},
},
},
topologyv1alpha1.Zone{
Name: "node-1",
Type: "Node",
Costs: topologyv1alpha1.CostList{
topologyv1alpha1.CostInfo{
Name: "node-0",
Value: 20,
},
topologyv1alpha1.CostInfo{
Name: "node-1",
Value: 10,
},
},
Resources: topologyv1alpha1.ResourceInfoList{
topologyv1alpha1.ResourceInfo{
Name: "cpu",
Available: resource.MustParse("10"),
Allocatable: *resource.NewQuantity(12, resource.DecimalSI),
Capacity: *resource.NewQuantity(12, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "fake.io/gpu",
Available: *resource.NewQuantity(1, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1, resource.DecimalSI),
Capacity: *resource.NewQuantity(1, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "fake.io/net",
Available: *resource.NewQuantity(0, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1, resource.DecimalSI),
Capacity: *resource.NewQuantity(1, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "hugepages-2Mi",
Available: *resource.NewQuantity(512, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1024, resource.DecimalSI),
Capacity: *resource.NewQuantity(2048, resource.DecimalSI),
},
topologyv1alpha1.ResourceInfo{
Name: "memory",
Available: *resource.NewQuantity(512, resource.DecimalSI),
Allocatable: *resource.NewQuantity(1024, resource.DecimalSI),
Capacity: *resource.NewQuantity(2048, resource.DecimalSI),
},
},
},
}
res := resAggr.Aggregate(podRes)
sort.Slice(res, func(i, j int) bool {
return res[i].Name < res[j].Name
})
for _, resource := range res {
sort.Slice(resource.Costs, func(x, y int) bool {
return resource.Costs[x].Name < resource.Costs[y].Name
})
}
for _, resource := range res {
sort.Slice(resource.Resources, func(x, y int) bool {
return resource.Resources[x].Name < resource.Resources[y].Name
})
}
log.Printf("result=%+v", res)
log.Printf("expected=%+v", expected)
log.Printf("diff=%s", cmp.Diff(res, expected))
So(cmp.Equal(res, expected), ShouldBeTrue)
})
})
}
// ghwc topology -f json
var testTopology = `{
"nodes": [
{
"id": 0,
"cores": [
{
"id": 0,
"index": 0,
"total_threads": 2,
"logical_processors": [
0,
12
]
},
{
"id": 10,
"index": 1,
"total_threads": 2,
"logical_processors": [
10,
22
]
},
{
"id": 1,
"index": 2,
"total_threads": 2,
"logical_processors": [
14,
2
]
},
{
"id": 2,
"index": 3,
"total_threads": 2,
"logical_processors": [
16,
4
]
},
{
"id": 8,
"index": 4,
"total_threads": 2,
"logical_processors": [
18,
6
]
},
{
"id": 9,
"index": 5,
"total_threads": 2,
"logical_processors": [
20,
8
]
}
],
"distances": [
10,
20
]
},
{
"id": 1,
"cores": [
{
"id": 0,
"index": 0,
"total_threads": 2,
"logical_processors": [
1,
13
]
},
{
"id": 10,
"index": 1,
"total_threads": 2,
"logical_processors": [
11,
23
]
},
{
"id": 1,
"index": 2,
"total_threads": 2,
"logical_processors": [
15,
3
]
},
{
"id": 2,
"index": 3,
"total_threads": 2,
"logical_processors": [
17,
5
]
},
{
"id": 8,
"index": 4,
"total_threads": 2,
"logical_processors": [
19,
7
]
},
{
"id": 9,
"index": 5,
"total_threads": 2,
"logical_processors": [
21,
9
]
}
],
"distances": [
20,
10
]
}
]
}`