From dd76e88984888527ac8f45e4aed006707aa67a9f Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Fri, 13 Mar 2026 14:29:39 +0100 Subject: [PATCH 1/5] Add changed crd spec and fix incompatible parts --- go.mod | 2 +- go.sum | 2 ++ internal/libvirt/libvirt.go | 32 ++++++++++++++++---------------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 2d79a67..b8c6387 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ go 1.26 require ( github.com/cert-manager/cert-manager v1.19.4 - github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20260309144200-9c8ed613a94c + github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20260313120621-e3699e2ccab9 github.com/coreos/go-systemd/v22 v22.7.0 github.com/digitalocean/go-libvirt v0.0.0-20260217163227-273eaa321819 github.com/godbus/dbus/v5 v5.2.2 diff --git a/go.sum b/go.sum index 7d38453..2d8a494 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20260309144200-9c8ed613a94c h1:KylfcJikSMWNJnuNfG1Od6fNUw4kQTjseP7khmwVlrM= github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20260309144200-9c8ed613a94c/go.mod h1:b0KmJdxvRI8UXlGe8cRm5BD8Tm2WhF7zSKMSIRGyVL4= +github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20260313120621-e3699e2ccab9 h1:fIQCfP6HTOMu9XqcRLUYeUCK2mPWcOkSqYVF9HUhQyE= +github.com/cobaltcore-dev/openstack-hypervisor-operator v0.0.0-20260313120621-e3699e2ccab9/go.mod h1:b0KmJdxvRI8UXlGe8cRm5BD8Tm2WhF7zSKMSIRGyVL4= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= diff --git a/internal/libvirt/libvirt.go b/internal/libvirt/libvirt.go index fd2bdeb..90711a6 100644 --- a/internal/libvirt/libvirt.go +++ b/internal/libvirt/libvirt.go @@ -458,14 +458,14 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error cellsById[cell.ID] = v1.Cell{ CellID: cell.ID, - Allocation: map[string]resource.Quantity{ + Allocation: map[v1.ResourceName]resource.Quantity{ // Will be updated below when we look at the domain infos. - "memory": *resource.NewQuantity(0, resource.BinarySI), - "cpu": *resource.NewQuantity(0, resource.DecimalSI), + v1.ResourceMemory: *resource.NewQuantity(0, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(0, resource.DecimalSI), }, - Capacity: map[string]resource.Quantity{ - "memory": memoryCapacity, - "cpu": cpuCapacity, + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: memoryCapacity, + v1.ResourceCPU: cpuCapacity, }, } } @@ -505,14 +505,14 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error domInfo.Name, memoryNode.CellID, ) } - memAllocCell := cell.Allocation["memory"] + memAllocCell := cell.Allocation[v1.ResourceMemory] // If a domain is using multiple memory cells, assume the // distribution across cells is even. nCells := int64(len(domInfo.NumaTune.MemNodes)) // is non-zero memAllocPerCell := *resource. NewQuantity(memAlloc.Value()/nCells, resource.BinarySI) memAllocCell.Add(memAllocPerCell) - cell.Allocation["memory"] = memAllocCell + cell.Allocation[v1.ResourceMemory] = memAllocCell cellsById[memoryNode.CellID] = cell } @@ -528,14 +528,14 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error domInfo.Name, cpuCell.ID, ) } - cpuAllocCell := cell.Allocation["cpu"] + cpuAllocCell := cell.Allocation[v1.ResourceCPU] // If a domain is using multiple cpu cells, assume the distribution // across cells is even. nCells := int64(len(domInfo.CPU.Numa.Cells)) // is non-zero cpuAllocPerCell := *resource. NewQuantity(cpuAlloc.Value()/nCells, resource.DecimalSI) cpuAllocCell.Add(cpuAllocPerCell) - cell.Allocation["cpu"] = cpuAllocCell + cell.Allocation[v1.ResourceCPU] = cpuAllocCell cellsById[cpuCell.ID] = cell } } @@ -544,12 +544,12 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error cellsAsSlice = append(cellsAsSlice, cell) } - newHv.Status.Capacity = make(map[string]resource.Quantity) - newHv.Status.Capacity["memory"] = *totalMemoryCapacity - newHv.Status.Capacity["cpu"] = *totalCpuCapacity - newHv.Status.Allocation = make(map[string]resource.Quantity) - newHv.Status.Allocation["memory"] = *totalMemoryAlloc - newHv.Status.Allocation["cpu"] = *totalCpuAlloc + newHv.Status.Capacity = make(map[v1.ResourceName]resource.Quantity) + newHv.Status.Capacity[v1.ResourceMemory] = *totalMemoryCapacity + newHv.Status.Capacity[v1.ResourceCPU] = *totalCpuCapacity + newHv.Status.Allocation = make(map[v1.ResourceName]resource.Quantity) + newHv.Status.Allocation[v1.ResourceMemory] = *totalMemoryAlloc + newHv.Status.Allocation[v1.ResourceCPU] = *totalCpuAlloc newHv.Status.Cells = cellsAsSlice return newHv, nil } From 59de598a12cf538a1050e0412d852cd0b479084a Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Fri, 13 Mar 2026 14:44:26 +0100 Subject: [PATCH 2/5] Implement addEffectiveAllocationCapacity --- internal/libvirt/libvirt.go | 41 ++++ internal/libvirt/libvirt_test.go | 344 +++++++++++++++++++++++++++++++ 2 files changed, 385 insertions(+) diff --git a/internal/libvirt/libvirt.go b/internal/libvirt/libvirt.go index 90711a6..9c28c63 100644 --- a/internal/libvirt/libvirt.go +++ b/internal/libvirt/libvirt.go @@ -274,6 +274,7 @@ func (l *LibVirt) Process(hv v1.Hypervisor) (v1.Hypervisor, error) { l.addCapabilities, l.addDomainCapabilities, l.addAllocationCapacity, + l.addEffectiveAllocationCapacity, } var err error for _, processor := range processors { @@ -553,3 +554,43 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error newHv.Status.Cells = cellsAsSlice return newHv, nil } + +// Add the effective allocation capacity to the hypervisor instance. +// +// The effective allocation capacity is calculated as the physical capacity and +// allocation times the applied overcommit ratio, or 1.0 by default. In case +// the resulting values are fractional, they are floored. +func (l *LibVirt) addEffectiveAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error) { + newHv := *old.DeepCopy() + // Initialize the EffectiveCapacity map if it's nil + if newHv.Status.EffectiveCapacity == nil { + newHv.Status.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) + } + for resourceName, capacity := range newHv.Status.Capacity { + overcommit, ok := newHv.Spec.Overcommit[resourceName] + if !ok { + overcommit = 1.0 + } + flooredValue := int64(float64(capacity.Value()) * overcommit) + effectiveCapacity := resource.NewQuantity(flooredValue, capacity.Format) + newHv.Status.EffectiveCapacity[resourceName] = *effectiveCapacity + } + // Also apply this to each cell. + for i, cell := range newHv.Status.Cells { + // Initialize the cell's EffectiveCapacity map if it's nil + if cell.EffectiveCapacity == nil { + cell.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) + } + for resourceName, capacity := range cell.Capacity { + overcommit, ok := newHv.Spec.Overcommit[resourceName] + if !ok { + overcommit = 1.0 + } + flooredValue := int64(float64(capacity.Value()) * overcommit) + effectiveCapacity := resource.NewQuantity(flooredValue, capacity.Format) + cell.EffectiveCapacity[resourceName] = *effectiveCapacity + } + newHv.Status.Cells[i] = cell + } + return newHv, nil +} diff --git a/internal/libvirt/libvirt_test.go b/internal/libvirt/libvirt_test.go index e104c58..03589d0 100644 --- a/internal/libvirt/libvirt_test.go +++ b/internal/libvirt/libvirt_test.go @@ -1552,3 +1552,347 @@ func TestRunEventLoop_ClosedEventChannel(t *testing.T) { t.Fatal("Event loop did not handle closed channel within timeout") } } + +func TestAddEffectiveAllocationCapacity_NoOvercommit(t *testing.T) { + // Test that when no overcommit is specified, effective capacity equals capacity + l := &LibVirt{} + + hv := v1.Hypervisor{ + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(64*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(16, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{ + { + CellID: 0, + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(64*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(16, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + }, + }, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // With no overcommit, effective capacity should equal physical capacity + expectedMemory := resource.NewQuantity(64*1024*1024*1024, resource.BinarySI) + memEffective := result.Status.EffectiveCapacity[v1.ResourceMemory] + if !memEffective.Equal(*expectedMemory) { + t.Errorf("Expected effective memory capacity %s, got %s", + expectedMemory.String(), memEffective.String()) + } + + expectedCPU := resource.NewQuantity(16, resource.DecimalSI) + cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !cpuEffective.Equal(*expectedCPU) { + t.Errorf("Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cpuEffective.String()) + } + + // Check cell effective capacity + if len(result.Status.Cells) != 1 { + t.Fatalf("Expected 1 cell, got %d", len(result.Status.Cells)) + } + cellMemEffective := result.Status.Cells[0].EffectiveCapacity[v1.ResourceMemory] + if !cellMemEffective.Equal(*expectedMemory) { + t.Errorf("Cell 0: Expected effective memory capacity %s, got %s", + expectedMemory.String(), cellMemEffective.String()) + } + cellCpuEffective := result.Status.Cells[0].EffectiveCapacity[v1.ResourceCPU] + if !cellCpuEffective.Equal(*expectedCPU) { + t.Errorf("Cell 0: Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cellCpuEffective.String()) + } +} + +func TestAddEffectiveAllocationCapacity_WithMemoryOvercommit(t *testing.T) { + // Test memory overcommit ratio of 1.5 + l := &LibVirt{} + + hv := v1.Hypervisor{ + Spec: v1.HypervisorSpec{ + Overcommit: map[v1.ResourceName]float64{ + v1.ResourceMemory: 1.5, + }, + }, + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(64*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(16, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{}, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // Memory should be 64 GiB * 1.5 = 96 GiB + expectedMemory := resource.NewQuantity(96*1024*1024*1024, resource.BinarySI) + memEffective := result.Status.EffectiveCapacity[v1.ResourceMemory] + if !memEffective.Equal(*expectedMemory) { + t.Errorf("Expected effective memory capacity %s, got %s", + expectedMemory.String(), memEffective.String()) + } + + // CPU should remain unchanged (no overcommit specified, defaults to 1.0) + expectedCPU := resource.NewQuantity(16, resource.DecimalSI) + cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !cpuEffective.Equal(*expectedCPU) { + t.Errorf("Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cpuEffective.String()) + } +} + +func TestAddEffectiveAllocationCapacity_WithCPUOvercommit(t *testing.T) { + // Test CPU overcommit ratio of 4.0 + l := &LibVirt{} + + hv := v1.Hypervisor{ + Spec: v1.HypervisorSpec{ + Overcommit: map[v1.ResourceName]float64{ + v1.ResourceCPU: 4.0, + }, + }, + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(64*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(16, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{}, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // Memory should remain unchanged (no overcommit specified, defaults to 1.0) + expectedMemory := resource.NewQuantity(64*1024*1024*1024, resource.BinarySI) + memEffective := result.Status.EffectiveCapacity[v1.ResourceMemory] + if !memEffective.Equal(*expectedMemory) { + t.Errorf("Expected effective memory capacity %s, got %s", + expectedMemory.String(), memEffective.String()) + } + + // CPU should be 16 * 4.0 = 64 + expectedCPU := resource.NewQuantity(64, resource.DecimalSI) + cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !cpuEffective.Equal(*expectedCPU) { + t.Errorf("Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cpuEffective.String()) + } +} + +func TestAddEffectiveAllocationCapacity_WithBothOvercommit(t *testing.T) { + // Test both memory and CPU overcommit + l := &LibVirt{} + + hv := v1.Hypervisor{ + Spec: v1.HypervisorSpec{ + Overcommit: map[v1.ResourceName]float64{ + v1.ResourceMemory: 2.0, + v1.ResourceCPU: 8.0, + }, + }, + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(32*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(8, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{}, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // Memory should be 32 GiB * 2.0 = 64 GiB + expectedMemory := resource.NewQuantity(64*1024*1024*1024, resource.BinarySI) + memEffective := result.Status.EffectiveCapacity[v1.ResourceMemory] + if !memEffective.Equal(*expectedMemory) { + t.Errorf("Expected effective memory capacity %s, got %s", + expectedMemory.String(), memEffective.String()) + } + + // CPU should be 8 * 8.0 = 64 + expectedCPU := resource.NewQuantity(64, resource.DecimalSI) + cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !cpuEffective.Equal(*expectedCPU) { + t.Errorf("Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cpuEffective.String()) + } +} + +func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { + // Test that fractional values are floored + l := &LibVirt{} + + hv := v1.Hypervisor{ + Spec: v1.HypervisorSpec{ + Overcommit: map[v1.ResourceName]float64{ + v1.ResourceCPU: 1.5, // 10 * 1.5 = 15 + }, + }, + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceCPU: *resource.NewQuantity(10, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{}, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // CPU should be floor(10 * 1.5) = floor(15) = 15 + expectedCPU := resource.NewQuantity(15, resource.DecimalSI) + cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !cpuEffective.Equal(*expectedCPU) { + t.Errorf("Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cpuEffective.String()) + } +} + +func TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown(t *testing.T) { + // Test that fractional values are floored down, not rounded + l := &LibVirt{} + + hv := v1.Hypervisor{ + Spec: v1.HypervisorSpec{ + Overcommit: map[v1.ResourceName]float64{ + v1.ResourceCPU: 1.9, // 10 * 1.9 = 19 + }, + }, + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceCPU: *resource.NewQuantity(10, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{}, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // CPU should be floor(10 * 1.9) = floor(19) = 19 + expectedCPU := resource.NewQuantity(19, resource.DecimalSI) + cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !cpuEffective.Equal(*expectedCPU) { + t.Errorf("Expected effective CPU capacity %s, got %s", + expectedCPU.String(), cpuEffective.String()) + } +} + +func TestAddEffectiveAllocationCapacity_MultipleCells(t *testing.T) { + // Test that overcommit is applied to each cell individually + l := &LibVirt{} + + hv := v1.Hypervisor{ + Spec: v1.HypervisorSpec{ + Overcommit: map[v1.ResourceName]float64{ + v1.ResourceMemory: 1.5, + v1.ResourceCPU: 2.0, + }, + }, + Status: v1.HypervisorStatus{ + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(128*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(32, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + Cells: []v1.Cell{ + { + CellID: 0, + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(64*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(16, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + }, + { + CellID: 1, + Capacity: map[v1.ResourceName]resource.Quantity{ + v1.ResourceMemory: *resource.NewQuantity(64*1024*1024*1024, resource.BinarySI), + v1.ResourceCPU: *resource.NewQuantity(16, resource.DecimalSI), + }, + EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), + }, + }, + }, + } + + result, err := l.addEffectiveAllocationCapacity(hv) + + if err != nil { + t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + } + + // Check total effective capacity + // Memory: 128 GiB * 1.5 = 192 GiB + expectedTotalMemory := resource.NewQuantity(192*1024*1024*1024, resource.BinarySI) + totalMemEffective := result.Status.EffectiveCapacity[v1.ResourceMemory] + if !totalMemEffective.Equal(*expectedTotalMemory) { + t.Errorf("Expected total effective memory capacity %s, got %s", + expectedTotalMemory.String(), totalMemEffective.String()) + } + + // CPU: 32 * 2.0 = 64 + expectedTotalCPU := resource.NewQuantity(64, resource.DecimalSI) + totalCpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] + if !totalCpuEffective.Equal(*expectedTotalCPU) { + t.Errorf("Expected total effective CPU capacity %s, got %s", + expectedTotalCPU.String(), totalCpuEffective.String()) + } + + // Check each cell's effective capacity + if len(result.Status.Cells) != 2 { + t.Fatalf("Expected 2 cells, got %d", len(result.Status.Cells)) + } + + for i, cell := range result.Status.Cells { + // Cell memory: 64 GiB * 1.5 = 96 GiB + expectedCellMemory := resource.NewQuantity(96*1024*1024*1024, resource.BinarySI) + cellMemEffective := cell.EffectiveCapacity[v1.ResourceMemory] + if !cellMemEffective.Equal(*expectedCellMemory) { + t.Errorf("Cell %d: Expected effective memory capacity %s, got %s", + i, expectedCellMemory.String(), cellMemEffective.String()) + } + + // Cell CPU: 16 * 2.0 = 32 + expectedCellCPU := resource.NewQuantity(32, resource.DecimalSI) + cellCpuEffective := cell.EffectiveCapacity[v1.ResourceCPU] + if !cellCpuEffective.Equal(*expectedCellCPU) { + t.Errorf("Cell %d: Expected effective CPU capacity %s, got %s", + i, expectedCellCPU.String(), cellCpuEffective.String()) + } + } +} From ae02d4bcfd566dd22b18215f0e7d58dc4b2e931d Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Fri, 13 Mar 2026 14:54:20 +0100 Subject: [PATCH 3/5] Actually test flooring --- internal/libvirt/libvirt_test.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/internal/libvirt/libvirt_test.go b/internal/libvirt/libvirt_test.go index 03589d0..66e550d 100644 --- a/internal/libvirt/libvirt_test.go +++ b/internal/libvirt/libvirt_test.go @@ -1745,18 +1745,19 @@ func TestAddEffectiveAllocationCapacity_WithBothOvercommit(t *testing.T) { } func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { - // Test that fractional values are floored + // Test that fractional values are floored (not rounded) + // 11 * 1.5 = 16.5, which should be floored to 16 l := &LibVirt{} hv := v1.Hypervisor{ Spec: v1.HypervisorSpec{ Overcommit: map[v1.ResourceName]float64{ - v1.ResourceCPU: 1.5, // 10 * 1.5 = 15 + v1.ResourceCPU: 1.5, // 11 * 1.5 = 16.5 }, }, Status: v1.HypervisorStatus{ Capacity: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: *resource.NewQuantity(10, resource.DecimalSI), + v1.ResourceCPU: *resource.NewQuantity(11, resource.DecimalSI), }, EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), Cells: []v1.Cell{}, @@ -1769,8 +1770,8 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) } - // CPU should be floor(10 * 1.5) = floor(15) = 15 - expectedCPU := resource.NewQuantity(15, resource.DecimalSI) + // CPU should be floor(11 * 1.5) = floor(16.5) = 16 + expectedCPU := resource.NewQuantity(16, resource.DecimalSI) cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] if !cpuEffective.Equal(*expectedCPU) { t.Errorf("Expected effective CPU capacity %s, got %s", @@ -1779,18 +1780,19 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { } func TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown(t *testing.T) { - // Test that fractional values are floored down, not rounded + // Test that fractional values are floored down, not rounded up + // 3 * 1.9 = 5.7, which should be floored to 5 (not rounded to 6) l := &LibVirt{} hv := v1.Hypervisor{ Spec: v1.HypervisorSpec{ Overcommit: map[v1.ResourceName]float64{ - v1.ResourceCPU: 1.9, // 10 * 1.9 = 19 + v1.ResourceCPU: 1.9, // 3 * 1.9 = 5.7 }, }, Status: v1.HypervisorStatus{ Capacity: map[v1.ResourceName]resource.Quantity{ - v1.ResourceCPU: *resource.NewQuantity(10, resource.DecimalSI), + v1.ResourceCPU: *resource.NewQuantity(3, resource.DecimalSI), }, EffectiveCapacity: make(map[v1.ResourceName]resource.Quantity), Cells: []v1.Cell{}, @@ -1803,8 +1805,8 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown(t *testing.T t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) } - // CPU should be floor(10 * 1.9) = floor(19) = 19 - expectedCPU := resource.NewQuantity(19, resource.DecimalSI) + // CPU should be floor(3 * 1.9) = floor(5.7) = 5 + expectedCPU := resource.NewQuantity(5, resource.DecimalSI) cpuEffective := result.Status.EffectiveCapacity[v1.ResourceCPU] if !cpuEffective.Equal(*expectedCPU) { t.Errorf("Expected effective CPU capacity %s, got %s", From 6586e8fcf3a4f000056e93d69459c3d56ed84ea6 Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Fri, 13 Mar 2026 14:56:01 +0100 Subject: [PATCH 4/5] Remove stale entries from map --- internal/libvirt/libvirt.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/libvirt/libvirt.go b/internal/libvirt/libvirt.go index 9c28c63..775f316 100644 --- a/internal/libvirt/libvirt.go +++ b/internal/libvirt/libvirt.go @@ -562,10 +562,8 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error // the resulting values are fractional, they are floored. func (l *LibVirt) addEffectiveAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error) { newHv := *old.DeepCopy() - // Initialize the EffectiveCapacity map if it's nil - if newHv.Status.EffectiveCapacity == nil { - newHv.Status.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) - } + // Always recreate the EffectiveCapacity map to remove stale entries + newHv.Status.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) for resourceName, capacity := range newHv.Status.Capacity { overcommit, ok := newHv.Spec.Overcommit[resourceName] if !ok { @@ -577,10 +575,8 @@ func (l *LibVirt) addEffectiveAllocationCapacity(old v1.Hypervisor) (v1.Hypervis } // Also apply this to each cell. for i, cell := range newHv.Status.Cells { - // Initialize the cell's EffectiveCapacity map if it's nil - if cell.EffectiveCapacity == nil { - cell.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) - } + // Always recreate the cell's EffectiveCapacity map to remove stale entries + cell.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) for resourceName, capacity := range cell.Capacity { overcommit, ok := newHv.Spec.Overcommit[resourceName] if !ok { From cd961645dd75bcf008e07496e893043473ffda1a Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Fri, 13 Mar 2026 14:58:51 +0100 Subject: [PATCH 5/5] Rename addEffectiveAllocationCapacity -> addEffectiveCapacity --- internal/libvirt/libvirt.go | 12 ++++----- internal/libvirt/libvirt_test.go | 42 ++++++++++++++++---------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/internal/libvirt/libvirt.go b/internal/libvirt/libvirt.go index 775f316..591a18c 100644 --- a/internal/libvirt/libvirt.go +++ b/internal/libvirt/libvirt.go @@ -274,7 +274,7 @@ func (l *LibVirt) Process(hv v1.Hypervisor) (v1.Hypervisor, error) { l.addCapabilities, l.addDomainCapabilities, l.addAllocationCapacity, - l.addEffectiveAllocationCapacity, + l.addEffectiveCapacity, } var err error for _, processor := range processors { @@ -555,12 +555,12 @@ func (l *LibVirt) addAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error return newHv, nil } -// Add the effective allocation capacity to the hypervisor instance. +// Add the effective capacity to the hypervisor instance. // -// The effective allocation capacity is calculated as the physical capacity and -// allocation times the applied overcommit ratio, or 1.0 by default. In case -// the resulting values are fractional, they are floored. -func (l *LibVirt) addEffectiveAllocationCapacity(old v1.Hypervisor) (v1.Hypervisor, error) { +// The effective capacity is calculated as the physical capacity times the +// applied overcommit ratio, or 1.0 by default. In case the resulting values +// are fractional, they are floored. +func (l *LibVirt) addEffectiveCapacity(old v1.Hypervisor) (v1.Hypervisor, error) { newHv := *old.DeepCopy() // Always recreate the EffectiveCapacity map to remove stale entries newHv.Status.EffectiveCapacity = make(map[v1.ResourceName]resource.Quantity) diff --git a/internal/libvirt/libvirt_test.go b/internal/libvirt/libvirt_test.go index 66e550d..9e4da3d 100644 --- a/internal/libvirt/libvirt_test.go +++ b/internal/libvirt/libvirt_test.go @@ -1553,7 +1553,7 @@ func TestRunEventLoop_ClosedEventChannel(t *testing.T) { } } -func TestAddEffectiveAllocationCapacity_NoOvercommit(t *testing.T) { +func TestAddEffectiveCapacity_NoOvercommit(t *testing.T) { // Test that when no overcommit is specified, effective capacity equals capacity l := &LibVirt{} @@ -1577,10 +1577,10 @@ func TestAddEffectiveAllocationCapacity_NoOvercommit(t *testing.T) { }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // With no overcommit, effective capacity should equal physical capacity @@ -1614,7 +1614,7 @@ func TestAddEffectiveAllocationCapacity_NoOvercommit(t *testing.T) { } } -func TestAddEffectiveAllocationCapacity_WithMemoryOvercommit(t *testing.T) { +func TestAddEffectiveCapacity_WithMemoryOvercommit(t *testing.T) { // Test memory overcommit ratio of 1.5 l := &LibVirt{} @@ -1634,10 +1634,10 @@ func TestAddEffectiveAllocationCapacity_WithMemoryOvercommit(t *testing.T) { }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // Memory should be 64 GiB * 1.5 = 96 GiB @@ -1657,7 +1657,7 @@ func TestAddEffectiveAllocationCapacity_WithMemoryOvercommit(t *testing.T) { } } -func TestAddEffectiveAllocationCapacity_WithCPUOvercommit(t *testing.T) { +func TestAddEffectiveCapacity_WithCPUOvercommit(t *testing.T) { // Test CPU overcommit ratio of 4.0 l := &LibVirt{} @@ -1677,10 +1677,10 @@ func TestAddEffectiveAllocationCapacity_WithCPUOvercommit(t *testing.T) { }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // Memory should remain unchanged (no overcommit specified, defaults to 1.0) @@ -1700,7 +1700,7 @@ func TestAddEffectiveAllocationCapacity_WithCPUOvercommit(t *testing.T) { } } -func TestAddEffectiveAllocationCapacity_WithBothOvercommit(t *testing.T) { +func TestAddEffectiveCapacity_WithBothOvercommit(t *testing.T) { // Test both memory and CPU overcommit l := &LibVirt{} @@ -1721,10 +1721,10 @@ func TestAddEffectiveAllocationCapacity_WithBothOvercommit(t *testing.T) { }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // Memory should be 32 GiB * 2.0 = 64 GiB @@ -1744,7 +1744,7 @@ func TestAddEffectiveAllocationCapacity_WithBothOvercommit(t *testing.T) { } } -func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { +func TestAddEffectiveCapacity_FractionalValuesFloored(t *testing.T) { // Test that fractional values are floored (not rounded) // 11 * 1.5 = 16.5, which should be floored to 16 l := &LibVirt{} @@ -1764,10 +1764,10 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // CPU should be floor(11 * 1.5) = floor(16.5) = 16 @@ -1779,7 +1779,7 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFloored(t *testing.T) { } } -func TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown(t *testing.T) { +func TestAddEffectiveCapacity_FractionalValuesFlooredDown(t *testing.T) { // Test that fractional values are floored down, not rounded up // 3 * 1.9 = 5.7, which should be floored to 5 (not rounded to 6) l := &LibVirt{} @@ -1799,10 +1799,10 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown(t *testing.T }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // CPU should be floor(3 * 1.9) = floor(5.7) = 5 @@ -1814,7 +1814,7 @@ func TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown(t *testing.T } } -func TestAddEffectiveAllocationCapacity_MultipleCells(t *testing.T) { +func TestAddEffectiveCapacity_MultipleCells(t *testing.T) { // Test that overcommit is applied to each cell individually l := &LibVirt{} @@ -1852,10 +1852,10 @@ func TestAddEffectiveAllocationCapacity_MultipleCells(t *testing.T) { }, } - result, err := l.addEffectiveAllocationCapacity(hv) + result, err := l.addEffectiveCapacity(hv) if err != nil { - t.Fatalf("addEffectiveAllocationCapacity() returned unexpected error: %v", err) + t.Fatalf("addEffectiveCapacity() returned unexpected error: %v", err) } // Check total effective capacity