diff --git a/internal/scheduling/nova/plugins/filters/filter_quota_enforcement.go b/internal/scheduling/nova/plugins/filters/filter_quota_enforcement.go index 4e139f8d8..754cbf706 100644 --- a/internal/scheduling/nova/plugins/filters/filter_quota_enforcement.go +++ b/internal/scheduling/nova/plugins/filters/filter_quota_enforcement.go @@ -81,43 +81,49 @@ func (s *FilterQuotaEnforcement) Run(traceLog *slog.Logger, request api.External } // Step 1: Skip intents that don't represent new resource consumption. + // We distinguish two sub-buckets in the decision label so operators can tell + // external non-consuming intents (evacuate / live-migrate — VM already exists, + // being moved) apart from cortex-internal reservation intents (failover / + // committed-resource reservations — new schedules driven by cortex itself). intent, err := request.GetIntent() if err == nil { switch intent { case api.EvacuateIntent, api.LiveMigrationIntent: traceLog.Info("skipping quota enforcement for non-consuming intent", "intent", intent) - QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped", "", "", "") + QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped_intent_migration", "", "", "") return result, nil case api.ReserveForFailoverIntent: traceLog.Info("skipping quota enforcement for failover reservation intent") - QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped", "", "", "") + QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped_intent_internal", "", "", "") return result, nil case api.ReserveForCommittedResourceIntent: // TODO: revisit whether committed resource reservation scheduling should also be quota-checked traceLog.Info("skipping quota enforcement for committed resource reservation intent") - QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped", "", "", "") + QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped_intent_internal", "", "", "") return result, nil } } // Step 2: Extract project, AZ, and hw_version from the request. + // Missing-data skips share a single decision bucket; the specific missing + // field is in the trace log. projectID := request.Spec.Data.ProjectID az := request.Spec.Data.AvailabilityZone hwVersion := request.Spec.Data.Flavor.Data.ExtraSpecs["hw_version"] if projectID == "" { traceLog.Warn("no project ID in request, skipping quota enforcement") - QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped", "", az, hwVersion) + QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped_missing_data", "", az, hwVersion) return result, nil } if az == "" { traceLog.Warn("no availability zone in request, skipping quota enforcement") - QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped", "", "", hwVersion) + QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped_missing_data", "", "", hwVersion) return result, nil } if hwVersion == "" { traceLog.Warn("no hw_version in flavor extra specs, skipping quota enforcement") - QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped", "", az, "") + QuotaEnforcementMetricsSingleton.RecordDecision(mode, "accept_skipped_missing_data", "", az, "") return result, nil } diff --git a/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_metrics.go b/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_metrics.go index 8d8edc17e..0743b6346 100644 --- a/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_metrics.go +++ b/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_metrics.go @@ -21,7 +21,10 @@ type QuotaEnforcementMetrics struct { // produces. Labels: // - mode: "enforce" | "shadow" // - decision: "accept_cr" | "accept_payg" | "accept_no_quota" | - // "accept_skipped" | "reject" + // "accept_skipped_intent_migration" | + // "accept_skipped_intent_internal" | + // "accept_skipped_missing_data" | + // "reject" // - resource: "ram" | "cores" | "instances" | "" (empty when // the decision is not driven by a single resource) // - availability_zone: AZ string or "" if unknown at decision time diff --git a/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_test.go b/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_test.go index 32ea16ad4..006dfba50 100644 --- a/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_test.go +++ b/internal/scheduling/nova/plugins/filters/filter_quota_enforcement_test.go @@ -78,7 +78,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { // Metric expectations — every case asserts exactly one increment on the // labeled series and exactly one series in the vector. expectMode string // "enforce" | "shadow" - expectDecision string // accept_cr | accept_payg | accept_no_quota | accept_skipped | reject + expectDecision string // accept_cr | accept_payg | accept_no_quota | accept_skipped_intent_migration | accept_skipped_intent_internal | accept_skipped_missing_data | reject expectResource string // "ram" | "cores" | "instances" | "" expectAZ string expectFG string @@ -263,7 +263,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_intent_migration", expectResource: "", expectAZ: "", expectFG: "", @@ -276,7 +276,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_intent_migration", expectResource: "", expectAZ: "", expectFG: "", @@ -289,7 +289,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_intent_internal", expectResource: "", expectAZ: "", expectFG: "", @@ -302,7 +302,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_intent_internal", expectResource: "", expectAZ: "", expectFG: "", @@ -314,7 +314,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_missing_data", expectResource: "", expectAZ: "az-1", expectFG: "", @@ -326,7 +326,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_missing_data", expectResource: "", expectAZ: "az-1", expectFG: "hana_v2", @@ -338,7 +338,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { expectAccept: true, enforce: true, expectMode: "enforce", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_missing_data", expectResource: "", expectAZ: "", expectFG: "hana_v2", @@ -904,7 +904,7 @@ func TestFilterQuotaEnforcement_Run(t *testing.T) { map[string]any{"_nova_check_type": "live_migrate"}), expectAccept: true, expectMode: "shadow", - expectDecision: "accept_skipped", + expectDecision: "accept_skipped_intent_migration", expectResource: "", expectAZ: "", expectFG: "",