From 0867ea3005f4da7640cad20cbd1c384eea809212 Mon Sep 17 00:00:00 2001
From: Philipp Matthes
Date: Mon, 16 Mar 2026 09:53:59 +0100
Subject: [PATCH] Use hv1 effective capacity for weighing + filtering
---
internal/scheduling/nova/integration_test.go | 2 +-
.../plugins/filters/filter_has_enough_capacity.go | 10 ++++++++--
.../filters/filter_has_enough_capacity_test.go | 2 +-
.../nova/plugins/weighers/kvm_binpack.go | 7 ++++---
.../nova/plugins/weighers/kvm_binpack_test.go | 8 ++++----
.../plugins/weighers/kvm_prefer_smaller_hosts.go | 15 ++++++++-------
.../weighers/kvm_prefer_smaller_hosts_test.go | 12 ++++++------
7 files changed, 32 insertions(+), 24 deletions(-)
diff --git a/internal/scheduling/nova/integration_test.go b/internal/scheduling/nova/integration_test.go
index 596d9f2ed..a1267c9c0 100644
--- a/internal/scheduling/nova/integration_test.go
+++ b/internal/scheduling/nova/integration_test.go
@@ -48,7 +48,7 @@ func newHypervisor(name, cpuCap, cpuAlloc, memCap, memAlloc string) *hv1.Hypervi
Name: name,
},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse(cpuCap),
hv1.ResourceMemory: resource.MustParse(memCap),
},
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
index 8852f6151..198f1a28f 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
@@ -56,8 +56,14 @@ func (s *FilterHasEnoughCapacity) Run(traceLog *slog.Logger, request api.Externa
return nil, err
}
for _, hv := range hvs.Items {
- // Start with the total capacity.
- freeResourcesByHost[hv.Name] = hv.Status.Capacity
+ // This case would be caught below, but we want to log this explicitly.
+ if hv.Status.EffectiveCapacity == nil {
+ traceLog.Warn("hypervisor with nil effective capacity, skipping", "host", hv.Name)
+ continue
+ }
+
+ // Start with the total effective capacity which is capacity * overcommit ratio.
+ freeResourcesByHost[hv.Name] = hv.Status.EffectiveCapacity
// Subtract allocated resources.
for resourceName, allocated := range hv.Status.Allocation {
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
index 504bbb523..4068cf900 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity_test.go
@@ -39,7 +39,7 @@ func newHypervisor(name, cpuCap, cpuAlloc, memCap, memAlloc string) *hv1.Hypervi
Name: name,
},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse(cpuCap),
hv1.ResourceMemory: resource.MustParse(memCap),
},
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_binpack.go b/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
index 3bed165f4..e1509a4cc 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
@@ -93,14 +93,15 @@ func (s *KVMBinpackStep) Run(traceLog *slog.Logger, request api.ExternalSchedule
var totalWeightedUtilization, totalWeight float64
for resourceName, weight := range s.Options.ResourceWeights {
- capacity, ok := hv.Status.Capacity[resourceName]
+ // Effective capacity = capacity * overcommit ratio.
+ capacity, ok := hv.Status.EffectiveCapacity[resourceName]
if !ok {
- traceLog.Warn("no capacity in status, skipping",
+ traceLog.Warn("no effective capacity in status, skipping",
"host", host, "resource", resourceName)
continue
}
if capacity.IsZero() {
- traceLog.Warn("capacity is zero, skipping",
+ traceLog.Warn("effective capacity is zero, skipping",
"host", host, "resource", resourceName)
continue
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go b/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go
index e867c5bf7..69e1aa9f6 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_binpack_test.go
@@ -22,7 +22,7 @@ func newHypervisor(name, capacityCPU, capacityMem, allocationCPU, allocationMem
Name: name,
},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse(capacityCPU),
hv1.ResourceMemory: resource.MustParse(capacityMem),
},
@@ -343,7 +343,7 @@ func TestKVMBinpackStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host1"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse("0"),
hv1.ResourceMemory: resource.MustParse("100Gi"),
},
@@ -371,7 +371,7 @@ func TestKVMBinpackStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host1"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse("100"),
},
Allocation: map[hv1.ResourceName]resource.Quantity{
@@ -397,7 +397,7 @@ func TestKVMBinpackStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host1"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
// No CPU capacity
},
Allocation: map[hv1.ResourceName]resource.Quantity{
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go
index 8bb5928ee..b65a5f75f 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts.go
@@ -81,9 +81,10 @@ func (s *KVMPreferSmallerHostsStep) Run(traceLog *slog.Logger, request api.Exter
if _, ok := result.Activations[hv.Name]; !ok {
continue
}
- capacity, ok := hv.Status.Capacity[resourceName]
+ // Effective capacity = capacity * overcommit ratio.
+ capacity, ok := hv.Status.EffectiveCapacity[resourceName]
if !ok {
- traceLog.Warn("hypervisor has no capacity for resource, skipping",
+ traceLog.Warn("hypervisor has no effective capacity for resource, skipping",
"host", hv.Name, "resource", resourceName)
continue
}
@@ -106,9 +107,9 @@ func (s *KVMPreferSmallerHostsStep) Run(traceLog *slog.Logger, request api.Exter
var totalWeightedScore, totalWeight float64
for resourceName, weight := range s.Options.ResourceWeights {
- capacity, ok := hv.Status.Capacity[resourceName]
+ capacity, ok := hv.Status.EffectiveCapacity[resourceName]
if !ok {
- traceLog.Warn("hypervisor has no capacity for resource, skipping",
+ traceLog.Warn("hypervisor has no effective capacity for resource, skipping",
"host", hv.Name, "resource", resourceName)
continue
}
@@ -117,14 +118,14 @@ func (s *KVMPreferSmallerHostsStep) Run(traceLog *slog.Logger, request api.Exter
largestCap := largest[resourceName]
if smallestCap == nil || largestCap == nil {
- traceLog.Warn("no capacity range found for resource, skipping",
+ traceLog.Warn("no effective capacity range found for resource, skipping",
"resource", resourceName)
continue
}
- // If all hosts have the same capacity for this resource, skip it
+ // If all hosts have the same effective capacity for this resource, skip it
if smallestCap.Cmp(*largestCap) == 0 {
- traceLog.Info("all hypervisors have the same capacity for resource, skipping",
+ traceLog.Info("all hypervisors have the same effective capacity for resource, skipping",
"resource", resourceName)
continue
}
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go
index 4a1b70e20..2ab2deb89 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_prefer_smaller_hosts_test.go
@@ -22,7 +22,7 @@ func newHypervisorWithCapacity(name, capacityCPU, capacityMem string) *hv1.Hyper
Name: name,
},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse(capacityCPU),
hv1.ResourceMemory: resource.MustParse(capacityMem),
},
@@ -376,7 +376,7 @@ func TestKVMPreferSmallerHostsStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host3"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceCPU: resource.MustParse("100"),
// No memory capacity
},
@@ -466,13 +466,13 @@ func TestKVMPreferSmallerHostsStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host1"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{},
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{},
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "host2"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{},
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{},
},
},
},
@@ -534,7 +534,7 @@ func TestKVMPreferSmallerHostsStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host1"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceMemory: resource.MustParse("64Gi"),
// No CPU
},
@@ -543,7 +543,7 @@ func TestKVMPreferSmallerHostsStep_Run(t *testing.T) {
{
ObjectMeta: metav1.ObjectMeta{Name: "host2"},
Status: hv1.HypervisorStatus{
- Capacity: map[hv1.ResourceName]resource.Quantity{
+ EffectiveCapacity: map[hv1.ResourceName]resource.Quantity{
hv1.ResourceMemory: resource.MustParse("128Gi"),
// No CPU
},