diff --git a/api/external/nova/messages.go b/api/external/nova/messages.go index 5b4736ef7..789cd17d4 100644 --- a/api/external/nova/messages.go +++ b/api/external/nova/messages.go @@ -31,9 +31,6 @@ type ExternalSchedulerRequest struct { // Request context from Nova that contains additional meta information. Context NovaRequestContext `json:"context"` - // Whether the request is a reservation. - Reservation bool `json:"reservation"` - Hosts []ExternalSchedulerHost `json:"hosts"` Weights map[string]float64 `json:"weights"` diff --git a/cmd/main.go b/cmd/main.go index 1565c15cd..68800a185 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -321,8 +321,7 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "nova FilterWeigherPipelineController") os.Exit(1) } - httpAPIConf := conf.GetConfigOrDie[nova.HTTPAPIConfig]() - nova.NewAPI(httpAPIConf, filterWeigherController).Init(mux) + nova.NewAPI(filterWeigherController).Init(mux) // Initialize commitments API for LIQUID interface commitmentsAPI := commitments.NewAPI(multiclusterClient) diff --git a/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml b/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml index 038d09704..faf854cf1 100644 --- a/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml +++ b/helm/bundles/cortex-nova/templates/pipelines_kvm.yaml @@ -66,6 +66,17 @@ spec: This step selects hosts not in the instance group specified in the nova scheduler request spec, but only until the max_server_per_host limit is reached (default = 1). + - name: filter_has_enough_capacity + description: | + This step will filter out hosts that do not have enough available capacity + to host the requested flavor. If enabled, this step will subtract the + current reservations residing on this host from the available capacity. + params: + # If reserved space should be locked even for matching requests. + # For the reservations pipeline, we don't want to unlock + # reserved space, to avoid reservations for the same project + # and flavor to overlap. + - {key: lockReserved, boolValue: false} - name: filter_allowed_projects description: | This step filters hosts based on allowed projects defined in the @@ -179,200 +190,6 @@ spec: This step selects hosts not in the instance group specified in the nova scheduler request spec, but only until the max_server_per_host limit is reached (default = 1). - - name: filter_allowed_projects - description: | - This step filters hosts based on allowed projects defined in the - hypervisor resource. Note that hosts allowing all projects are still - accessible and will not be filtered out. In this way some hypervisors - are made accessible to some projects only. - - name: filter_live_migratable - description: | - This step ensures that the target host of a live migration can accept - the migrating VM, by checking cpu architecture, cpu features, emulated - devices, and cpu modes. - - name: filter_requested_destination - params: {{ .Values.kvm.filterRequestedDestinationParams | toYaml | nindent 8 }} - description: | - This step filters hosts based on the `requested_destination` instruction - from the nova scheduler request spec. It supports filtering by host and - by aggregates. - weighers: - - name: kvm_prefer_smaller_hosts - params: - - {key: resourceWeights, floatMapValue: {"memory": 1.0}} - description: | - This step pulls virtual machines onto smaller hosts (by capacity). This - ensures that larger hosts are not overly fragmented with small VMs, - and can still accommodate larger VMs when they need to be scheduled. - - name: kvm_instance_group_soft_affinity - description: | - This weigher implements the "soft affinity" and "soft anti-affinity" policy - for instance groups in nova. - It assigns a weight to each host based on how many instances of the same - 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. - - name: kvm_binpack - params: - - {key: resourceWeights, floatMapValue: {"memory": 1.0}} - description: | - This step implements a binpacking weigher for workloads on kvm hypervisors. - It pulls the requested vm into the smallest gaps possible, to ensure - other hosts with less allocation stay free for bigger vms. - In this pipeline, the binpacking will focus on hana virtual machines. ---- -apiVersion: cortex.cloud/v1alpha1 -kind: Pipeline -metadata: - name: kvm-general-purpose-load-balancing-all-filters-enabled -spec: - schedulingDomain: nova - description: | - This pipeline uses the same filtering steps as implemented in the - nova service, ensuring a valid placement. Note: this pipeline is - currently under testing and should not be used for production workloads yet. - - This is the pipeline used for KVM hypervisors (qemu and cloud-hypervisor). - Specifically, this pipeline is used for general purpose workloads. - It is also used for (CR/HA) reservation requests. - type: filter-weigher - createDecisions: false - # Fetch all placement candidates, ignoring nova's preselection. - ignorePreselection: true - filters: - - 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_has_enough_capacity - description: | - This step will filter out hosts that do not have enough available capacity - to host the requested flavor. If enabled, this step will subtract the - current reservations residing on this host from the available capacity. - params: - # If reserved space should be locked even for matching requests. - # For the reservations pipeline, we don't want to unlock - # reserved space, to avoid reservations for the same project - # and flavor to overlap. - - {key: lockReserved, boolValue: true} - - name: filter_has_requested_traits - description: | - This step filters hosts that do not have the requested traits given by the - nova flavor extra spec: "trait:": "forbidden" means the host must - not have the specified trait. "trait:": "required" means the host - must have the specified trait. - - name: filter_has_accelerators - 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_external_customer - description: | - This step prefix-matches the domain name for external customer domains and - filters out hosts that are not intended for external customers. It considers - the `CUSTOM_EXTERNAL_CUSTOMER_SUPPORTED` trait on hosts as well as the - `domain_name` scheduler hint from the nova request spec. - params: - - {key: domainNamePrefixes, stringListValue: ["iaas-"]} - - name: filter_allowed_projects - description: | - This step filters hosts based on allowed projects defined in the - hypervisor resource. Note that hosts allowing all projects are still - accessible and will not be filtered out. In this way some hypervisors - are made accessible to some projects only. - - name: filter_capabilities - description: | - This step will filter out hosts that do not meet the compute capabilities - requested by the nova flavor extra specs, like `{"arch": "x86_64", - "maxphysaddr:bits": 46, ...}`. - - Note: currently, advanced boolean/numeric operators for the capabilities - like `>`, `!`, ... are not supported because they are not used by any of our - flavors in production. - - name: filter_instance_group_affinity - description: | - This step selects hosts in the instance group specified in the nova - scheduler request spec. - - name: filter_instance_group_anti_affinity - description: | - This step selects hosts not in the instance group specified in the nova - scheduler request spec, but only until the max_server_per_host limit is - reached (default = 1). - - name: filter_live_migratable - description: | - This step ensures that the target host of a live migration can accept - the migrating VM, by checking cpu architecture, cpu features, emulated - devices, and cpu modes. - - name: filter_requested_destination - params: {{ .Values.kvm.filterRequestedDestinationParams | toYaml | nindent 8 }} - description: | - This step filters hosts based on the `requested_destination` instruction - from the nova scheduler request spec. It supports filtering by host and - by aggregates. - weighers: - - name: kvm_prefer_smaller_hosts - params: - - {key: resourceWeights, floatMapValue: {"memory": 1.0}} - description: | - This step pulls virtual machines onto smaller hosts (by capacity). This - ensures that larger hosts are not overly fragmented with small VMs, - and can still accommodate larger VMs when they need to be scheduled. - - name: kvm_instance_group_soft_affinity - description: | - This weigher implements the "soft affinity" and "soft anti-affinity" policy - for instance groups in nova. - - It assigns a weight to each host based on how many instances of the same - 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. - - name: kvm_binpack - multiplier: -1.0 # inverted = balancing - params: - - {key: resourceWeights, floatMapValue: {"memory": 1.0}} - description: | - This step implements a balancing weigher for workloads on kvm hypervisors, - which is the opposite of binpacking. Instead of pulling the requested vm - into the smallest gaps possible, it spreads the load to ensure - workloads are balanced across hosts. In this pipeline, the balancing will - focus on general purpose virtual machines. ---- -apiVersion: cortex.cloud/v1alpha1 -kind: Pipeline -metadata: - name: kvm-hana-bin-packing-all-filters-enabled -spec: - schedulingDomain: nova - description: | - This pipeline uses the same filtering steps as implemented in the - nova service, ensuring a valid placement. Note: this pipeline is - currently under testing and should not be used for production workloads yet. - - This is the pipeline used for KVM hypervisors (qemu and cloud-hypervisor). - Specifically, this pipeline is used for hana virtual machines. - type: filter-weigher - createDecisions: false - # Fetch all placement candidates, ignoring nova's preselection. - ignorePreselection: true - filters: - - 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_has_enough_capacity description: | This step will filter out hosts that do not have enough available capacity @@ -383,58 +200,13 @@ spec: # For the reservations pipeline, we don't want to unlock # reserved space, to avoid reservations for the same project # and flavor to overlap. - - {key: lockReserved, boolValue: true} - - name: filter_has_requested_traits - description: | - This step filters hosts that do not have the requested traits given by the - nova flavor extra spec: "trait:": "forbidden" means the host must - not have the specified trait. "trait:": "required" means the host - must have the specified trait. - - name: filter_has_accelerators - 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_external_customer - description: | - This step prefix-matches the domain name for external customer domains and - filters out hosts that are not intended for external customers. It considers - the `CUSTOM_EXTERNAL_CUSTOMER_SUPPORTED` trait on hosts as well as the - `domain_name` scheduler hint from the nova request spec. - params: - - {key: domainNamePrefixes, stringListValue: ["iaas-"]} + - {key: lockReserved, boolValue: false} - name: filter_allowed_projects description: | This step filters hosts based on allowed projects defined in the hypervisor resource. Note that hosts allowing all projects are still accessible and will not be filtered out. In this way some hypervisors are made accessible to some projects only. - - name: filter_capabilities - description: | - This step will filter out hosts that do not meet the compute capabilities - requested by the nova flavor extra specs, like `{"arch": "x86_64", - "maxphysaddr:bits": 46, ...}`. - - Note: currently, advanced boolean/numeric operators for the capabilities - like `>`, `!`, ... are not supported because they are not used by any of our - flavors in production. - - name: filter_instance_group_affinity - description: | - This step selects hosts in the instance group specified in the nova - scheduler request spec. - - name: filter_instance_group_anti_affinity - description: | - This step selects hosts not in the instance group specified in the nova - scheduler request spec, but only until the max_server_per_host limit is - reached (default = 1). - name: filter_live_migratable description: | This step ensures that the target host of a live migration can accept @@ -458,7 +230,6 @@ spec: description: | This weigher implements the "soft affinity" and "soft anti-affinity" policy for instance groups in nova. - It assigns a weight to each host based on how many instances of the same 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 diff --git a/helm/bundles/cortex-nova/values.yaml b/helm/bundles/cortex-nova/values.yaml index 1b2084d40..c17b5b169 100644 --- a/helm/bundles/cortex-nova/values.yaml +++ b/helm/bundles/cortex-nova/values.yaml @@ -124,8 +124,6 @@ cortex-scheduling-controllers: conf: <<: *cortexConf leaderElectionID: cortex-nova-scheduling - # OpenStack projects that use experimental features. - experimentalProjectIDs: [] monitoring: labels: <<: *cortexMonitoringLabels @@ -142,11 +140,11 @@ cortex-scheduling-controllers: # CommittedResourceFlavorGroupPipelines maps flavor group IDs to pipeline names for CR reservations # This allows different scheduling strategies per flavor group (e.g., HANA vs GP) committedResourceFlavorGroupPipelines: - "2152": "kvm-hana-bin-packing-all-filters-enabled" # HANA flavor group - "2101": "kvm-general-purpose-load-balancing-all-filters-enabled" # General Purpose flavor group - "*": "kvm-general-purpose-load-balancing-all-filters-enabled" # Catch-all fallback + "2152": "kvm-hana-bin-packing" # HANA flavor group + "2101": "kvm-general-purpose-load-balancing" # General Purpose flavor group + "*": "kvm-general-purpose-load-balancing" # Catch-all fallback # Default pipeline for CR reservations when no CommittedResourceFlavorGroupPipelines entry matches - committedResourcePipelineDefault: "kvm-general-purpose-load-balancing-all-filters-enabled" + committedResourcePipelineDefault: "kvm-general-purpose-load-balancing" # How often to re-verify active reservations # 5m = 300000000000 nanoseconds committedResourceRequeueIntervalActive: 300000000000 diff --git a/internal/scheduling/nova/e2e_checks.go b/internal/scheduling/nova/e2e_checks.go index c167904e3..a8caca8ba 100644 --- a/internal/scheduling/nova/e2e_checks.go +++ b/internal/scheduling/nova/e2e_checks.go @@ -321,7 +321,7 @@ func randomRequest(dc datacenter, seed int) api.ExternalSchedulerRequest { if err != nil { slog.Info("failed to determine hypervisor type, using default pipeline", "error", err) } else if hvType == api.HypervisorTypeQEMU || hvType == api.HypervisorTypeCH { - request.Pipeline = "kvm-general-purpose-load-balancing-all-filters-enabled" + request.Pipeline = "kvm-general-purpose-load-balancing" } return request } diff --git a/internal/scheduling/nova/external_scheduler_api.go b/internal/scheduling/nova/external_scheduler_api.go index c3a5de071..0210a81b8 100644 --- a/internal/scheduling/nova/external_scheduler_api.go +++ b/internal/scheduling/nova/external_scheduler_api.go @@ -12,7 +12,6 @@ import ( "io" "log/slog" "net/http" - "slices" api "github.com/cobaltcore-dev/cortex/api/external/nova" "github.com/cobaltcore-dev/cortex/api/v1alpha1" @@ -25,12 +24,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics" ) -// Custom configuration for the Nova external scheduler api. -type HTTPAPIConfig struct { - // OpenStack projects that use experimental features. - ExperimentalProjectIDs []string `json:"experimentalProjectIDs,omitempty"` -} - type HTTPAPIDelegate interface { // Process the decision from the API. Should create and return the updated decision. ProcessNewDecisionFromAPI(ctx context.Context, decision *v1alpha1.Decision) error @@ -42,14 +35,12 @@ type HTTPAPI interface { } type httpAPI struct { - config HTTPAPIConfig monitor scheduling.APIMonitor delegate HTTPAPIDelegate } -func NewAPI(config HTTPAPIConfig, delegate HTTPAPIDelegate) HTTPAPI { +func NewAPI(delegate HTTPAPIDelegate) HTTPAPI { return &httpAPI{ - config: config, monitor: scheduling.NewSchedulerMonitor(), delegate: delegate, } @@ -99,23 +90,6 @@ func (httpAPI *httpAPI) inferPipelineName(requestData api.ExternalSchedulerReque } switch hvType { case api.HypervisorTypeCH, api.HypervisorTypeQEMU: - enableAllFilters := false - // If the nova request matches a configurable openstack project, - // use a different pipeline that has all filters enabled. - if slices.Contains(httpAPI.config.ExperimentalProjectIDs, requestData.Spec.Data.ProjectID) { - enableAllFilters = true - } - if requestData.Reservation { - enableAllFilters = true - } - if enableAllFilters { - switch flavorType { - case api.FlavorTypeHANA: - return "kvm-hana-bin-packing-all-filters-enabled", nil - default: - return "kvm-general-purpose-load-balancing-all-filters-enabled", nil - } - } switch flavorType { case api.FlavorTypeHANA: return "kvm-hana-bin-packing", nil @@ -123,9 +97,6 @@ func (httpAPI *httpAPI) inferPipelineName(requestData api.ExternalSchedulerReque return "kvm-general-purpose-load-balancing", nil } case api.HypervisorTypeVMware: - if requestData.Reservation { - return "", errors.New("reservations are not supported on vmware hypervisors") - } switch flavorType { case api.FlavorTypeHANA: return "vmware-hana-bin-packing", nil diff --git a/internal/scheduling/nova/external_scheduler_api_test.go b/internal/scheduling/nova/external_scheduler_api_test.go index 78b2a84b1..510df9f94 100644 --- a/internal/scheduling/nova/external_scheduler_api_test.go +++ b/internal/scheduling/nova/external_scheduler_api_test.go @@ -32,9 +32,8 @@ func (m *mockHTTPAPIDelegate) ProcessNewDecisionFromAPI(ctx context.Context, dec func TestNewAPI(t *testing.T) { delegate := &mockHTTPAPIDelegate{} - config := HTTPAPIConfig{} - api := NewAPI(config, delegate) + api := NewAPI(delegate) if api == nil { t.Fatal("NewAPI returned nil") @@ -56,8 +55,7 @@ func TestNewAPI(t *testing.T) { func TestHTTPAPI_Init(t *testing.T) { delegate := &mockHTTPAPIDelegate{} - config := HTTPAPIConfig{} - api := NewAPI(config, delegate) + api := NewAPI(delegate) mux := http.NewServeMux() api.Init(mux) @@ -75,8 +73,7 @@ func TestHTTPAPI_Init(t *testing.T) { func TestHTTPAPI_canRunScheduler(t *testing.T) { delegate := &mockHTTPAPIDelegate{} - config := HTTPAPIConfig{} - api := NewAPI(config, delegate).(*httpAPI) + api := NewAPI(delegate).(*httpAPI) tests := []struct { name string @@ -279,8 +276,7 @@ func TestHTTPAPI_NovaExternalScheduler(t *testing.T) { }, } - config := HTTPAPIConfig{} - api := NewAPI(config, delegate).(*httpAPI) + api := NewAPI(delegate).(*httpAPI) var body *strings.Reader if tt.body != "" { @@ -331,8 +327,7 @@ func TestHTTPAPI_NovaExternalScheduler_DecisionCreation(t *testing.T) { }, } - config := HTTPAPIConfig{} - api := NewAPI(config, delegate).(*httpAPI) + api := NewAPI(delegate).(*httpAPI) requestData := novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ @@ -513,10 +508,7 @@ func TestLimitHostsToRequest(t *testing.T) { func TestHTTPAPI_inferPipelineName(t *testing.T) { delegate := &mockHTTPAPIDelegate{} - config := HTTPAPIConfig{ - ExperimentalProjectIDs: []string{"my-experimental-project-id"}, - } - api := NewAPI(config, delegate).(*httpAPI) + api := NewAPI(delegate).(*httpAPI) tests := []struct { name string @@ -527,7 +519,7 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }{ // KVM/QEMU general purpose tests { - name: "qemu hypervisor general purpose without reservation", + name: "qemu hypervisor general purpose", requestData: novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ Data: novaapi.NovaSpec{ @@ -541,7 +533,6 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "kvm-general-purpose-load-balancing", expectErr: false, @@ -561,55 +552,13 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "kvm-general-purpose-load-balancing", expectErr: false, }, - { - name: "qemu hypervisor general purpose with reservation", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "qemu", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "forbidden", - }, - }, - }, - }, - }, - Reservation: true, - }, - expectedResult: "kvm-general-purpose-load-balancing-all-filters-enabled", - expectErr: false, - }, - { - name: "experimental project ID requesting kvm general purpose vm", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - ProjectID: "my-experimental-project-id", - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "qemu", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "forbidden", - }, - }, - }, - }, - }, - Reservation: false, - }, - expectedResult: "kvm-general-purpose-load-balancing-all-filters-enabled", - expectErr: false, - }, // KVM/QEMU HANA tests { - name: "qemu hypervisor HANA without reservation", + name: "qemu hypervisor HANA", requestData: novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ Data: novaapi.NovaSpec{ @@ -623,55 +572,13 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "kvm-hana-bin-packing", expectErr: false, }, - { - name: "qemu hypervisor HANA with reservation", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "qemu", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "required", - }, - }, - }, - }, - }, - Reservation: true, - }, - expectedResult: "kvm-hana-bin-packing-all-filters-enabled", - expectErr: false, - }, - { - name: "experimental project ID requesting kvm HANA vm", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - ProjectID: "my-experimental-project-id", - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "qemu", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "required", - }, - }, - }, - }, - }, - Reservation: false, - }, - expectedResult: "kvm-hana-bin-packing-all-filters-enabled", - expectErr: false, - }, // CH hypervisor tests { - name: "ch hypervisor general purpose without reservation", + name: "ch hypervisor general purpose", requestData: novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ Data: novaapi.NovaSpec{ @@ -685,33 +592,12 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "kvm-general-purpose-load-balancing", expectErr: false, }, { - name: "ch hypervisor general purpose with reservation", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "ch", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "forbidden", - }, - }, - }, - }, - }, - Reservation: true, - }, - expectedResult: "kvm-general-purpose-load-balancing-all-filters-enabled", - expectErr: false, - }, - { - name: "ch hypervisor HANA without reservation", + name: "ch hypervisor HANA", requestData: novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ Data: novaapi.NovaSpec{ @@ -725,34 +611,13 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "kvm-hana-bin-packing", expectErr: false, }, - { - name: "ch hypervisor HANA with reservation", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "ch", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "required", - }, - }, - }, - }, - }, - Reservation: true, - }, - expectedResult: "kvm-hana-bin-packing-all-filters-enabled", - expectErr: false, - }, // VMware tests { - name: "vmware hypervisor general purpose without reservation", + name: "vmware hypervisor general purpose", requestData: novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ Data: novaapi.NovaSpec{ @@ -766,13 +631,12 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "vmware-general-purpose-load-balancing", expectErr: false, }, { - name: "vmware hypervisor HANA without reservation", + name: "vmware hypervisor HANA", requestData: novaapi.ExternalSchedulerRequest{ Spec: novaapi.NovaObject[novaapi.NovaSpec]{ Data: novaapi.NovaSpec{ @@ -786,31 +650,10 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectedResult: "vmware-hana-bin-packing", expectErr: false, }, - { - name: "vmware hypervisor with reservation - error", - requestData: novaapi.ExternalSchedulerRequest{ - Spec: novaapi.NovaObject[novaapi.NovaSpec]{ - Data: novaapi.NovaSpec{ - Flavor: novaapi.NovaObject[novaapi.NovaFlavor]{ - Data: novaapi.NovaFlavor{ - ExtraSpecs: map[string]string{ - "capabilities:hypervisor_type": "VMware vCenter Server", - "trait:CUSTOM_HANA_EXCLUSIVE_HOST": "forbidden", - }, - }, - }, - }, - }, - Reservation: true, - }, - expectErr: true, - errContains: "reservations are not supported on vmware hypervisors", - }, // Error cases { name: "missing hypervisor_type", @@ -826,7 +669,6 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectErr: true, errContains: "failed to determine hypervisor type from request data", @@ -846,7 +688,6 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectErr: true, errContains: "failed to determine hypervisor type from request data", @@ -865,7 +706,6 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectErr: false, // should infer general purpose. expectedResult: "kvm-general-purpose-load-balancing", @@ -886,7 +726,6 @@ func TestHTTPAPI_inferPipelineName(t *testing.T) { }, }, }, - Reservation: false, }, expectErr: true, errContains: "failed to determine flavor type from request data", diff --git a/internal/scheduling/nova/integration_test.go b/internal/scheduling/nova/integration_test.go index a1267c9c0..47f371f0b 100644 --- a/internal/scheduling/nova/integration_test.go +++ b/internal/scheduling/nova/integration_test.go @@ -222,7 +222,7 @@ type PipelineConfig struct { // filter_has_enough_capacity and kvm_failover_evacuation func DefaultPipelineConfig() PipelineConfig { return PipelineConfig{ - Name: "kvm-general-purpose-load-balancing-all-filters-enabled", + Name: "kvm-general-purpose-load-balancing", Filters: []v1alpha1.FilterSpec{ {Name: "filter_has_enough_capacity"}, }, @@ -284,7 +284,6 @@ func NewIntegrationTestServer(t *testing.T, pipelineConfig PipelineConfig, objec // Create the HTTP API with the controller as delegate - skip metrics registration api := &httpAPI{ - config: HTTPAPIConfig{}, monitor: lib.NewSchedulerMonitor(), // Create new monitor but don't register delegate: controller, } @@ -371,7 +370,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { reservations: []*v1alpha1.Reservation{ newFailoverReservation("failover-vm-existing", "host3", "m1.large", "4", "8Gi", map[string]string{"vm-existing": "host1"}), }, - request: newNovaRequest("new-vm-uuid", "project-B", "m1.medium", "gp-1", 2, "4Gi", false, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("new-vm-uuid", "project-B", "m1.medium", "gp-1", 2, "4Gi", false, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing"), filteredHosts: []string{"host3"}, minExpectedHostsCount: 2, }, @@ -384,7 +383,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { reservations: []*v1alpha1.Reservation{ newFailoverReservation("failover-vm-123", "host3", "m1.large", "4", "8Gi", map[string]string{"vm-123": "host1"}), }, - request: newNovaRequest("vm-123", "project-A", "m1.large", "gp-1", 4, "8Gi", true, []string{"host2", "host3"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("vm-123", "project-A", "m1.large", "gp-1", 4, "8Gi", true, []string{"host2", "host3"}, "kvm-general-purpose-load-balancing"), expectedHosts: []string{"host3", "host2"}, // Failover host should be first expectedHostsOrdered: true, minExpectedHostsCount: 2, @@ -400,7 +399,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { newFailoverReservation("failover-vm-456-on-host1", "host1", "m1.large", "4", "8Gi", map[string]string{"vm-456": "host-original"}), newFailoverReservation("failover-vm-456-on-host3", "host3", "m1.large", "4", "8Gi", map[string]string{"vm-456": "host-original"}), }, - request: newNovaRequest("vm-456", "project-A", "m1.large", "gp-1", 4, "8Gi", true, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("vm-456", "project-A", "m1.large", "gp-1", 4, "8Gi", true, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing"), expectedHosts: []string{"host1", "host2", "host3"}, minExpectedHostsCount: 3, // Both host1 and host3 have failover reservations, so they should be preferred over host2 @@ -416,7 +415,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { newFailoverReservation("failover-vm-456-on-host1", "host1", "m1.large", "4", "8Gi", map[string]string{"some-other-vm": "host-original"}), newFailoverReservation("failover-vm-456-on-host3", "host3", "m1.large", "4", "8Gi", map[string]string{"vm-456": "host-original"}), }, - request: newNovaRequest("vm-456", "project-A", "m1.large", "gp-1", 4, "8Gi", true, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("vm-456", "project-A", "m1.large", "gp-1", 4, "8Gi", true, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing"), expectedHosts: []string{"host3", "host2"}, expectedHostsOrdered: true, minExpectedHostsCount: 2, @@ -431,7 +430,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { reservations: []*v1alpha1.Reservation{ newCommittedReservation("committed-res-host1", "host1", "host1", "project-A", "m1.large", "gp-1", "4", "8Gi"), }, - request: newNovaRequest("new-vm should work", "project-A", "m1.large", "gp-1", 4, "8Gi", false, []string{"host1", "host2"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("new-vm should work", "project-A", "m1.large", "gp-1", 4, "8Gi", false, []string{"host1", "host2"}, "kvm-general-purpose-load-balancing"), expectedHosts: []string{"host1", "host2"}, // host1 unlocked because project/flavor match minExpectedHostsCount: 2, }, @@ -444,7 +443,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { reservations: []*v1alpha1.Reservation{ newCommittedReservation("committed-res-host1", "host1", "host1", "project-A", "m1.large", "gp-1", "4", "8Gi"), }, - request: newNovaRequest("new-vm", "project-B", "m1.large", "gp-1", 4, "8Gi", false, []string{"host1", "host2"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("new-vm", "project-B", "m1.large", "gp-1", 4, "8Gi", false, []string{"host1", "host2"}, "kvm-general-purpose-load-balancing"), expectedHosts: []string{"host2"}, filteredHosts: []string{"host1"}, // host1 blocked because project doesn't match minExpectedHostsCount: 1, @@ -457,7 +456,7 @@ func TestIntegration_SchedulingWithReservations(t *testing.T) { newHypervisor("host3", "16", "4", "32Gi", "8Gi"), }, reservations: []*v1alpha1.Reservation{}, - request: newNovaRequest("new-vm", "project-A", "m1.large", "gp-1", 4, "8Gi", false, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing-all-filters-enabled"), + request: newNovaRequest("new-vm", "project-A", "m1.large", "gp-1", 4, "8Gi", false, []string{"host1", "host2", "host3"}, "kvm-general-purpose-load-balancing"), filters: []v1alpha1.FilterSpec{{Name: "filter_has_enough_capacity"}}, weighers: []v1alpha1.WeigherSpec{}, // No weighers filteredHosts: []string{"host2"}, diff --git a/internal/scheduling/reservations/commitments/config.go b/internal/scheduling/reservations/commitments/config.go index 151483fb6..0004cf19b 100644 --- a/internal/scheduling/reservations/commitments/config.go +++ b/internal/scheduling/reservations/commitments/config.go @@ -31,7 +31,7 @@ type Config struct { DatabaseSecretRef *corev1.SecretReference `json:"databaseSecretRef,omitempty"` // FlavorGroupPipelines maps flavor group names to pipeline names. - // Example: {"2152": "kvm-hana-bin-packing-all-filters-enabled", "2101": "kvm-general-purpose-load-balancing-all-filters-enabled", "*": "kvm-general-purpose-load-balancing-all-filters-enabled"} + // Example: {"2152": "kvm-hana-bin-packing", "2101": "kvm-general-purpose-load-balancing", "*": "kvm-general-purpose-load-balancing"} // Used to select different scheduling pipelines based on flavor group characteristics. FlavorGroupPipelines map[string]string `json:"committedResourceFlavorGroupPipelines,omitempty"` @@ -53,7 +53,7 @@ func DefaultConfig() Config { return Config{ RequeueIntervalActive: 5 * time.Minute, RequeueIntervalRetry: 1 * time.Minute, - PipelineDefault: "kvm-general-purpose-load-balancing-all-filters-enabled", + PipelineDefault: "kvm-general-purpose-load-balancing", SchedulerURL: "http://localhost:8080/scheduler/nova/external", ChangeAPIWatchReservationsTimeout: 10 * time.Second, ChangeAPIWatchReservationsPollInterval: 500 * time.Millisecond, diff --git a/internal/scheduling/reservations/scheduler_client.go b/internal/scheduling/reservations/scheduler_client.go index 91d62ef74..e89628b34 100644 --- a/internal/scheduling/reservations/scheduler_client.go +++ b/internal/scheduling/reservations/scheduler_client.go @@ -112,10 +112,9 @@ func (c *SchedulerClient) ScheduleReservation(ctx context.Context, req ScheduleR // Build the external scheduler request externalSchedulerRequest := api.ExternalSchedulerRequest{ - Reservation: true, - Pipeline: req.Pipeline, - Hosts: req.EligibleHosts, - Weights: weights, + Pipeline: req.Pipeline, + Hosts: req.EligibleHosts, + Weights: weights, Context: api.NovaRequestContext{ RequestID: RequestIDFromContext(ctx), GlobalRequestID: globalReqID,