commitments

package
v0.0.0-...-e625038 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 18, 2026 License: Apache-2.0 Imports: 50 Imported by: 0

Documentation

Index

Constants

View Source
const (

	// Resource type suffixes
	ResourceSuffixRAM       = "_ram"
	ResourceSuffixCores     = "_cores"
	ResourceSuffixInstances = "_instances"
)

Limes LIQUID resource naming convention: hw_version_<flavorgroup>_<resourcetype> Supported resource types: _ram, _cores, _instances

View Source
const (
	SkipReasonUnitMismatch       = "unit_mismatch"
	SkipReasonUnknownFlavorGroup = "unknown_flavor_group"
	SkipReasonInvalidResource    = "invalid_resource_name"
	SkipReasonEmptyUUID          = "empty_uuid"
	SkipReasonNonCompute         = "non_compute"
)

Skip reason labels for commitment processing

Variables

View Source
var (
	// CreatorValue identifies reservations created by this syncer.
	CreatorValue = "commitments-syncer"
)

Functions

func CheckCommitmentsInfoEndpoint

func CheckCommitmentsInfoEndpoint(ctx context.Context, config E2EChecksConfig)

CheckCommitmentsInfoEndpoint verifies that GET /commitments/v1/info returns 200 with a valid ServiceInfo.

func CheckCommitmentsMultiFlavorGroupBatch

func CheckCommitmentsMultiFlavorGroupBatch(ctx context.Context, config E2EChecksConfig)

CheckCommitmentsMultiFlavorGroupBatch exercises the pending→batch-confirm flow for each (AZ, resource) pair. For each pair it:

  1. Creates a pending commitment (UUID:A, amount=1, ExpiresAt=10min) — non-confirming, always accepted
  2. Sends an atomic batch: UUID:A pending→confirmed amount=3, UUID:B new confirmed amount=1
  3. If the batch is rejected (no capacity): cleans up the pending UUID:A and continues
  4. If the batch is accepted: parses and logs the usage report; deletes unless NoCleanup=true

This exercises the all-or-nothing semantics of change-commitments: if capacity for the full batch (4 units) is unavailable, neither UUID:A nor UUID:B is confirmed.

func CheckCommitmentsRoundTrip

func CheckCommitmentsRoundTrip(ctx context.Context, config E2EChecksConfig)

CheckCommitmentsRoundTrip iterates all HandlesCommitments resources from /info and for each (AZ, resource) pair:

  1. Creates a confirmed test commitment (amount=2, expires in 5 minutes)
  2. If accepted: calls the usage API to verify it returns a valid response, then deletes the commitment
  3. If rejected: logs the reason and continues — capacity rejection is not an error

Panics on infrastructure failures (non-200 from the API, deletion failure after acceptance).

func DeleteChildReservations

func DeleteChildReservations(ctx context.Context, k8sClient client.Client, cr *v1alpha1.CommittedResource) error

DeleteChildReservations deletes all Reservation CRDs belonging to cr, matched by CommitmentUUID. Called both by the controller on inactive/rollback transitions and by the API handler on CR deletion.

func GetFlavorGroupAndTypeFromResource

func GetFlavorGroupAndTypeFromResource(resourceName string) (string, v1alpha1.CommittedResourceType, error)

GetFlavorGroupAndTypeFromResource extracts the flavor group name and resource type from a LIQUID resource name. Accepts _ram (memory) and _cores (CPU) suffixes. _instances resources are not supported for commitments.

func GetFlavorGroupNameFromResource

func GetFlavorGroupNameFromResource(resourceName string) (string, error)

GetFlavorGroupNameFromResource extracts the flavor group name from a LIQUID resource name. Only accepts _ram resources since CommitmentState is RAM-based. Callers handling _cores or _instances must use a different approach.

func GetMaxSlotIndex

func GetMaxSlotIndex(reservations []v1alpha1.Reservation) int

func GetNextSlotIndex

func GetNextSlotIndex(reservations []v1alpha1.Reservation) int

Always continue counting slots from max, instead of filling gaps.

func LogFlavorGroupResourceConfig

func LogFlavorGroupResourceConfig(log logr.Logger, cfg map[string]FlavorGroupResourcesConfig)

LogFlavorGroupResourceConfig logs the effective RAM unit for each configured flavor group. It warns when RAMUnitGiB is 0, which silently defaults to 1 GiB.

func LoggerFromContext

func LoggerFromContext(ctx context.Context) logr.Logger

LoggerFromContext returns a logger with greq and req values from the context. This creates a child logger with the request tracking values pre-attached, so you don't need to repeat them in every log call.

func ResourceNameCores

func ResourceNameCores(flavorGroup string) string

ResourceNameCores creates a LIQUID resource name for CPU cores from a flavor group name. Format: hw_version_<flavorgroup>_cores

func ResourceNameInstances

func ResourceNameInstances(flavorGroup string) string

ResourceNameInstances creates a LIQUID resource name for instance count from a flavor group name. Format: hw_version_<flavorgroup>_instances

func ResourceNameRAM

