Skip to content

Calculate effective capacity from overcommit and capacity#70

Open
PhilippMatthes wants to merge 5 commits intomainfrom
add-overcommit-spec-and-status
Open

Calculate effective capacity from overcommit and capacity#70
PhilippMatthes wants to merge 5 commits intomainfrom
add-overcommit-spec-and-status

Conversation

@PhilippMatthes
Copy link
Member

@PhilippMatthes PhilippMatthes commented Mar 13, 2026

In cobaltcore-dev/openstack-hypervisor-operator#257 we add a spec field that can be used to specify the desired overcommit ratio for each resource name. This change applies the overcommit ratio to the hypervisor to its status, by calculating the effective capacity and exposing it through the hypervisor status.

Summary by CodeRabbit

  • New Features

    • Calculates effective capacity for CPU and memory using overcommit ratios, tracks per-cell effective capacity, and floors fractional results.
  • Chores

    • Updated a dependency version.
  • Tests

    • Added comprehensive unit tests covering overcommit scenarios, fractional-flooring behavior, and multi-cell aggregation.

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

Adds typed resource-key maps and a new effective-capacity processor that applies overcommit ratios (floored) to hypervisor and per-cell capacities; updates go.mod and adds unit tests covering overcommit and per-cell scenarios.

Changes

Cohort / File(s) Summary
Dependency Update
go.mod
Updated github.com/cobaltcore-dev/openstack-hypervisor-operator from v0.0.0-20260309144200-9c8ed613a94c to v0.0.0-20260313120621-e3699e2ccab9.
Resource type migration & effective-capacity logic
internal/libvirt/libvirt.go
Migrated allocation and capacity maps from map[string]resource.Quantity to map[v1.ResourceName]resource.Quantity; added Status.EffectiveCapacity (map[v1.ResourceName]resource.Quantity); introduced addEffectiveCapacity(old v1.Hypervisor) (v1.Hypervisor, error) and added it to the processing pipeline to apply overcommit ratios and floor fractional results.
Unit tests for effective capacity
internal/libvirt/libvirt_test.go
Added multiple tests for addEffectiveCapacity covering no overcommit, memory/cpu/both overcommit, fractional-flooring behavior, and multi-cell aggregation scenarios.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hop through maps with keys now true,
CPUs and memory counted anew.
Overcommit folded, fractions made small,
Cells add up neatly — I dance down the hall.
Tests nibble edge cases, one and all.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing effective capacity calculation based on overcommit ratios and capacity values, which is the primary focus across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Docstrings were successfully generated.
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-overcommit-spec-and-status
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@PhilippMatthes PhilippMatthes marked this pull request as ready for review March 13, 2026 13:49
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/libvirt/libvirt.go (1)

560-562: Align docstring with implementation.

Line 560 states "capacity and allocation", but the method uses only capacity. Update wording to avoid misleading future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/libvirt/libvirt.go` around lines 560 - 562, The docstring above the
method that calculates the effective allocation capacity currently says
"capacity and allocation" but the implementation only uses physical capacity;
update that comment (the block starting "The effective allocation capacity is
calculated...") to state that it uses physical capacity multiplied by the
applied overcommit ratio (default 1.0), and note fractional results are
floored—remove the misleading "and allocation" phrasing so the docstring matches
the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/libvirt/libvirt_test.go`:
- Around line 1747-1812: The tests
TestAddEffectiveAllocationCapacity_FractionalValuesFloored and
TestAddEffectiveAllocationCapacity_FractionalValuesFlooredDown don't actually
produce fractional products (10*1.5 and 10*1.9 are integers), so change the
hypervisor Capacity values or Overcommit factors to produce non-integer results
(e.g., set Capacity CPU to a value like 11 or 3 so 11*1.5=16.5 or 3*1.9=5.7) and
assert the EffectiveCapacity from addEffectiveAllocationCapacity is floored (16
and 5 respectively); update the expected resource.Quantity values accordingly
and keep references to the same tests and addEffectiveAllocationCapacity
function.

In `@internal/libvirt/libvirt.go`:
- Around line 565-593: The EffectiveCapacity maps are only initialized when nil
which leaves stale keys from prior status; instead, always recreate the maps
before populating: replace the nil-checks for newHv.Status.EffectiveCapacity and
cell.EffectiveCapacity in the function that computes capacities so you
unconditionally set newHv.Status.EffectiveCapacity =
make(map[v1.ResourceName]resource.Quantity) and cell.EffectiveCapacity =
make(map[v1.ResourceName]resource.Quantity) respectively, then populate them
from newHv.Status.Capacity and cell.Capacity using the existing overcommit logic
(refer to newHv.Status.EffectiveCapacity, newHv.Spec.Overcommit, and the loop
over newHv.Status.Cells/cell.Capacity) so stale entries are removed.

