Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/v1alpha1/pipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ type PipelineSpec struct {
// +kubebuilder:default=false
CreateDecisions bool `json:"createDecisions,omitempty"`

// If this pipeline should ignore host preselection and gather all
// available placement candidates before applying filters, instead of
// relying on a pre-filtered set and weights.
// +kubebuilder:default=false
IgnorePreselection bool `json:"ignorePreselection,omitempty"`

// The type of the pipeline, used to differentiate between
// filter-weigher and detector pipelines within the same
// scheduling domain.
Expand Down
2 changes: 2 additions & 0 deletions helm/bundles/cortex-nova/templates/pipelines_kvm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ spec:
This is the pipeline used for KVM hypervisors (qemu and cloud-hypervisor).
type: filter-weigher
createDecisions: true
# Fetch all placement candidates, ignoring nova's preselection.
ignorePreselection: true
filters:
- name: filter_host_instructions
description: |
Expand Down
7 changes: 7 additions & 0 deletions helm/library/cortex/files/crds/cortex.cloud_pipelines.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ spec:
- name
type: object
type: array
ignorePreselection:
default: false
description: |-
If this pipeline should ignore host preselection and gather all
available placement candidates before applying filters, instead of
relying on a pre-filtered set and weights.
type: boolean
schedulingDomain:
description: |-
SchedulingDomain defines in which scheduling domain this pipeline
Expand Down
70 changes: 70 additions & 0 deletions internal/scheduling/nova/candidate_gatherer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright SAP SE
// SPDX-License-Identifier: Apache-2.0

package nova

import (
"context"
"fmt"
"strings"

api "github.com/cobaltcore-dev/cortex/api/delegation/nova"
hv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// CandidateGatherer is an interface for gathering placement candidates
// for a given Nova scheduling request.
type CandidateGatherer interface {
// Gather all placement candidates and mutate the request accordingly.
MutateWithAllCandidates(ctx context.Context, request *api.ExternalSchedulerRequest) error
}

// candidateGatherer is the default implementation of CandidateGatherer
// for Nova scheduling requests.
type candidateGatherer struct{ client.Client }

// MutateWithAllCandidates gathers all placement candidates and mutates
// the request accordingly.
func (g *candidateGatherer) MutateWithAllCandidates(ctx context.Context, request *api.ExternalSchedulerRequest) error {
// Currently we can only get candidates for kvm placements.
hvType, ok := request.Spec.Data.Flavor.Data.ExtraSpecs["capabilities:hypervisor_type"]
if !ok {
return fmt.Errorf(
"missing hypervisor_type in flavor extra specs: %v",
request.Spec.Data.Flavor.Data.ExtraSpecs,
)
}
switch strings.ToLower(hvType) {
case "qemu", "ch":
// Supported hypervisor type.
default:
// Unsupported hypervisor type, do nothing.
return fmt.Errorf(
"cannot gather all placement candidates for hypervisor type %q",
request.Spec.Data.Flavor.Data.ExtraSpecs["capabilities:hypervisor_type"],
)
}

// List all kvm hypervisors.
hypervisorList := &hv1.HypervisorList{}
if err := g.List(ctx, hypervisorList); err != nil {
return err
}
hosts := make([]api.ExternalSchedulerHost, 0, len(hypervisorList.Items))
weights := make(map[string]float64, len(hypervisorList.Items))
for _, hv := range hypervisorList.Items {
host := api.ExternalSchedulerHost{
// For KVM hosts, compute host name and hypervisor hostname is identical.
ComputeHost: hv.Name,
HypervisorHostname: hv.Name,
}
hosts = append(hosts, host)
weights[host.ComputeHost] = 0.0 // Default weight.
}

// Mutate the request with all gathered hosts and weights.
request.Hosts = hosts
request.Weights = weights
return nil
}
Loading