func ResourceNameRAM(flavorGroup string) string

ResourceNameRAM creates a LIQUID resource name for RAM from a flavor group name. Format: hw_version_<flavorgroup>_ram

func RunCommitmentsE2EChecks

func RunCommitmentsE2EChecks(ctx context.Context, config E2EChecksConfig)

RunCommitmentsE2EChecks runs all e2e checks for the commitments API.

func WithGlobalRequestID

func WithGlobalRequestID(ctx context.Context, greq string) context.Context

WithGlobalRequestID creates a new context with the specified global request ID. This is used to propagate existing request IDs (e.g., from the creator annotation).

func WithNewGlobalRequestID

func WithNewGlobalRequestID(ctx context.Context) context.Context

WithNewGlobalRequestID creates a new context with a committed-resource-prefixed global request ID.

Types

type APIConfig

type APIConfig struct {
	// EnableChangeCommitments controls whether the change-commitments endpoint is active.
	// When false the endpoint returns HTTP 503; the info endpoint remains available.
	EnableChangeCommitments bool `json:"enableChangeCommitments"`
	// EnableReportUsage controls whether the report-usage endpoint is active.
	EnableReportUsage bool `json:"enableReportUsage"`
	// EnableReportCapacity controls whether the report-capacity endpoint is active.
	EnableReportCapacity bool `json:"enableReportCapacity"`
	// EnableQuotaAPI controls whether the quota API endpoint is active.
	// When false, the endpoint will return HTTP 503 Service Unavailable.
	EnableQuotaAPI bool `json:"enableQuota"`
	// WatchTimeout is how long the change-commitments handler polls CommittedResource
	// CRD conditions before giving up and rolling back.
	WatchTimeout metav1.Duration `json:"watchTimeout"`
	// WatchPollInterval is how frequently the change-commitments handler polls
	// CommittedResource CRD conditions while waiting for the controller outcome.
	WatchPollInterval metav1.Duration `json:"watchPollInterval"`
	// FlavorGroupResourceConfig maps flavor group IDs to resource flag configs; "*" acts as catch-all.
	FlavorGroupResourceConfig map[string]FlavorGroupResourcesConfig `json:"flavorGroupResourceConfig,omitempty"`
	// QuotaServedAvailabilityZones restricts quota handling to these AZs.
	// Quota received for AZs not in this list is silently skipped.
	// If empty/nil, no pre-filtering is applied (relies on error-based fallback).
	QuotaServedAvailabilityZones []string `json:"quotaServedAvailabilityZones,omitempty"`
}

APIConfig holds configuration for the LIQUID commitment HTTP endpoints.

func DefaultAPIConfig

func DefaultAPIConfig() APIConfig

func (APIConfig) ResourceConfigForGroup

func (c APIConfig) ResourceConfigForGroup(groupName string) FlavorGroupResourcesConfig

ResourceConfigForGroup returns the resource config for the given flavor group name, falling back to the "*" catch-all if no exact match exists.

type ApplyResult

type ApplyResult struct {
	// Created is the number of reservations created
	Created int
	// Deleted is the number of reservations deleted
	Deleted int
	// Repaired is the number of reservations repaired (metadata sync or recreated due to wrong config)
	Repaired int
	// TotalSlots is the total number of reservation slots that should exist after the apply.
	// Used by the CR controller to wait for the correct number of children in the cache.
	TotalSlots int
	// TouchedReservations are reservations that were created or updated
	TouchedReservations []v1alpha1.Reservation
	// RemovedReservations are reservations that were deleted
	RemovedReservations []v1alpha1.Reservation
}

ApplyResult contains the result of applying a commitment state.

type CapacityCalculator

type CapacityCalculator struct {
	// contains filtered or unexported fields
}

CapacityCalculator computes capacity reports for Limes LIQUID API.

func NewCapacityCalculator

func NewCapacityCalculator(client client.Client, conf APIConfig) *CapacityCalculator

func (*CapacityCalculator) CalculateCapacity

CalculateCapacity computes per-AZ capacity for all flavor groups. For each flavor group, three resources are reported: _ram, _cores, _instances. Capacity and usage are read from FlavorGroupCapacity CRDs pre-computed by the capacity controller. Usage is approximated from slot counts (total − placeable of the smallest flavor); this may slightly under-report usage when larger flavors are running, showing more free capacity than reality — acceptable for capacity planning purposes.

type Commitment

type Commitment struct {
	// A unique numerical identifier for this commitment. This API uses this
	// numerical ID to refer to the commitment in other API calls.
	ID int `json:"id"`
	// A unique string identifier for this commitment. The next major version of
	// this API will use this UUID instead of the numerical ID to refer to
	// commitments in API calls.
	UUID string `json:"uuid"`
	// The resource for which usage is committed.
	ServiceType  string `json:"service_type"`
	ResourceName string `json:"resource_name"`
	// The availability zone in which usage is committed.
	AvailabilityZone string `json:"availability_zone"`
	// The amount of usage that was committed to.
	Amount uint64 `json:"amount"`
	// For measured resources, the unit for this resource. The value from the
	// amount field is measured in this unit.
	Unit string `json:"unit"`
	// The requested duration of this commitment, expressed as a comma-separated
	// sequence of positive integer multiples of time units like "1 year,
	// 3 months". Acceptable time units include "second", "minute", "hour",
	// "day", "month" and "year".
	Duration string `json:"duration"`
	// UNIX timestamp when this commitment was created.
	CreatedAt uint64 `json:"created_at"`
	// UNIX timestamp when this commitment should be confirmed. Only shown if
	// this was given when creating the commitment, to delay confirmation into
	// the future.
	ConfirmBy *uint64 `json:"confirm_by,omitempty"`
	// UNIX timestamp when this commitment was confirmed. Only shown after
	// confirmation.
	ConfirmedAt *uint64 `json:"confirmed_at,omitempty"`
	// UNIX timestamp when this commitment is set to expire. Note that the
	// duration counts from confirmBy (or from createdAt for immediately-
	// confirmed commitments) and is calculated at creation time, so this is
	// also shown on unconfirmed commitments.
	ExpiresAt uint64 `json:"expires_at"`
	// Whether the commitment is marked for transfer to a different project.
	// Transferable commitments do not count towards quota calculation in their
	// project, but still block capacity and still count towards billing. Not
	// shown if false.
	Transferable bool `json:"transferable"`
	// The current status of this commitment. If provided, one of "planned",
	// "pending", "guaranteed", "confirmed", "superseded", or "expired".
	Status string `json:"status,omitempty"`
	// Whether a mail notification should be sent if a created commitment is
	// confirmed. Can only be set if the commitment contains a confirmBy value.
	NotifyOnConfirm bool `json:"notify_on_confirm"`

	// The openstack project ID this commitment is for.
	ProjectID string `json:"project_id"`
	// The openstack domain ID this commitment is for.
	DomainID string `json:"domain_id"`
}

Commitment model from the limes API. See: https://github.com/sapcc/limes/blob/5ea068b/docs/users/api-spec-resources.md?plain=1#L493 See: https://github.com/sapcc/go-api-declarations/blob/94ee3e5/limes/resources/commitment.go#L19

type CommitmentReservationController

type CommitmentReservationController struct {
	// Client for the kubernetes API.
	client.Client
	// Kubernetes scheme to use for the reservations.
	Scheme *runtime.Scheme
	// Configuration for the controller.
	Conf ReservationControllerConfig
	// SchedulerClient for making scheduler API calls.
	SchedulerClient *reservations.SchedulerClient
}

CommitmentReservationController reconciles commitment Reservation objects

func (*CommitmentReservationController) Init

Init initializes the reconciler with required clients and DB connection.

func (*CommitmentReservationController) Reconcile

Reconcile is part of the main kubernetes reconciliation loop which aims to move the current state of the cluster closer to the desired state. Note: This controller only handles commitment reservations, as filtered by the predicate.

func (*CommitmentReservationController) SetupWithManager

func (r *CommitmentReservationController) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error

SetupWithManager sets up the controller with the Manager.

type CommitmentState

type CommitmentState struct {
	// CommitmentUUID is the UUID of the commitment this state corresponds to.
	CommitmentUUID string
	// ProjectID is the OpenStack project this commitment belongs to
	ProjectID string
	// DomainID is the OpenStack domain this commitment belongs to
	DomainID string
	// FlavorGroupName identifies the flavor group (e.g., "hana_medium_v2")
	FlavorGroupName string
	// ResourceType is the kind of resource committed: memory or cores.
	ResourceType v1alpha1.CommittedResourceType
	// TotalMemoryBytes is the total memory in bytes across all reservation slots (memory commitments only).
	TotalMemoryBytes int64
	// TotalCores is the number of committed CPU cores (cores commitments only).
	TotalCores int64
	// AvailabilityZone specifies the availability zone for this commitment
	AvailabilityZone string
	// StartTime is when the commitment becomes active
	StartTime *time.Time
	// EndTime is when the commitment expires
	EndTime *time.Time
	// CreatorRequestID is the request ID that triggered this state change (for traceability)
	CreatorRequestID string
	// NamePrefix overrides the default "commitment-<uuid>-" reservation naming convention.
	// When set (e.g. "<cr-name>-"), Reservation CRDs are named "<NamePrefix><slot-index>".
	// Used by the CommittedResource controller; leave empty for the legacy syncer path.
	NamePrefix string
	// ParentGeneration is the Generation of the parent CommittedResource CRD. Written into
	// Reservation spec so the Reservation controller can echo it back in status, letting
	// the CR controller detect when all children have been processed for the current spec.
	// Zero for syncer-created reservations (no parent CR).
	ParentGeneration int64
	// State is the lifecycle state from Limes (planned/pending/guaranteed/confirmed/superseded/expired).
	State v1alpha1.CommitmentStatus
}

CommitmentState represents desired or current commitment resource allocation.

func FromChangeCommitmentTargetState

func FromChangeCommitmentTargetState(
	commitment liquid.Commitment,
	projectID string,
	domainID string,
	flavorGroupName string,
	resourceType v1alpha1.CommittedResourceType,
	az string,
	ramUnitMiB uint64,
) (*CommitmentState, error)

FromChangeCommitmentTargetState converts LIQUID API request to CommitmentState. ramUnitMiB is the size of one external RAM unit in MiB:

  • fixed-ratio flavor groups: SmallestFlavor.MemoryMB (1 unit = 1 smallest-flavor slot)
  • variable-ratio flavor groups: 1024 (1 unit = 1 GiB)

func FromCommitment

func FromCommitment(
	commitment Commitment,
	ramUnitMiB uint64,
) (*CommitmentState, error)

FromCommitment converts Limes commitment to CommitmentState. ramUnitMiB is the size of one external RAM unit in MiB, as returned by RAMResourceTypeConfig.RAMUnitMiB():

  • fixed-ratio flavor groups: SmallestFlavor.MemoryMB (1 unit = 1 smallest-flavor slot)
  • variable-ratio flavor groups: 1024 (1 unit = 1 GiB)

func FromCommittedResource

func FromCommittedResource(cr v1alpha1.CommittedResource) (*CommitmentState, error)

FromCommittedResource reads CommitmentState from a CommittedResource CRD.

func FromReservations

func FromReservations(reservations []v1alpha1.Reservation) (*CommitmentState, error)

FromReservations reconstructs CommitmentState from existing Reservation CRDs.

type CommitmentStateWithUsage

type CommitmentStateWithUsage struct {
	CommitmentState
	// RemainingMemoryBytes is the uncommitted capacity left for VM assignment
	RemainingMemoryBytes int64
	// AssignedInstances tracks which VM instances have been assigned to this commitment
	AssignedInstances []string
	// UsedVCPUs is the total vCPU count of assigned VM instances
	UsedVCPUs int64
}

CommitmentStateWithUsage extends CommitmentState with usage tracking for billing calculations. Used by the report-usage API to track remaining capacity during VM-to-commitment assignment.

func NewCommitmentStateWithUsage

func NewCommitmentStateWithUsage(state *CommitmentState) *CommitmentStateWithUsage

NewCommitmentStateWithUsage creates a CommitmentStateWithUsage from a CommitmentState.

func (*CommitmentStateWithUsage) AssignVM

func (c *CommitmentStateWithUsage) AssignVM(vmUUID string, vmMemoryBytes, vCPUs int64) bool

AssignVM attempts to assign a VM to this commitment if there's enough capacity. Returns true if the VM was assigned, false if not enough capacity.

func (*CommitmentStateWithUsage) HasRemainingCapacity

func (c *CommitmentStateWithUsage) HasRemainingCapacity() bool

HasRemainingCapacity returns true if the commitment has any remaining capacity.

type CommitmentsClient

type CommitmentsClient interface {
	// Init the client.
	Init(ctx context.Context, client client.Client, conf SyncerConfig) error
	// List all projects to resolve commitments.
	ListProjects(ctx context.Context) ([]Project, error)
	// List all commitments with resolved metadata (e.g. project, flavor, ...).
	ListCommitmentsByID(ctx context.Context, projects ...Project) (map[string]Commitment, error)
}

Client to fetch commitments.

func NewCommitmentsClient

func NewCommitmentsClient() CommitmentsClient

type CommittedResourceController

type CommittedResourceController struct {
	client.Client
	Scheme *runtime.Scheme
	Conf   CommittedResourceControllerConfig
}

CommittedResourceController reconciles CommittedResource CRDs and owns all child Reservation CRUD.

func (*CommittedResourceController) Reconcile

func (*CommittedResourceController) SetupWithManager

func (r *CommittedResourceController) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error

SetupWithManager sets up the controller with the Manager.

type CommittedResourceControllerConfig

type CommittedResourceControllerConfig struct {
	// RequeueIntervalRetry is the base back-off interval when placement fails (AllowRejection=false path).
	// The actual delay doubles with each consecutive failure: base * 2^min(failures, 6), capped at MaxRequeueInterval.
	// If zero (unconfigured), backoff is disabled and the controller retries immediately on every failure.
	RequeueIntervalRetry metav1.Duration `json:"requeueIntervalRetry"`

	// MaxRequeueInterval caps the exponential backoff delay.
	// Once this ceiling is reached, every subsequent retry fires after exactly this interval.
	MaxRequeueInterval metav1.Duration `json:"maxRequeueInterval"`
}

CommittedResourceControllerConfig holds tuning knobs for the CommittedResource CRD controller.

func DefaultCommittedResourceControllerConfig

func DefaultCommittedResourceControllerConfig() CommittedResourceControllerConfig

func (*CommittedResourceControllerConfig) ApplyDefaults

func (c *CommittedResourceControllerConfig) ApplyDefaults()

ApplyDefaults fills in zero-value fields from the defaults, leaving explicitly configured values intact.

type Config

type Config struct {
	ReservationController       ReservationControllerConfig       `json:"committedResourceReservationController"`
	CommittedResourceController CommittedResourceControllerConfig `json:"committedResourceController"`
	UsageReconciler             UsageReconcilerConfig             `json:"committedResourceUsageReconciler"`
	API                         APIConfig                         `json:"committedResourceAPI"`

	// DatasourceName is the name of the Datasource CRD that provides database
	// connection info. Used to construct the UsageDBClient for report-usage and usage reconciler.
	DatasourceName string `json:"datasourceName,omitempty"`
}

Config aggregates configuration for all commitments components. Each controller and the API have their own sub-struct so that unrelated fields are never visible to the wrong component.

type E2EChecksConfig

type E2EChecksConfig struct {
	// BaseURL for the commitments API. If empty, defaults to defaultCommitmentsAPIURL.
	BaseURL string `json:"baseURL"`
	// NoCleanup prevents test commitments from being deleted after a successful run.
	// Useful for local inspection with Tilt. Zero value (false) means cleanup runs normally.
	NoCleanup bool `json:"noCleanup,omitempty"`
	// ProjectID is the OpenStack project UUID for all e2e test commitments.
	// If empty, falls back to RoundTripCheck.TestProjectID, then defaultE2EProjectUUID.
	ProjectID string `json:"projectID,omitempty"`
	// AZs is the list of availability zones to test. If empty, falls back to
	// RoundTripCheck.AZ, then uses "qa-de-1b".
	AZs []string `json:"azs,omitempty"`
	// RequestTimeout is the per-request HTTP timeout for change-commitments calls.
	// Defaults to 30s if unset.
	RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"`
	// RoundTripCheck holds optional overrides for backward compatibility.
	// Prefer the top-level ProjectID and AZs fields for new configurations.
	RoundTripCheck *E2ERoundTripConfig `json:"roundTripCheck,omitempty"`
}

E2EChecksConfig holds the configuration for CR e2e checks.

type E2ERoundTripConfig deprecated

type E2ERoundTripConfig struct {
	// AZ is the availability zone to use (e.g. "qa-de-1d"). Defaults to "" if not set.
	AZ string `json:"az"`
	// TestProjectID is the OpenStack project UUID to create test commitments under.
	// Defaults to defaultE2EProjectUUID if not set.
	TestProjectID string `json:"testProjectID"`
}

E2ERoundTripConfig holds optional overrides for the create→delete round-trip e2e check.

Deprecated: use E2EChecksConfig.ProjectID and E2EChecksConfig.AZs instead.

type Flavor

type Flavor struct {
	ID          string  `json:"id"`
	Disk        int     `json:"disk"` // in GB.
	RAM         int     `json:"ram"`  // in MB.
	Name        string  `json:"name"`
	RxTxFactor  float64 `json:"rxtx_factor"`
	VCPUs       int     `json:"vcpus"`
	IsPublic    bool    `json:"os-flavor-access:is_public"`
	Ephemeral   int     `json:"OS-FLV-EXT-DATA:ephemeral"`
	Description string  `json:"description"`

	// JSON string of extra specifications used when scheduling the flavor.
	ExtraSpecs map[string]string `json:"extra_specs" db:"extra_specs"`
}

OpenStack flavor model as returned by the Nova API under /flavors/detail. See: https://docs.openstack.org/api-ref/compute/#list-flavors

type FlavorGroupResourcesConfig

type FlavorGroupResourcesConfig struct {
	RAM       RAMResourceTypeConfig `json:"ram"`
	Cores     ResourceTypeConfig    `json:"cores"`
	Instances ResourceTypeConfig    `json:"instances"`
}

FlavorGroupResourcesConfig groups resource type configs for the three resources of a flavor group.

func ResourceConfigForGroup

func ResourceConfigForGroup(cfg map[string]FlavorGroupResourcesConfig, groupName string) FlavorGroupResourcesConfig

ResourceConfigForGroup returns the resource config for the given flavor group name, falling back to the "*" catch-all if no exact match exists.

type Project

type Project struct {
	// DomainID is the domain ID the project belongs to.
	DomainID string `json:"domain_id"`
	// ID is the unique ID of the project.
	ID string `json:"id"`
	// Name is the name of the project.
	Name string `json:"name"`
	// ParentID is the parent_id of the project.
	ParentID string `json:"parent_id"`
}

OpenStack project model as returned by the Keystone API under /projects. See: https://docs.openstack.org/api-ref/identity/v3/#projects

type RAMResourceTypeConfig

type RAMResourceTypeConfig struct {
	HandlesCommitments bool `json:"handlesCommitments"`
	HasCapacity        bool `json:"hasCapacity"`
	HasQuota           bool `json:"hasQuota"`
	// RAMUnitGiB is the size of one declared LIQUID RAM unit in GiB.
	// Fixed-ratio groups set this to the smallest flavor's RAM (e.g. 480 for a 480 GiB HANA slot).
	// Variable-ratio groups set this to 1 (1 unit = 1 GiB).
	// Defaults to 1 if unset.
	RAMUnitGiB uint64 `json:"ramUnitGiB,omitempty"`
}

RAMResourceTypeConfig extends ResourceTypeConfig with RAM-specific unit configuration.

func (RAMResourceTypeConfig) DeclaredUnitsToGiB

func (c RAMResourceTypeConfig) DeclaredUnitsToGiB(units int64) int64

DeclaredUnitsToGiB converts a value in declared LIQUID units to GiB.

func (RAMResourceTypeConfig) GiBToDeclaredUnits

func (c RAMResourceTypeConfig) GiBToDeclaredUnits(gib int64) int64

GiBToDeclaredUnits converts a GiB value to declared LIQUID units.

func (RAMResourceTypeConfig) RAMUnitMiB

func (c RAMResourceTypeConfig) RAMUnitMiB() uint64

RAMUnitMiB returns the RAM unit in MiB (RAMUnitGiB × 1024), defaulting to 1024 if unset.

type ReservationControllerConfig

type ReservationControllerConfig struct {
	// RequeueIntervalActive is how often to re-verify a healthy Reservation CRD.
	RequeueIntervalActive metav1.Duration `json:"requeueIntervalActive"`
	// RequeueIntervalRetry is the back-off interval when knowledge is unavailable.
	RequeueIntervalRetry metav1.Duration `json:"requeueIntervalRetry"`
	// RequeueIntervalGracePeriod is how often to re-check while a VM allocation
	// is still within AllocationGracePeriod. Shorter than RequeueIntervalActive.
	RequeueIntervalGracePeriod metav1.Duration `json:"requeueIntervalGracePeriod"`
	// AllocationGracePeriod is the time window after a VM is allocated to a
	// reservation during which it's expected to appear on the target host.
	// VMs not confirmed within this period are considered stale and removed.
	AllocationGracePeriod metav1.Duration `json:"allocationGracePeriod"`
	// SchedulerURL is the endpoint of the nova external scheduler.
	SchedulerURL string `json:"schedulerURL"`
	// PipelineDefault is the fallback pipeline when no FlavorGroupPipelines entry matches.
	PipelineDefault string `json:"pipelineDefault"`
	// FlavorGroupPipelines maps flavor group IDs to pipeline names; "*" acts as catch-all.
	FlavorGroupPipelines map[string]string `json:"flavorGroupPipelines,omitempty"`
}

ReservationControllerConfig holds tuning knobs for the Reservation CRD controller.

type ReservationManager

type ReservationManager struct {
	client.Client
}

ReservationManager handles CRUD operations for Reservation CRDs.

func NewReservationManager

func NewReservationManager(k8sClient client.Client) *ReservationManager

func (*ReservationManager) ApplyCommitmentState

func (m *ReservationManager) ApplyCommitmentState(
	ctx context.Context,
	log logr.Logger,
	desiredState *CommitmentState,
	flavorGroups map[string]compute.FlavorGroupFeature,
	creator string,
) (*ApplyResult, error)

ApplyCommitmentState synchronizes Reservation CRDs to match the desired commitment state. This function performs CRUD operations (create/update/delete) on reservation slots to align with the capacity specified in desiredState.

Entry points:

  • from Syncer - periodic sync with Limes state
  • from API ChangeCommitmentsHandler - batch processing of commitment changes

The function is idempotent and handles:

  • Repairing inconsistent slots (wrong flavor group/project)
  • Creating new reservation slots when capacity increases
  • Deleting unused/excess slots when capacity decreases
  • Syncing reservation metadata for all remaining slots

Returns ApplyResult containing touched/removed reservations and counts for metrics.

type ResourceTypeConfig

type ResourceTypeConfig struct {
	HandlesCommitments bool `json:"handlesCommitments"`
	HasCapacity        bool `json:"hasCapacity"`
	HasQuota           bool `json:"hasQuota"`
}

ResourceTypeConfig holds per-resource flags for a single resource type within a flavor group.

type Server

type Server struct {
	ID                             string  `json:"id" db:"id,primarykey"`
	Name                           string  `json:"name" db:"name"`
	Status                         string  `json:"status" db:"status"`
	TenantID                       string  `json:"tenant_id" db:"tenant_id"`
	UserID                         string  `json:"user_id" db:"user_id"`
	HostID                         string  `json:"hostId" db:"host_id"`
	Created                        string  `json:"created" db:"created"`
	Updated                        string  `json:"updated" db:"updated"`
	AccessIPv4                     string  `json:"accessIPv4" db:"access_ipv4"`
	AccessIPv6                     string  `json:"accessIPv6" db:"access_ipv6"`
	OSDCFdiskConfig                string  `json:"OS-DCF:diskConfig" db:"os_dcf_disk_config"`
	Progress                       int     `json:"progress" db:"progress"`
	OSEXTAvailabilityZone          string  `json:"OS-EXT-AZ:availability_zone" db:"os_ext_az_availability_zone"`
	ConfigDrive                    string  `json:"config_drive" db:"config_drive"`
	KeyName                        string  `json:"key_name" db:"key_name"`
	OSSRVUSGLaunchedAt             string  `json:"OS-SRV-USG:launched_at" db:"os_srv_usg_launched_at"`
	OSSRVUSGTerminatedAt           *string `json:"OS-SRV-USG:terminated_at" db:"os_srv_usg_terminated_at"`
	OSEXTSRVATTRHost               string  `json:"OS-EXT-SRV-ATTR:host" db:"os_ext_srv_attr_host"`
	OSEXTSRVATTRInstanceName       string  `json:"OS-EXT-SRV-ATTR:instance_name" db:"os_ext_srv_attr_instance_name"`
	OSEXTSRVATTRHypervisorHostname string  `json:"OS-EXT-SRV-ATTR:hypervisor_hostname" db:"os_ext_srv_attr_hypervisor_hostname"`
	OSEXTSTSTaskState              *string `json:"OS-EXT-STS:task_state" db:"os_ext_sts_task_state"`
	OSEXTSTSVmState                string  `json:"OS-EXT-STS:vm_state" db:"os_ext_sts_vm_state"`
	OSEXTSTSPowerState             int     `json:"OS-EXT-STS:power_state" db:"os_ext_sts_power_state"`

	// From nested JSON
	FlavorName string `json:"-" db:"flavor_name"`
}