---

Nitpick comments:
In `@internal/libvirt/libvirt.go`:
- Around line 560-562: The docstring above the method that calculates the
effective allocation capacity currently says "capacity and allocation" but the
implementation only uses physical capacity; update that comment (the block
starting "The effective allocation capacity is calculated...") to state that it
uses physical capacity multiplied by the applied overcommit ratio (default 1.0),
and note fractional results are floored—remove the misleading "and allocation"
phrasing so the docstring matches the implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e6319af8-b68c-4884-9d15-3ee4d6959f17

📥 Commits

Reviewing files that changed from the base of the PR and between 8e5b18e and 59de598.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (3)
  • go.mod
  • internal/libvirt/libvirt.go
  • internal/libvirt/libvirt_test.go

@github-actions
Copy link

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt 42.89% (+2.66%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt/libvirt.go 73.78% (+2.56%) 225 (+20) 166 (+20) 59 👍

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/cobaltcore-dev/kvm-node-agent/internal/libvirt/libvirt_test.go

@PhilippMatthes PhilippMatthes changed the title Add overcommit spec and status Calculate effective capacity from overcommit and capacity Mar 13, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/libvirt/libvirt_test.go (1)

1566-1575: Add a regression assertion for stale key removal.

Given the stale-entry fix in production code, it’s worth pre-populating EffectiveCapacity with an extra key and asserting it is removed after recomputation (hypervisor + cell map).

Also applies to: 1601-1614

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/libvirt/libvirt_test.go` around lines 1566 - 1575, Pre-populate the
EffectiveCapacity map with an extra stale key (use v1.ResourceName to create a
unique key) on the test Cell and on the hypervisor setup (the EffectiveCapacity
fields you already construct) before you run the recomputation step; then invoke
the same hypervisor+cell map recompute used in this test and add an assertion
that the stale key is no longer present in the Cell.EffectiveCapacity and the
hypervisor EffectiveCapacity maps. Reference the EffectiveCapacity and
Cells->Cell{CellID, Capacity, EffectiveCapacity} structures when locating where
to insert the stale key and where to assert its removal after the recompute.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/libvirt/libvirt.go`:
- Around line 568-573: The code uses newHv.Spec.Overcommit[resourceName]
directly to compute effectiveCapacity (variables: overcommit, capacity,
flooredValue, effectiveCapacity) and must validate the ratio before
multiplication; update the logic that reads overcommit to reject NaN, ±Inf, and
non-positive values (<=0) and fall back to a safe default (e.g., 1.0) or clamp
to a sensible minimum, then compute flooredValue =
int64(float64(capacity.Value()) * validOvercommit) and ensure the resulting
flooredValue is not negative before creating resource.NewQuantity; apply the
same validation wherever overcommit is read (the other block around the later
use) so invalid ratios cannot produce undefined or negative capacities.

---

Nitpick comments:
In `@internal/libvirt/libvirt_test.go`:
- Around line 1566-1575: Pre-populate the EffectiveCapacity map with an extra
stale key (use v1.ResourceName to create a unique key) on the test Cell and on
the hypervisor setup (the EffectiveCapacity fields you already construct) before
you run the recomputation step; then invoke the same hypervisor+cell map
recompute used in this test and add an assertion that the stale key is no longer
present in the Cell.EffectiveCapacity and the hypervisor EffectiveCapacity maps.
Reference the EffectiveCapacity and Cells->Cell{CellID, Capacity,
EffectiveCapacity} structures when locating where to insert the stale key and
where to assert its removal after the recompute.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 404fa40e-d442-46ce-a0c9-8ea3c3eac652

📥 Commits

Reviewing files that changed from the base of the PR and between 59de598 and 6586e8f.

📒 Files selected for processing (2)
  • internal/libvirt/libvirt.go
  • internal/libvirt/libvirt_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
internal/libvirt/libvirt.go (2)

563-591: Factor the effective-capacity transform into one helper.

The hypervisor-level and per-cell loops duplicate the same overcommit-to-quantity calculation. Pulling that into a small helper reduces drift the next time this logic changes.

♻️ Suggested refactor
 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)
