Skip to content
36 changes: 36 additions & 0 deletions helm/bundles/cortex-nova/templates/pipelines_kvm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -557,4 +557,40 @@ spec:
VM is allocated get a higher weight, encouraging placement on
pre-reserved failover capacity. For non-evacuation requests, this
weigher has no effect.
---
apiVersion: cortex.cloud/v1alpha1
kind: Pipeline
metadata:
name: kvm-report-capacity
spec:
schedulingDomain: nova
description: |
This pipeline is used by the Liquid capacity reporter to determine the
theoretical maximum capacity of each flavor group per availability zone,
as if all hosts were completely empty. It ignores current VM allocations
and all reservation blockings so that only raw hardware capacity is
considered.
type: filter-weigher
createDecisions: false
# Fetch all placement candidates, ignoring nova's preselection.
ignorePreselection: true
filters:
- name: filter_correct_az
description: |
Restricts host candidates to the requested availability zone.
- name: filter_has_enough_capacity
description: |
Filters hosts that cannot fit the flavor based on raw hardware capacity.
VM allocations and all reservation types are ignored to represent an
empty datacenter scenario.
params:
- {key: ignoreAllocations, boolValue: true}
- {key: ignoredReservationTypes, stringListValue: ["CommittedResourceReservation", "FailoverReservation"]}
- name: filter_has_requested_traits
description: |
Ensures hosts have the hardware traits required by the flavor.
- name: filter_status_conditions
description: |
Excludes hosts that are not ready or are disabled.
weighers: []
{{- end }}
4 changes: 4 additions & 0 deletions helm/bundles/cortex-nova/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ cortex-scheduling-controllers:
"*": "kvm-general-purpose-load-balancing" # Catch-all fallback
# Default pipeline for CR reservations when no CommittedResourceFlavorGroupPipelines entry matches
committedResourcePipelineDefault: "kvm-general-purpose-load-balancing"
# Pipeline used for currently-available capacity check (respects VM allocations and reservations)
reportCapacityCurrentPipeline: "kvm-general-purpose-load-balancing-all-filters-enabled"
# Pipeline used for total theoretical capacity check (ignores VM allocations and reservations)
reportCapacityTotalPipeline: "kvm-report-capacity"
# How often to re-verify active reservations
# 5m = 300000000000 nanoseconds
committedResourceRequeueIntervalActive: 300000000000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ type FilterHasEnoughCapacityOpts struct {
// When a reservation type is in this list, its capacity is not blocked.
// Default: empty (all reservation types are considered)
IgnoredReservationTypes []v1alpha1.ReservationType `json:"ignoredReservationTypes,omitempty"`

// IgnoreAllocations skips subtracting current VM allocations from host capacity.
// When true, only raw hardware capacity is considered (empty datacenter scenario).
IgnoreAllocations bool `json:"ignoreAllocations,omitempty"`
}

func (FilterHasEnoughCapacityOpts) Validate() error { return nil }
Expand Down Expand Up @@ -71,18 +75,20 @@ func (s *FilterHasEnoughCapacity) Run(traceLog *slog.Logger, request api.Externa
freeResourcesByHost[hv.Name] = hv.Status.EffectiveCapacity
}

// Subtract allocated resources.
for resourceName, allocated := range hv.Status.Allocation {
free, ok := freeResourcesByHost[hv.Name][resourceName]
if !ok {
traceLog.Error(
"hypervisor with allocation for unknown resource",
"host", hv.Name, "resource", resourceName,
)
continue
// Subtract allocated resources (skip when ignoring allocations for empty-datacenter capacity queries).
if !s.Options.IgnoreAllocations {
for resourceName, allocated := range hv.Status.Allocation {
free, ok := freeResourcesByHost[hv.Name][resourceName]
if !ok {
traceLog.Error(
"hypervisor with allocation for unknown resource",
"host", hv.Name, "resource", resourceName,
)
continue
}
free.Sub(allocated)
freeResourcesByHost[hv.Name][resourceName] = free
}
free.Sub(allocated)
freeResourcesByHost[hv.Name][resourceName] = free
}
}

Expand Down
3 changes: 3 additions & 0 deletions internal/scheduling/reservations/commitments/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"github.com/cobaltcore-dev/cortex/internal/scheduling/nova"
"github.com/go-logr/logr"
"github.com/prometheus/client_golang/prometheus"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -78,3 +79,5 @@
http.Error(w, "Not found", http.StatusNotFound)
}
}

var commitmentApiLog = ctrl.Log.WithName("commitment_api")

Check failure on line 83 in internal/scheduling/reservations/commitments/api.go

View workflow job for this annotation

GitHub Actions / CodeQL

var commitmentApiLog is unused (unused)

Check failure on line 83 in internal/scheduling/reservations/commitments/api.go

View workflow job for this annotation

GitHub Actions / Checks

var commitmentApiLog is unused (unused)
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/cobaltcore-dev/cortex/internal/scheduling/reservations"
"github.com/google/uuid"
"github.com/sapcc/go-api-declarations/liquid"
)

// handles POST /commitments/v1/report-capacity requests from Limes:
Expand Down Expand Up @@ -50,16 +49,9 @@ func (api *HTTPAPI) HandleReportCapacity(w http.ResponseWriter, r *http.Request)

logger.V(1).Info("processing report capacity request")

// Parse request body (may be empty or contain ServiceCapacityRequest)
var req liquid.ServiceCapacityRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
// Empty body is acceptable for capacity reports
req = liquid.ServiceCapacityRequest{}
}

// Calculate capacity
calculator := NewCapacityCalculator(api.client)
report, err := calculator.CalculateCapacity(ctx, req)
calculator := NewCapacityCalculator(api.client, api.config)
report, err := calculator.CalculateCapacity(ctx)
if err != nil {
logger.Error(err, "failed to calculate capacity")
statusCode = http.StatusInternalServerError
Expand Down
Loading
Loading