OpenStack server model as returned by the Nova API under /servers/detail. See: https://docs.openstack.org/api-ref/compute/#list-servers-detailed

func (*Server) MarshalJSON

func (s *Server) MarshalJSON() ([]byte, error)

Custom marshaler for OpenStackServer to handle nested JSON.

func (*Server) UnmarshalJSON

func (s *Server) UnmarshalJSON(data []byte) error

Custom unmarshaler for OpenStackServer to handle nested JSON.

type Syncer

type Syncer struct {
	// Client to fetch commitments from Limes
	CommitmentsClient
	// Kubernetes client for CRD operations
	client.Client
	// contains filtered or unexported fields
}

func NewSyncer

func NewSyncer(k8sClient client.Client, monitor *SyncerMonitor) *Syncer

func (*Syncer) Init

func (s *Syncer) Init(ctx context.Context, config SyncerConfig) error

func (*Syncer) SyncReservations

func (s *Syncer) SyncReservations(ctx context.Context) error

SyncReservations fetches commitments from Limes and synchronizes Reservation CRDs.

type SyncerConfig

type SyncerConfig struct {
	// Secret ref to keystone credentials stored in a k8s secret.
	KeystoneSecretRef corev1.SecretReference `json:"keystoneSecretRef"`
	// Secret ref to SSO credentials stored in a k8s secret, if applicable.
	SSOSecretRef *corev1.SecretReference `json:"ssoSecretRef"`
	// SyncInterval defines how often the syncer reconciles Limes commitments to Reservation CRDs.
	SyncInterval metav1.Duration `json:"committedResourceSyncInterval"`
	// FlavorGroupResourceConfig maps flavor group names to resource configs; "*" acts as catch-all.
	FlavorGroupResourceConfig map[string]FlavorGroupResourcesConfig `json:"flavorGroupResourceConfig,omitempty"`
}

func (SyncerConfig) ResourceConfigForGroup

func (c SyncerConfig) ResourceConfigForGroup(groupName string) FlavorGroupResourcesConfig

ResourceConfigForGroup returns the resource config for the given flavor group name.

type SyncerMonitor

type SyncerMonitor struct {
	// contains filtered or unexported fields
}

SyncerMonitor provides metrics for the commitment syncer.

func NewSyncerMonitor

func NewSyncerMonitor() *SyncerMonitor

NewSyncerMonitor creates a new monitor with Prometheus metrics.

func (*SyncerMonitor) Collect

func (m *SyncerMonitor) Collect(ch chan<- prometheus.Metric)

Collect implements prometheus.Collector.

func (*SyncerMonitor) Describe

func (m *SyncerMonitor) Describe(ch chan<- *prometheus.Desc)

Describe implements prometheus.Collector.

func (*SyncerMonitor) RecordCRCreates

func (m *SyncerMonitor) RecordCRCreates(count int)

func (*SyncerMonitor) RecordCRDeletes

func (m *SyncerMonitor) RecordCRDeletes(count int)

func (*SyncerMonitor) RecordCRUpdates

func (m *SyncerMonitor) RecordCRUpdates(count int)

func (*SyncerMonitor) RecordCommitmentSkipped

func (m *SyncerMonitor) RecordCommitmentSkipped(reason string)

func (*SyncerMonitor) RecordDuration

func (m *SyncerMonitor) RecordDuration(seconds float64)

func (*SyncerMonitor) RecordError

func (m *SyncerMonitor) RecordError()

func (*SyncerMonitor) RecordStaleCRs

func (m *SyncerMonitor) RecordStaleCRs(count int)

func (*SyncerMonitor) SetLimesCommitmentsActive

func (m *SyncerMonitor) SetLimesCommitmentsActive(count int)

type UsageCalculator

type UsageCalculator struct {
	// contains filtered or unexported fields
}

UsageCalculator computes usage reports for Limes LIQUID API.

func NewUsageCalculator

func NewUsageCalculator(client client.Client, usageDB UsageDBClient, config APIConfig) *UsageCalculator