-	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
-	}
+	calculateEffectiveCapacity := func(
+		capacities map[v1.ResourceName]resource.Quantity,
+	) map[v1.ResourceName]resource.Quantity {
+		effective := make(map[v1.ResourceName]resource.Quantity, len(capacities))
+		for resourceName, capacity := range capacities {
+			overcommit := 1.0
+			if configured, ok := newHv.Spec.Overcommit[resourceName]; ok {
+				overcommit = configured
+			}
+			effective[resourceName] = *resource.NewQuantity(
+				int64(float64(capacity.Value())*overcommit),
+				capacity.Format,
+			)
+		}
+		return effective
+	}
+
+	newHv.Status.EffectiveCapacity = calculateEffectiveCapacity(newHv.Status.Capacity)
 	// Also apply this to each cell.
 	for i, cell := range newHv.Status.Cells {
-		// 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 {
-				overcommit = 1.0
-			}
-			flooredValue := int64(float64(capacity.Value()) * overcommit)
-			effectiveCapacity := resource.NewQuantity(flooredValue, capacity.Format)
-			cell.EffectiveCapacity[resourceName] = *effectiveCapacity
-		}
+		cell.EffectiveCapacity = calculateEffectiveCapacity(cell.Capacity)
 		newHv.Status.Cells[i] = cell
 	}
 	return newHv, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/libvirt/libvirt.go` around lines 563 - 591, The addEffectiveCapacity
function duplicates the overcommit-to-quantity calculation for the hypervisor
and for each cell; extract that logic into a small helper (e.g.,
computeEffectiveCapacity or applyOvercommitToCapacity) that accepts a source
map[v1.ResourceName]resource.Quantity and the overcommit map
(newHv.Spec.Overcommit) and returns a map[v1.ResourceName]resource.Quantity of
floored effective quantities (preserving each capacity.Format and using
resource.NewQuantity(int64(float64(cap.Value())*overcommit), cap.Format) with
default overcommit 1.0). Replace the inline loops in addEffectiveCapacity with
calls to this helper for newHv.Status.Capacity and for each cell.Capacity,
assign the returned maps to newHv.Status.EffectiveCapacity and
cell.EffectiveCapacity respectively, and keep the existing newHv.Status.Cells[i]
= cell update.

543-554: Sort Status.Cells before persisting it.

Line 544 builds the slice from a map, so cell order can flip across reconciles. That makes status updates noisy and multi-cell assertions order-dependent.

💡 Suggested change
 import (
 	"context"
 	"errors"
 	"fmt"
 	"os"
 	"reflect"
+	"sort"
 	"sync"
 	"time"
 	cellsAsSlice := []v1.Cell{}
 	for _, cell := range cellsById {
 		cellsAsSlice = append(cellsAsSlice, cell)
 	}
+	sort.Slice(cellsAsSlice, func(i, j int) bool {
+		return cellsAsSlice[i].CellID < cellsAsSlice[j].CellID
+	})
 
 	newHv.Status.Capacity = make(map[v1.ResourceName]resource.Quantity)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/libvirt/libvirt.go` around lines 543 - 554, The slice built from
cellsById can vary order across reconciles; sort cellsAsSlice before assigning
to newHv.Status.Cells to ensure deterministic status updates. After constructing
cellsAsSlice (from cellsById) call sort.SliceStable on it using a stable unique
key (e.g. cell.ID or cell.Name) to compare elements, then assign the sorted
slice to newHv.Status.Cells; also ensure the sort package is imported. This
affects the code around cellsAsSlice and newHv.Status.Cells in
internal/libvirt/libvirt.go.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/libvirt/libvirt.go`:
- Around line 563-591: The addEffectiveCapacity function duplicates the
overcommit-to-quantity calculation for the hypervisor and for each cell; extract
that logic into a small helper (e.g., computeEffectiveCapacity or
applyOvercommitToCapacity) that accepts a source
map[v1.ResourceName]resource.Quantity and the overcommit map
(newHv.Spec.Overcommit) and returns a map[v1.ResourceName]resource.Quantity of
floored effective quantities (preserving each capacity.Format and using
resource.NewQuantity(int64(float64(cap.Value())*overcommit), cap.Format) with
default overcommit 1.0). Replace the inline loops in addEffectiveCapacity with
calls to this helper for newHv.Status.Capacity and for each cell.Capacity,
assign the returned maps to newHv.Status.EffectiveCapacity and
cell.EffectiveCapacity respectively, and keep the existing newHv.Status.Cells[i]
= cell update.
- Around line 543-554: The slice built from cellsById can vary order across
reconciles; sort cellsAsSlice before assigning to newHv.Status.Cells to ensure
deterministic status updates. After constructing cellsAsSlice (from cellsById)
call sort.SliceStable on it using a stable unique key (e.g. cell.ID or
cell.Name) to compare elements, then assign the sorted slice to
newHv.Status.Cells; also ensure the sort package is imported. This affects the
code around cellsAsSlice and newHv.Status.Cells in internal/libvirt/libvirt.go.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61a03da2-6469-48a4-b747-47fe3a4db5c7

📥 Commits

Reviewing files that changed from the base of the PR and between 6586e8f and cd96164.

📒 Files selected for processing (2)
  • internal/libvirt/libvirt.go
  • internal/libvirt/libvirt_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/libvirt/libvirt_test.go

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Caution

Docstrings generation - FAILED

No docstrings were generated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants