From e08b060ec05839dd671c25c5ec476f10c0485341 Mon Sep 17 00:00:00 2001
From: Philipp Matthes
Date: Tue, 4 Aug 2026 14:36:40 +0200
Subject: [PATCH] Skip non-candidate hypervisors in nova filters and weighers
Signed-off-by: Philipp Matthes
---
.../bundles/cortex-nova/templates/alerts.yaml | 20 +++
.../cortex-nova/templates/pipelines_kvm.yaml | 120 ++++++++--------
.../filters/filter_aggregate_metadata.go | 3 +
.../filters/filter_allowed_projects.go | 3 +
.../plugins/filters/filter_capabilities.go | 7 +
.../filters/filter_capabilities_test.go | 128 +++++++++++++++++-
.../nova/plugins/filters/filter_correct_az.go | 3 +
.../filters/filter_external_customer.go | 3 +
.../filters/filter_has_accelerators.go | 3 +
.../filters/filter_has_enough_capacity.go | 3 +
.../filters/filter_has_requested_traits.go | 3 +
.../filter_instance_group_anti_affinity.go | 3 +
.../filters/filter_requested_destination.go | 3 +
.../filters/filter_status_conditions.go | 3 +
.../nova/plugins/weighers/kvm_binpack.go | 3 +
.../kvm_instance_group_soft_affinity.go | 3 +
16 files changed, 247 insertions(+), 64 deletions(-)
diff --git a/helm/bundles/cortex-nova/templates/alerts.yaml b/helm/bundles/cortex-nova/templates/alerts.yaml
index 6c1a10250..133d8468a 100644
--- a/helm/bundles/cortex-nova/templates/alerts.yaml
+++ b/helm/bundles/cortex-nova/templates/alerts.yaml
@@ -363,6 +363,26 @@ spec:
property format has changed. Investigate the image metadata of the affected
requests.
+ - alert: CortexNovaCapabilitiesUnknownHypervisorType
+ expr: |
+ sum by (pipeline, step, hypervisor_type) (rate(cortex_filter_weigher_pipeline_step_events_total{service="cortex-nova-metrics", event="filter_capabilities_unknown_hypervisor_type"}[5m])) > 0.1
+ for: 15m
+ labels:
+ context: scheduling
+ dashboard: cortex-status-dashboard/cortex-status-dashboard
+ service: cortex
+ severity: warning
+ support_group: workload-management
+ playbook: docs/support/playbook/cortex/alerts/scheduling
+ annotations:
+ summary: "Nova capabilities filter frequently encounters unknown hypervisor types"
+ description: >
+ The `filter_capabilities` step in pipeline `{{ "{{" }} $labels.pipeline {{ "}}" }}`
+ is frequently unable to determine the hypervisor type
+ (`{{ "{{" }} $labels.hypervisor_type {{ "}}" }}`) for candidate hosts.
+ This may indicate that hypervisors are reporting incomplete domain capabilities.
+ Investigate the affected hosts and hypervisor operator status.
+
{{- if .Values.kvm.enabled }}
- alert: CortexNovaDoesntFindValidKVMHosts
expr: sum by (az, hvtype) (increase(cortex_vm_faults{hvtype=~"CH|QEMU",faultmsg=~".*No valid host was found.*",faultmsg!~".*No such host.*"}[5m])) > 0
diff --git a/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml b/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml
index fa160c508..e72905e27 100644
--- a/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml
+++ b/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml
@@ -17,6 +17,11 @@ spec:
# Fetch all placement candidates, ignoring nova's preselection.
ignorePreselection: true
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_correct_az
description: |
This step will filter out hosts whose aggregate information indicates they
@@ -31,11 +36,6 @@ spec:
This step will filter out hosts for which the hypervisor type does not match
the one specified in the image properties, for example, filtering out all
known KVM hypervisors if the image requires a different hypervisor type.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_capabilities
description: |
This step will filter out hosts that do not meet the compute capabilities
@@ -131,8 +131,8 @@ spec:
instance group are already running on that host. The more instances of the
same group on a host, the lower (for soft-anti-affinity) or higher
(for soft-affinity) the weight, which makes it less likely or more likely,
- respectively, for the scheduler to choose that host for new instances of
- the same group.
+ respectively, for the scheduler to choose that host for new instances of the
+ same group.
- name: kvm_binpack
multiplier: -1.0 # inverted = balancing
params:
@@ -174,6 +174,11 @@ spec:
# Fetch all placement candidates, ignoring nova's preselection.
ignorePreselection: true
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_correct_az
description: |
This step will filter out hosts whose aggregate information indicates they
@@ -188,11 +193,6 @@ spec:
This step will filter out hosts for which the hypervisor type does not match
the one specified in the image properties, for example, filtering out all
known KVM hypervisors if the image requires a different hypervisor type.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_capabilities
description: |
This step will filter out hosts that do not meet the compute capabilities
@@ -287,8 +287,8 @@ spec:
instance group are already running on that host. The more instances of the
same group on a host, the lower (for soft-anti-affinity) or higher
(for soft-affinity) the weight, which makes it less likely or more likely,
- respectively, for the scheduler to choose that host for new instances of
- the same group.
+ respectively, for the scheduler to choose that host for new instances of the
+ same group.
- name: kvm_binpack
params:
- {key: resourceWeights, floatMapValue: {"memory": 1.0}}
@@ -323,11 +323,20 @@ spec:
type: filter-weigher
ignorePreselection: true
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_host_instructions
description: |
This step will consider the `ignore_hosts` and `force_hosts` instructions
from the nova scheduler request spec to filter out or exclusively allow
certain hosts.
+ - name: filter_correct_az
+ description: |
+ This step will filter out hosts whose aggregate information indicates they
+ are not placed in the requested availability zone.
- name: filter_has_enough_capacity
description: |
This step will filter out hosts that do not have enough available capacity
@@ -345,15 +354,6 @@ spec:
description: |
This step will filter out hosts without the trait `COMPUTE_ACCELERATORS` if
the nova flavor extra specs request accelerators via "accel:device_profile".
- - name: filter_correct_az
- description: |
- This step will filter out hosts whose aggregate information indicates they
- are not placed in the requested availability zone.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_allowed_projects
description: |
This step filters hosts based on allowed projects defined in the
@@ -454,11 +454,21 @@ spec:
on a host, this pipeline validates the host is still suitable for the VM.
type: filter-weigher
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_host_instructions
description: |
This step will consider the `ignore_hosts` and `force_hosts` instructions
from the nova scheduler request spec to filter out or exclusively allow
certain hosts.
+ - name: filter_correct_az
+ description: |
+ This step will filter out hosts whose aggregate information indicates they
+ are not placed in the requested availability zone. This ensures VMs can
+ only reuse reservations in their own AZ.
- name: filter_has_requested_traits
description: |
This step filters hosts that do not have the requested traits given by the
@@ -469,16 +479,6 @@ spec:
description: |
This step will filter out hosts without the trait `COMPUTE_ACCELERATORS` if
the nova flavor extra specs request accelerators via "accel:device_profile".
- - name: filter_correct_az
- description: |
- This step will filter out hosts whose aggregate information indicates they
- are not placed in the requested availability zone. This ensures VMs can
- only reuse reservations in their own AZ.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_external_customer
description: |
This step prefix-matches the domain name for external customer domains and
@@ -517,11 +517,21 @@ spec:
fails for any VM, the reservation is deleted (nack).
type: filter-weigher
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_host_instructions
description: |
This step will consider the `ignore_hosts` and `force_hosts` instructions
from the nova scheduler request spec to filter out or exclusively allow
certain hosts.
+ - name: filter_correct_az
+ description: |
+ This step will filter out hosts whose aggregate information indicates they
+ are not placed in the requested availability zone. This ensures reservation
+ validation respects AZ boundaries.
- name: filter_has_enough_capacity
description: |
This step will filter out hosts that do not have enough available capacity
@@ -538,16 +548,6 @@ spec:
description: |
This step will filter out hosts without the trait `COMPUTE_ACCELERATORS` if
the nova flavor extra specs request accelerators via "accel:device_profile".
- - name: filter_correct_az
- description: |
- This step will filter out hosts whose aggregate information indicates they
- are not placed in the requested availability zone. This ensures reservation
- validation respects AZ boundaries.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_external_customer
description: |
This step prefix-matches the domain name for external customer domains and
@@ -592,6 +592,9 @@ spec:
# Fetch all placement candidates, ignoring nova's preselection.
ignorePreselection: true
filters:
+ - name: filter_status_conditions
+ description: |
+ Excludes hosts that are not ready or are disabled.
- name: filter_correct_az
description: |
Restricts host candidates to the requested availability zone.
@@ -610,9 +613,6 @@ spec:
description: |
Ensures hosts meet the compute capabilities required by the flavor
extra specs (e.g., architecture, maxphysaddr bits).
- - name: filter_status_conditions
- description: |
- Excludes hosts that are not ready or are disabled.
weighers: []
---
apiVersion: cortex.cloud/v1alpha1
@@ -630,6 +630,11 @@ spec:
ignorePreselection: true
createHistory: false
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_correct_az
description: |
This step will filter out hosts whose aggregate information indicates they
@@ -644,11 +649,6 @@ spec:
This step will filter out hosts for which the hypervisor type does not match
the one specified in the image properties, for example, filtering out all
known KVM hypervisors if the image requires a different hypervisor type.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_capabilities
description: |
This step will filter out hosts that do not meet the compute capabilities
@@ -746,8 +746,8 @@ spec:
instance group are already running on that host. The more instances of the
same group on a host, the lower (for soft-anti-affinity) or higher
(for soft-affinity) the weight, which makes it less likely or more likely,
- respectively, for the scheduler to choose that host for new instances of
- the same group.
+ respectively, for the scheduler to choose that host for new instances of the
+ same group.
- name: kvm_binpack
multiplier: -1.0 # inverted = balancing
params:
@@ -787,6 +787,11 @@ spec:
ignorePreselection: true
createHistory: false
filters:
+ - name: filter_status_conditions
+ description: |
+ This step will filter out hosts for which the hypervisor status conditions
+ do not meet the expected values, for example, that the hypervisor is ready
+ and not disabled.
- name: filter_correct_az
description: |
This step will filter out hosts whose aggregate information indicates they
@@ -801,11 +806,6 @@ spec:
This step will filter out hosts for which the hypervisor type does not match
the one specified in the image properties, for example, filtering out all
known KVM hypervisors if the image requires a different hypervisor type.
- - name: filter_status_conditions
- description: |
- This step will filter out hosts for which the hypervisor status conditions
- do not meet the expected values, for example, that the hypervisor is ready
- and not disabled.
- name: filter_capabilities
description: |
This step will filter out hosts that do not meet the compute capabilities
@@ -902,8 +902,8 @@ spec:
instance group are already running on that host. The more instances of the
same group on a host, the lower (for soft-anti-affinity) or higher
(for soft-affinity) the weight, which makes it less likely or more likely,
- respectively, for the scheduler to choose that host for new instances of
- the same group.
+ respectively, for the scheduler to choose that host for new instances of the
+ same group.
- name: kvm_binpack
params:
- {key: resourceWeights, floatMapValue: {"memory": 1.0}}
diff --git a/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go b/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go
index 08a7e55c3..f65c37379 100644
--- a/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go
+++ b/internal/scheduling/nova/plugins/filters/filter_aggregate_metadata.go
@@ -40,6 +40,9 @@ func (s *FilterAggregateMetadata) Run(traceLog *slog.Logger, request api.Externa
restrictedProjectsByHost := make(map[string][]string)
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
for _, aggregate := range hv.Status.Aggregates {
// Any metadata key prefixed with "filter_tenant_id" restricts the
// aggregate to the referenced projects. Multiple numbered keys (e.g.
diff --git a/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go b/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go
index bd0849cf1..536129b71 100644
--- a/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go
+++ b/internal/scheduling/nova/plugins/filters/filter_allowed_projects.go
@@ -42,6 +42,9 @@ func (s *FilterAllowedProjectsStep) Run(traceLog *slog.Logger, request api.Exter
}
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
if len(hv.Spec.AllowedProjects) == 0 {
// Hypervisor is available for all projects.
traceLog.Info("host allows all projects, keeping", "host", hv.Name)
diff --git a/internal/scheduling/nova/plugins/filters/filter_capabilities.go b/internal/scheduling/nova/plugins/filters/filter_capabilities.go
index cda9a9a20..7facd24ac 100644
--- a/internal/scheduling/nova/plugins/filters/filter_capabilities.go
+++ b/internal/scheduling/nova/plugins/filters/filter_capabilities.go
@@ -93,9 +93,16 @@ func (s *FilterCapabilitiesStep) Run(traceLog *slog.Logger, request api.External
hvCaps := make(map[string]map[string]string)
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
caps, err := hvToNovaCapabilities(hv)
if err != nil {
traceLog.Warn("hypervisor has unknown capabilities, using empty defaults", "host", hv.Name, "error", err)
+ result.Events = append(result.Events, lib.FilterWeigherPipelineStepEvent{
+ Name: "filter_capabilities_unknown_hypervisor_type",
+ Labels: map[string]string{"hypervisor_type": hv.Status.DomainCapabilities.HypervisorType},
+ })
caps = make(map[string]string)
}
hvCaps[hv.Name] = caps
diff --git a/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go b/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go
index 76c2131f0..e63d68885 100644
--- a/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go
+++ b/internal/scheduling/nova/plugins/filters/filter_capabilities_test.go
@@ -8,6 +8,7 @@ import (
"testing"
api "github.com/cobaltcore-dev/cortex/api/external/nova"
+ "github.com/cobaltcore-dev/cortex/internal/scheduling/lib"
hv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -194,13 +195,40 @@ func TestFilterCapabilitiesStep_Run(t *testing.T) {
},
},
},
+ &hv1.Hypervisor{
+ ObjectMeta: v1.ObjectMeta{
+ Name: "host-empty-type",
+ },
+ Status: hv1.HypervisorStatus{
+ DomainCapabilities: hv1.DomainCapabilities{
+ HypervisorType: "",
+ },
+ Capabilities: hv1.Capabilities{
+ HostCpuArch: "x86_64",
+ },
+ },
+ },
+ &hv1.Hypervisor{
+ ObjectMeta: v1.ObjectMeta{
+ Name: "host-unknown-type",
+ },
+ Status: hv1.HypervisorStatus{
+ DomainCapabilities: hv1.DomainCapabilities{
+ HypervisorType: "xen",
+ },
+ Capabilities: hv1.Capabilities{
+ HostCpuArch: "x86_64",
+ },
+ },
+ },
}
tests := []struct {
- name string
- request api.ExternalSchedulerRequest
- expectedHosts []string
- filteredHosts []string
+ name string
+ request api.ExternalSchedulerRequest
+ expectedHosts []string
+ filteredHosts []string
+ expectedEvents []lib.FilterWeigherPipelineStepEvent
}{
{
name: "No extra specs in request - all hosts pass",
@@ -391,6 +419,76 @@ func TestFilterCapabilitiesStep_Run(t *testing.T) {
expectedHosts: []string{"host4"},
filteredHosts: []string{"host1", "host2", "host3"},
},
+ {
+ name: "Ignore hypervisors not in request even with unknown type",
+ request: api.ExternalSchedulerRequest{
+ Spec: api.NovaObject[api.NovaSpec]{
+ Data: api.NovaSpec{
+ Flavor: api.NovaObject[api.NovaFlavor]{
+ Data: api.NovaFlavor{
+ ExtraSpecs: map[string]string{
+ "capabilities:hypervisor_type": "CH",
+ },
+ },
+ },
+ },
+ },
+ Hosts: []api.ExternalSchedulerHost{
+ {ComputeHost: "host1"},
+ },
+ },
+ expectedHosts: []string{"host1"},
+ filteredHosts: []string{},
+ expectedEvents: []lib.FilterWeigherPipelineStepEvent{},
+ },
+ {
+ name: "Candidate hypervisor with empty hypervisor type emits event and is filtered out",
+ request: api.ExternalSchedulerRequest{
+ Spec: api.NovaObject[api.NovaSpec]{
+ Data: api.NovaSpec{
+ Flavor: api.NovaObject[api.NovaFlavor]{
+ Data: api.NovaFlavor{
+ ExtraSpecs: map[string]string{
+ "capabilities:hypervisor_type": "CH",
+ },
+ },
+ },
+ },
+ },
+ Hosts: []api.ExternalSchedulerHost{
+ {ComputeHost: "host-empty-type"},
+ },
+ },
+ expectedHosts: []string{},
+ filteredHosts: []string{"host-empty-type"},
+ expectedEvents: []lib.FilterWeigherPipelineStepEvent{
+ {Name: "filter_capabilities_unknown_hypervisor_type", Labels: map[string]string{"hypervisor_type": ""}},
+ },
+ },
+ {
+ name: "Candidate hypervisor with unknown hypervisor type emits event and is filtered out",
+ request: api.ExternalSchedulerRequest{
+ Spec: api.NovaObject[api.NovaSpec]{
+ Data: api.NovaSpec{
+ Flavor: api.NovaObject[api.NovaFlavor]{
+ Data: api.NovaFlavor{
+ ExtraSpecs: map[string]string{
+ "capabilities:hypervisor_type": "CH",
+ },
+ },
+ },
+ },
+ },
+ Hosts: []api.ExternalSchedulerHost{
+ {ComputeHost: "host-unknown-type"},
+ },
+ },
+ expectedHosts: []string{},
+ filteredHosts: []string{"host-unknown-type"},
+ expectedEvents: []lib.FilterWeigherPipelineStepEvent{
+ {Name: "filter_capabilities_unknown_hypervisor_type", Labels: map[string]string{"hypervisor_type": "xen"}},
+ },
+ },
{
name: "No matching hosts",
request: api.ExternalSchedulerRequest{
@@ -578,6 +676,28 @@ func TestFilterCapabilitiesStep_Run(t *testing.T) {
if len(result.Activations) != len(tt.expectedHosts) {
t.Errorf("expected %d hosts, got %d", len(tt.expectedHosts), len(result.Activations))
}
+
+ // Check events
+ if len(result.Events) != len(tt.expectedEvents) {
+ t.Errorf("expected %d events, got %d", len(tt.expectedEvents), len(result.Events))
+ }
+ for i, expectedEvent := range tt.expectedEvents {
+ if i >= len(result.Events) {
+ break
+ }
+ gotEvent := result.Events[i]
+ if gotEvent.Name != expectedEvent.Name {
+ t.Errorf("expected event name %q, got %q", expectedEvent.Name, gotEvent.Name)
+ }
+ if len(gotEvent.Labels) != len(expectedEvent.Labels) {
+ t.Errorf("expected event labels %v, got %v", expectedEvent.Labels, gotEvent.Labels)
+ }
+ for k, v := range expectedEvent.Labels {
+ if gotEvent.Labels[k] != v {
+ t.Errorf("expected event label %q=%q, got %q", k, v, gotEvent.Labels[k])
+ }
+ }
+ }
})
}
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_correct_az.go b/internal/scheduling/nova/plugins/filters/filter_correct_az.go
index ed7f68188..465e01546 100644
--- a/internal/scheduling/nova/plugins/filters/filter_correct_az.go
+++ b/internal/scheduling/nova/plugins/filters/filter_correct_az.go
@@ -34,6 +34,9 @@ func (s *FilterCorrectAZStep) Run(traceLog *slog.Logger, request api.ExternalSch
// "topology.kubernetes.io/zone" on the hv crd.
var computeHostsInAZ = make(map[string]struct{})
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
az, ok := hv.Labels[corev1.LabelTopologyZone]
if !ok {
traceLog.Warn("host missing zone label, keeping", "host", hv.Name)
diff --git a/internal/scheduling/nova/plugins/filters/filter_external_customer.go b/internal/scheduling/nova/plugins/filters/filter_external_customer.go
index b574e4ad1..80bd7cf9c 100644
--- a/internal/scheduling/nova/plugins/filters/filter_external_customer.go
+++ b/internal/scheduling/nova/plugins/filters/filter_external_customer.go
@@ -77,6 +77,9 @@ func (s *FilterExternalCustomerStep) Run(traceLog *slog.Logger, request api.Exte
}
hvsWithTrait := make(map[string]struct{})
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
traits := hv.Status.Traits
traits = append(traits, hv.Spec.CustomTraits...)
if !slices.Contains(traits, "CUSTOM_EXTERNAL_CUSTOMER_EXCLUSIVE") {
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go b/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go
index dcccdc010..2a3c8e4ff 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_accelerators.go
@@ -33,6 +33,9 @@ func (s *FilterHasAcceleratorsStep) Run(traceLog *slog.Logger, request api.Exter
}
hvsWithTrait := make(map[string]struct{})
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
traits := hv.Status.Traits
traits = append(traits, hv.Spec.CustomTraits...)
if !slices.Contains(traits, "COMPUTE_ACCELERATORS") {
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 117b032ae..a66166fa4 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_enough_capacity.go
@@ -82,6 +82,9 @@ func (s *FilterHasEnoughCapacity) Run(traceLog *slog.Logger, request api.Externa
return nil, err
}
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
var sourceMap map[hv1.ResourceName]resource.Quantity
if hv.Status.EffectiveCapacity == nil {
traceLog.Warn("hypervisor with nil effective capacity, use capacity instead (overprovisioning not considered)", "host", hv.Name)
diff --git a/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go b/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go
index aa35d2fc9..ac7ed8748 100644
--- a/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go
+++ b/internal/scheduling/nova/plugins/filters/filter_has_requested_traits.go
@@ -62,6 +62,9 @@ func (s *FilterHasRequestedTraits) Run(traceLog *slog.Logger, request api.Extern
hostsMatchingAllTraits := map[string]struct{}{}
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
allRequiredPresent := true
traits := hv.Status.Traits
traits = append(traits, hv.Spec.CustomTraits...)
diff --git a/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go b/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go
index 137ddf04f..7cf3ebbfd 100644
--- a/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go
+++ b/internal/scheduling/nova/plugins/filters/filter_instance_group_anti_affinity.go
@@ -68,6 +68,9 @@ func (s *FilterInstanceGroupAntiAffinityStep) Run(
}
hvsByName := make(map[string]hv1.Hypervisor)
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
hvsByName[hv.Name] = hv
}
diff --git a/internal/scheduling/nova/plugins/filters/filter_requested_destination.go b/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
index 971848dcd..71cd48844 100644
--- a/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
+++ b/internal/scheduling/nova/plugins/filters/filter_requested_destination.go
@@ -128,6 +128,9 @@ func (s *FilterRequestedDestinationStep) Run(traceLog *slog.Logger, request api.
}
hvsByName := make(map[string]hv1.Hypervisor)
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
hvsByName[hv.Name] = hv
}
s.processRequestedAggregates(traceLog, rd.Data.Aggregates, hvsByName, result.Activations)
diff --git a/internal/scheduling/nova/plugins/filters/filter_status_conditions.go b/internal/scheduling/nova/plugins/filters/filter_status_conditions.go
index 3d7f2aae6..60b616d71 100644
--- a/internal/scheduling/nova/plugins/filters/filter_status_conditions.go
+++ b/internal/scheduling/nova/plugins/filters/filter_status_conditions.go
@@ -43,6 +43,9 @@ func (s *FilterStatusConditionsStep) Run(traceLog *slog.Logger, request api.Exte
var hostsReady = make(map[string]struct{})
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
allMet := true
for conditionType, expectedStatus := range expected {
cd := meta.FindStatusCondition(hv.Status.Conditions, conditionType)
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_binpack.go b/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
index e1509a4cc..39772f94e 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_binpack.go
@@ -80,6 +80,9 @@ func (s *KVMBinpackStep) Run(traceLog *slog.Logger, request api.ExternalSchedule
}
hvsByName := make(map[string]hv1.Hypervisor, len(hvs.Items))
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
hvsByName[hv.Name] = hv
}
vmResources := s.calcVMResources(request)
diff --git a/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go b/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go
index 5f13897f0..6085edb70 100644
--- a/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go
+++ b/internal/scheduling/nova/plugins/weighers/kvm_instance_group_soft_affinity.go
@@ -58,6 +58,9 @@ func (s *KVMInstanceGroupSoftAffinityStep) Run(traceLog *slog.Logger, request ap
}
hvsByName := make(map[string]hv1.Hypervisor, len(hvs.Items))
for _, hv := range hvs.Items {
+ if _, ok := result.Activations[hv.Name]; !ok {
+ continue
+ }
hvsByName[hv.Name] = hv
}