NewUsageCalculator creates a new UsageCalculator instance.

func (*UsageCalculator) BuildVMAssignmentsFromStatus

func (c *UsageCalculator) BuildVMAssignmentsFromStatus(ctx context.Context, projectID string) (map[string]string, error)

BuildVMAssignmentsFromStatus reads pre-computed VM-to-commitment assignments from CommittedResource CRD status. Returns a map of vmUUID → commitmentUUID (empty string = PAYG). This is the read path that replaces the inline assignment algorithm in the usage API.

func (*UsageCalculator) CalculateUsage

func (c *UsageCalculator) CalculateUsage(
	ctx context.Context,
	log logr.Logger,
	projectID string,
	allAZs []liquid.AvailabilityZone,
) (liquid.ServiceUsageReport, error)

CalculateUsage computes the usage report for a specific project. VM-to-commitment assignment is read from CommittedResource CRD status (pre-computed by the UsageReconciler). If a CR has no usage status yet, its VMs appear as PAYG until the first reconcile completes (within one CooldownInterval).

type UsageDBClient

type UsageDBClient interface {
	// ListProjectVMs returns all VMs for a project with their flavor data.
	ListProjectVMs(ctx context.Context, projectID string) ([]VMRow, error)
}

UsageDBClient is the minimal interface for querying VM usage data from Postgres.

func NewDBUsageClient

func NewDBUsageClient(k8sClient client.Client, datasourceName string) UsageDBClient

NewDBUsageClient creates a UsageDBClient that lazily connects to Postgres via the named Datasource CRD.

type UsageReconciler

type UsageReconciler struct {
	client.Client
	Conf    UsageReconcilerConfig
	UsageDB UsageDBClient
	Monitor UsageReconcilerMonitor
}

UsageReconciler reconciles CommittedResource.Status usage fields (AssignedInstances, UsedResources, LastUsageReconcileAt) by running the deterministic VM-to-CR assignment periodically and on relevant change events.

func (*UsageReconciler) Reconcile

func (r *UsageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)

func (*UsageReconciler) SetupWithManager

func (r *UsageReconciler) SetupWithManager(mgr ctrl.Manager, mcl *multicluster.Client) error

SetupWithManager registers the usage reconciler with the controller manager.

type UsageReconcilerConfig

type UsageReconcilerConfig struct {
	// CooldownInterval is the minimum time between usage reconcile runs for the same CommittedResource.
	// If a reconcile ran within this window, the next trigger is deferred until the window expires.
	// This interval also acts as the periodic fallback: every successful reconcile schedules the
	// next run after this duration so that changes not caught by watches are still picked up.
	CooldownInterval metav1.Duration `json:"cooldownInterval"`
}

UsageReconcilerConfig holds tuning knobs for the usage reconciler.

func DefaultUsageReconcilerConfig

func DefaultUsageReconcilerConfig() UsageReconcilerConfig

func (*UsageReconcilerConfig) ApplyDefaults

func (c *UsageReconcilerConfig) ApplyDefaults()

ApplyDefaults fills in zero-value fields from the defaults, leaving explicitly configured values intact.

type UsageReconcilerMonitor

type UsageReconcilerMonitor struct {
	// contains filtered or unexported fields
}

UsageReconcilerMonitor provides metrics for the usage reconciler.

func NewUsageReconcilerMonitor

func NewUsageReconcilerMonitor() UsageReconcilerMonitor

NewUsageReconcilerMonitor creates a new monitor with Prometheus metrics.

func (UsageReconcilerMonitor) Collect

func (m UsageReconcilerMonitor) Collect(ch chan<- prometheus.Metric)

Collect implements prometheus.Collector.

func (UsageReconcilerMonitor) Describe

func (m UsageReconcilerMonitor) Describe(ch chan<- *prometheus.Desc)

Describe implements prometheus.Collector.

type VMRow

type VMRow struct {
	ID           string
	Name         string
	Status       string
	Created      string
	AZ           string
	Hypervisor   string
	FlavorName   string
	FlavorRAM    uint64
	FlavorVCPUs  uint64
	FlavorDisk   uint64
	FlavorExtras string // JSON string of flavor extra_specs
	OSType       string // pre-computed from Glance image properties; "unknown" when not found
}

VMRow is the result of a joined server+flavor+image query from Postgres.

type VMUsageInfo

type VMUsageInfo struct {
	UUID          string
	Name          string
	FlavorName    string
	FlavorGroup   string
	Status        string
	MemoryMB      uint64
	VCPUs         uint64
	DiskGB        uint64
	VideoRAMMiB   *uint64 // optional, from flavor extra_specs hw_video:ram_max_mb
	OSType        string  // pre-computed from Glance image; "unknown" for volume-booted or unmapped images
	AZ            string
	Hypervisor    string
	CreatedAt     time.Time
	UsageMultiple uint64 // RAM in the group's declared unit: slot count (fixed-ratio) or GiB (variable-ratio)
}

VMUsageInfo contains VM information needed for usage calculation. This is a local view of the VM enriched with flavor group information.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL