From d99e352a388653b58ecd146388455d7c7adadb56 Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Mon, 29 Dec 2025 11:49:31 +0100 Subject: [PATCH 1/3] Extend hypervisor crd for cortex filtering --- api/v1/hypervisor_types.go | 49 +++++++++++-- api/v1/zz_generated.deepcopy.go | 59 +++++++++++++-- ...{capabilitiesstatus.go => capabilities.go} | 16 ++--- .../api/v1/domaincapabilities.go | 65 +++++++++++++++++ applyconfigurations/api/v1/hypervisorspec.go | 11 +++ .../api/v1/hypervisorstatus.go | 70 ++++++++++++++---- applyconfigurations/utils.go | 6 +- .../crds/hypervisor-crd.yaml | 71 ++++++++++++++++++- .../crd/bases/kvm.cloud.sap_hypervisors.yaml | 71 ++++++++++++++++++- 9 files changed, 382 insertions(+), 36 deletions(-) rename applyconfigurations/api/v1/{capabilitiesstatus.go => capabilities.go} (62%) create mode 100644 applyconfigurations/api/v1/domaincapabilities.go diff --git a/api/v1/hypervisor_types.go b/api/v1/hypervisor_types.go index e38a2e3..b6bf608 100644 --- a/api/v1/hypervisor_types.go +++ b/api/v1/hypervisor_types.go @@ -93,6 +93,12 @@ type HypervisorSpec struct { // Aggregates are used to apply aggregates to the hypervisor. Aggregates []string `json:"aggregates"` + // +kubebuilder:default:={} + // AllowedProjects defines which openstack projects are allowed to schedule + // instances on this hypervisor. The values of this list should be project + // uuids. If left empty, all projects are allowed. + AllowedProjects []string `json:"allowedProjects"` + // +kubebuilder:default:=true // HighAvailability is used to enable the high availability handling of the hypervisor. HighAvailability bool `json:"highAvailability"` @@ -190,8 +196,8 @@ type OperatingSystemStatus struct { GardenLinuxFeatures []string `json:"gardenLinuxFeatures,omitempty"` } -// Current capabilities reported by libvirt. -type CapabilitiesStatus struct { +// Capabilities of the hypervisor as reported by libvirt. +type Capabilities struct { // +kubebuilder:default:=unknown // The hosts CPU architecture (not the guests). HostCpuArch string `json:"cpuArch,omitempty"` @@ -201,6 +207,26 @@ type CapabilitiesStatus struct { HostCpus resource.Quantity `json:"cpus,omitempty"` } +// Domain capabilities of the hypervisor as reported by libvirt. +// These details are relevant to check if a VM can be scheduled on the hypervisor. +type DomainCapabilities struct { + // +kubebuilder:default:=unknown + // The available domain cpu architecture. + Arch string `json:"arch,omitempty"` + // +kubebuilder:default:=unknown + // The supported type of virtualization for domains, such as "ch". + HypervisorType string `json:"hypervisorType,omitempty"` + // +kubebuilder:default:={} + // Supported devices for domains, such as "video". + SupportedDevices []string `json:"supportedDevices,omitempty"` + // +kubebuilder:default:={} + // Supported cpu modes for domains, such as "host-passthrough". + SupportedCpuModes []string `json:"supportedCpuModes,omitempty"` + // +kubebuilder:default:={} + // Supported features for domains, such as "sev" or "sgx". + SupportedFeatures []string `json:"supportedFeatures,omitempty"` +} + // HypervisorStatus defines the observed state of Hypervisor type HypervisorStatus struct { // +kubebuilder:default:=unknown @@ -216,8 +242,23 @@ type HypervisorStatus struct { // Represents the Hypervisor hosted Virtual Machines Instances []Instance `json:"instances,omitempty"` - // The capabilities of the hypervisors as reported by libvirt. - Capabilities CapabilitiesStatus `json:"capabilities,omitempty"` + // Auto-discovered capabilities as reported by libvirt. + Capabilities Capabilities `json:"capabilities"` + + // Auto-discovered domain capabilities relevant to check if a VM + // can be scheduled on the hypervisor. + DomainCapabilities DomainCapabilities `json:"domainCapabilities"` + + // +kubebuilder:default:={} + // Auto-discovered capacity available in total on the hypervisor. + // The remaining physical capacity can be calculated + // as Capacity - Allocation. + Capacity map[string]resource.Quantity `json:"capacity,omitempty"` + + // +kubebuilder:default:={} + // Auto-discovered capacity currently allocated by instances + // on the hypervisor. Note that this does not include reserved capacity. + Allocation map[string]resource.Quantity `json:"allocation,omitempty"` // +kubebuilder:default:=0 // Represent the num of instances diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 1950f3b..7336a32 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -22,23 +22,54 @@ limitations under the License. package v1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CapabilitiesStatus) DeepCopyInto(out *CapabilitiesStatus) { +func (in *Capabilities) DeepCopyInto(out *Capabilities) { *out = *in out.HostMemory = in.HostMemory.DeepCopy() out.HostCpus = in.HostCpus.DeepCopy() } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapabilitiesStatus. -func (in *CapabilitiesStatus) DeepCopy() *CapabilitiesStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Capabilities. +func (in *Capabilities) DeepCopy() *Capabilities { if in == nil { return nil } - out := new(CapabilitiesStatus) + out := new(Capabilities) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainCapabilities) DeepCopyInto(out *DomainCapabilities) { + *out = *in + if in.SupportedDevices != nil { + in, out := &in.SupportedDevices, &out.SupportedDevices + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SupportedCpuModes != nil { + in, out := &in.SupportedCpuModes, &out.SupportedCpuModes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SupportedFeatures != nil { + in, out := &in.SupportedFeatures, &out.SupportedFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainCapabilities. +func (in *DomainCapabilities) DeepCopy() *DomainCapabilities { + if in == nil { + return nil + } + out := new(DomainCapabilities) in.DeepCopyInto(out) return out } @@ -231,6 +262,11 @@ func (in *HypervisorSpec) DeepCopyInto(out *HypervisorSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.AllowedProjects != nil { + in, out := &in.AllowedProjects, &out.AllowedProjects + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HypervisorSpec. @@ -254,6 +290,21 @@ func (in *HypervisorStatus) DeepCopyInto(out *HypervisorStatus) { copy(*out, *in) } in.Capabilities.DeepCopyInto(&out.Capabilities) + in.DomainCapabilities.DeepCopyInto(&out.DomainCapabilities) + if in.Capacity != nil { + in, out := &in.Capacity, &out.Capacity + *out = make(map[string]resource.Quantity, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + if in.Allocation != nil { + in, out := &in.Allocation, &out.Allocation + *out = make(map[string]resource.Quantity, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } if in.Traits != nil { in, out := &in.Traits, &out.Traits *out = make([]string, len(*in)) diff --git a/applyconfigurations/api/v1/capabilitiesstatus.go b/applyconfigurations/api/v1/capabilities.go similarity index 62% rename from applyconfigurations/api/v1/capabilitiesstatus.go rename to applyconfigurations/api/v1/capabilities.go index 0ab1ec7..7b87dd8 100644 --- a/applyconfigurations/api/v1/capabilitiesstatus.go +++ b/applyconfigurations/api/v1/capabilities.go @@ -6,24 +6,24 @@ import ( resource "k8s.io/apimachinery/pkg/api/resource" ) -// CapabilitiesStatusApplyConfiguration represents a declarative configuration of the CapabilitiesStatus type for use +// CapabilitiesApplyConfiguration represents a declarative configuration of the Capabilities type for use // with apply. -type CapabilitiesStatusApplyConfiguration struct { +type CapabilitiesApplyConfiguration struct { HostCpuArch *string `json:"cpuArch,omitempty"` HostMemory *resource.Quantity `json:"memory,omitempty"` HostCpus *resource.Quantity `json:"cpus,omitempty"` } -// CapabilitiesStatusApplyConfiguration constructs a declarative configuration of the CapabilitiesStatus type for use with +// CapabilitiesApplyConfiguration constructs a declarative configuration of the Capabilities type for use with // apply. -func CapabilitiesStatus() *CapabilitiesStatusApplyConfiguration { - return &CapabilitiesStatusApplyConfiguration{} +func Capabilities() *CapabilitiesApplyConfiguration { + return &CapabilitiesApplyConfiguration{} } // WithHostCpuArch sets the HostCpuArch field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the HostCpuArch field is set to the value of the last call. -func (b *CapabilitiesStatusApplyConfiguration) WithHostCpuArch(value string) *CapabilitiesStatusApplyConfiguration { +func (b *CapabilitiesApplyConfiguration) WithHostCpuArch(value string) *CapabilitiesApplyConfiguration { b.HostCpuArch = &value return b } @@ -31,7 +31,7 @@ func (b *CapabilitiesStatusApplyConfiguration) WithHostCpuArch(value string) *Ca // WithHostMemory sets the HostMemory field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the HostMemory field is set to the value of the last call. -func (b *CapabilitiesStatusApplyConfiguration) WithHostMemory(value resource.Quantity) *CapabilitiesStatusApplyConfiguration { +func (b *CapabilitiesApplyConfiguration) WithHostMemory(value resource.Quantity) *CapabilitiesApplyConfiguration { b.HostMemory = &value return b } @@ -39,7 +39,7 @@ func (b *CapabilitiesStatusApplyConfiguration) WithHostMemory(value resource.Qua // WithHostCpus sets the HostCpus field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the HostCpus field is set to the value of the last call. -func (b *CapabilitiesStatusApplyConfiguration) WithHostCpus(value resource.Quantity) *CapabilitiesStatusApplyConfiguration { +func (b *CapabilitiesApplyConfiguration) WithHostCpus(value resource.Quantity) *CapabilitiesApplyConfiguration { b.HostCpus = &value return b } diff --git a/applyconfigurations/api/v1/domaincapabilities.go b/applyconfigurations/api/v1/domaincapabilities.go new file mode 100644 index 0000000..8985c23 --- /dev/null +++ b/applyconfigurations/api/v1/domaincapabilities.go @@ -0,0 +1,65 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +// DomainCapabilitiesApplyConfiguration represents a declarative configuration of the DomainCapabilities type for use +// with apply. +type DomainCapabilitiesApplyConfiguration struct { + Arch *string `json:"arch,omitempty"` + HypervisorType *string `json:"hypervisorType,omitempty"` + SupportedDevices []string `json:"supportedDevices,omitempty"` + SupportedCpuModes []string `json:"supportedCpuModes,omitempty"` + SupportedFeatures []string `json:"supportedFeatures,omitempty"` +} + +// DomainCapabilitiesApplyConfiguration constructs a declarative configuration of the DomainCapabilities type for use with +// apply. +func DomainCapabilities() *DomainCapabilitiesApplyConfiguration { + return &DomainCapabilitiesApplyConfiguration{} +} + +// WithArch sets the Arch field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Arch field is set to the value of the last call. +func (b *DomainCapabilitiesApplyConfiguration) WithArch(value string) *DomainCapabilitiesApplyConfiguration { + b.Arch = &value + return b +} + +// WithHypervisorType sets the HypervisorType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HypervisorType field is set to the value of the last call. +func (b *DomainCapabilitiesApplyConfiguration) WithHypervisorType(value string) *DomainCapabilitiesApplyConfiguration { + b.HypervisorType = &value + return b +} + +// WithSupportedDevices adds the given value to the SupportedDevices field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SupportedDevices field. +func (b *DomainCapabilitiesApplyConfiguration) WithSupportedDevices(values ...string) *DomainCapabilitiesApplyConfiguration { + for i := range values { + b.SupportedDevices = append(b.SupportedDevices, values[i]) + } + return b +} + +// WithSupportedCpuModes adds the given value to the SupportedCpuModes field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SupportedCpuModes field. +func (b *DomainCapabilitiesApplyConfiguration) WithSupportedCpuModes(values ...string) *DomainCapabilitiesApplyConfiguration { + for i := range values { + b.SupportedCpuModes = append(b.SupportedCpuModes, values[i]) + } + return b +} + +// WithSupportedFeatures adds the given value to the SupportedFeatures field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SupportedFeatures field. +func (b *DomainCapabilitiesApplyConfiguration) WithSupportedFeatures(values ...string) *DomainCapabilitiesApplyConfiguration { + for i := range values { + b.SupportedFeatures = append(b.SupportedFeatures, values[i]) + } + return b +} diff --git a/applyconfigurations/api/v1/hypervisorspec.go b/applyconfigurations/api/v1/hypervisorspec.go index 0aa2dc1..6fa5d4a 100644 --- a/applyconfigurations/api/v1/hypervisorspec.go +++ b/applyconfigurations/api/v1/hypervisorspec.go @@ -12,6 +12,7 @@ type HypervisorSpecApplyConfiguration struct { SkipTests *bool `json:"skipTests,omitempty"` CustomTraits []string `json:"customTraits,omitempty"` Aggregates []string `json:"aggregates,omitempty"` + AllowedProjects []string `json:"allowedProjects,omitempty"` HighAvailability *bool `json:"highAvailability,omitempty"` CreateCertManagerCertificate *bool `json:"createCertManagerCertificate,omitempty"` InstallCertificate *bool `json:"installCertificate,omitempty"` @@ -84,6 +85,16 @@ func (b *HypervisorSpecApplyConfiguration) WithAggregates(values ...string) *Hyp return b } +// WithAllowedProjects adds the given value to the AllowedProjects field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AllowedProjects field. +func (b *HypervisorSpecApplyConfiguration) WithAllowedProjects(values ...string) *HypervisorSpecApplyConfiguration { + for i := range values { + b.AllowedProjects = append(b.AllowedProjects, values[i]) + } + return b +} + // WithHighAvailability sets the HighAvailability field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the HighAvailability field is set to the value of the last call. diff --git a/applyconfigurations/api/v1/hypervisorstatus.go b/applyconfigurations/api/v1/hypervisorstatus.go index e51cdc9..2e12c8e 100644 --- a/applyconfigurations/api/v1/hypervisorstatus.go +++ b/applyconfigurations/api/v1/hypervisorstatus.go @@ -3,26 +3,30 @@ package v1 import ( + resource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/client-go/applyconfigurations/meta/v1" ) // HypervisorStatusApplyConfiguration represents a declarative configuration of the HypervisorStatus type for use // with apply. type HypervisorStatusApplyConfiguration struct { - LibVirtVersion *string `json:"libVirtVersion,omitempty"` - OperatingSystem *OperatingSystemStatusApplyConfiguration `json:"operatingSystem,omitempty"` - Update *HyperVisorUpdateStatusApplyConfiguration `json:"updateStatus,omitempty"` - Instances []InstanceApplyConfiguration `json:"instances,omitempty"` - Capabilities *CapabilitiesStatusApplyConfiguration `json:"capabilities,omitempty"` - NumInstances *int `json:"numInstances,omitempty"` - HypervisorID *string `json:"hypervisorId,omitempty"` - ServiceID *string `json:"serviceId,omitempty"` - Traits []string `json:"traits,omitempty"` - Aggregates []string `json:"aggregates,omitempty"` - InternalIP *string `json:"internalIp,omitempty"` - Evicted *bool `json:"evicted,omitempty"` - Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"` - SpecHash *string `json:"specHash,omitempty"` + LibVirtVersion *string `json:"libVirtVersion,omitempty"` + OperatingSystem *OperatingSystemStatusApplyConfiguration `json:"operatingSystem,omitempty"` + Update *HyperVisorUpdateStatusApplyConfiguration `json:"updateStatus,omitempty"` + Instances []InstanceApplyConfiguration `json:"instances,omitempty"` + Capabilities *CapabilitiesApplyConfiguration `json:"capabilities,omitempty"` + DomainCapabilities *DomainCapabilitiesApplyConfiguration `json:"domainCapabilities,omitempty"` + Capacity map[string]resource.Quantity `json:"capacity,omitempty"` + Allocation map[string]resource.Quantity `json:"allocation,omitempty"` + NumInstances *int `json:"numInstances,omitempty"` + HypervisorID *string `json:"hypervisorId,omitempty"` + ServiceID *string `json:"serviceId,omitempty"` + Traits []string `json:"traits,omitempty"` + Aggregates []string `json:"aggregates,omitempty"` + InternalIP *string `json:"internalIp,omitempty"` + Evicted *bool `json:"evicted,omitempty"` + Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"` + SpecHash *string `json:"specHash,omitempty"` } // HypervisorStatusApplyConfiguration constructs a declarative configuration of the HypervisorStatus type for use with @@ -71,11 +75,47 @@ func (b *HypervisorStatusApplyConfiguration) WithInstances(values ...*InstanceAp // WithCapabilities sets the Capabilities field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Capabilities field is set to the value of the last call. -func (b *HypervisorStatusApplyConfiguration) WithCapabilities(value *CapabilitiesStatusApplyConfiguration) *HypervisorStatusApplyConfiguration { +func (b *HypervisorStatusApplyConfiguration) WithCapabilities(value *CapabilitiesApplyConfiguration) *HypervisorStatusApplyConfiguration { b.Capabilities = value return b } +// WithDomainCapabilities sets the DomainCapabilities field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DomainCapabilities field is set to the value of the last call. +func (b *HypervisorStatusApplyConfiguration) WithDomainCapabilities(value *DomainCapabilitiesApplyConfiguration) *HypervisorStatusApplyConfiguration { + b.DomainCapabilities = value + return b +} + +// WithCapacity puts the entries into the Capacity field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Capacity field, +// overwriting an existing map entries in Capacity field with the same key. +func (b *HypervisorStatusApplyConfiguration) WithCapacity(entries map[string]resource.Quantity) *HypervisorStatusApplyConfiguration { + if b.Capacity == nil && len(entries) > 0 { + b.Capacity = make(map[string]resource.Quantity, len(entries)) + } + for k, v := range entries { + b.Capacity[k] = v + } + return b +} + +// WithAllocation puts the entries into the Allocation field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Allocation field, +// overwriting an existing map entries in Allocation field with the same key. +func (b *HypervisorStatusApplyConfiguration) WithAllocation(entries map[string]resource.Quantity) *HypervisorStatusApplyConfiguration { + if b.Allocation == nil && len(entries) > 0 { + b.Allocation = make(map[string]resource.Quantity, len(entries)) + } + for k, v := range entries { + b.Allocation[k] = v + } + return b +} + // WithNumInstances sets the NumInstances field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the NumInstances field is set to the value of the last call. diff --git a/applyconfigurations/utils.go b/applyconfigurations/utils.go index 3ecf8f6..fcaf808 100644 --- a/applyconfigurations/utils.go +++ b/applyconfigurations/utils.go @@ -8,7 +8,7 @@ import ( internal "github.com/cobaltcore-dev/openstack-hypervisor-operator/applyconfigurations/internal" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" - managedfields "k8s.io/apimachinery/pkg/util/managedfields" + "k8s.io/apimachinery/pkg/util/managedfields" ) // ForKind returns an apply configuration type for the given GroupVersionKind, or nil if no @@ -16,8 +16,8 @@ import ( func ForKind(kind schema.GroupVersionKind) interface{} { switch kind { // Group=kvm.cloud.sap, Version=v1 - case v1.SchemeGroupVersion.WithKind("CapabilitiesStatus"): - return &apiv1.CapabilitiesStatusApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("Capabilities"): + return &apiv1.CapabilitiesApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("Eviction"): return &apiv1.EvictionApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("EvictionSpec"): diff --git a/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml b/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml index fe36d98..1708302 100644 --- a/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml +++ b/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml @@ -109,6 +109,15 @@ spec: items: type: string type: array + allowedProjects: + default: [] + description: |- + AllowedProjects defines which openstack projects are allowed to schedule + instances on this hypervisor. The values of this list should be project + uuids. If left empty, all projects are allowed. + items: + type: string + type: array createCertManagerCertificate: default: false description: |- @@ -166,6 +175,7 @@ spec: type: string required: - aggregates + - allowedProjects - createCertManagerCertificate - customTraits - evacuateOnReboot @@ -183,8 +193,20 @@ spec: items: type: string type: array + allocation: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + default: {} + description: |- + Auto-discovered capacity currently allocated by instances + on the hypervisor. Note that this does not include reserved capacity. + type: object capabilities: - description: The capabilities of the hypervisors as reported by libvirt. + description: Auto-discovered capabilities as reported by libvirt. properties: cpuArch: default: unknown @@ -207,6 +229,19 @@ spec: pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + default: {} + description: |- + Auto-discovered capacity available in total on the hypervisor. + The remaining physical capacity can be calculated + as Capacity - Allocation. + type: object conditions: description: Represents the Hypervisor node conditions. items: @@ -264,6 +299,40 @@ spec: - type type: object type: array + domainCapabilities: + description: |- + Auto-discovered domain capabilities relevant to check if a VM + can be scheduled on the hypervisor. + properties: + arch: + default: unknown + description: The available domain cpu architecture. + type: string + hypervisorType: + default: unknown + description: The supported type of virtualization for domains, + such as "ch". + type: string + supportedCpuModes: + default: [] + description: Supported cpu modes for domains, such as "host-passthrough". + items: + type: string + type: array + supportedDevices: + default: [] + description: Supported devices for domains, such as "video". + items: + type: string + type: array + supportedFeatures: + default: [] + description: Supported features for domains, such as "sev" or + "sgx". + items: + type: string + type: array + type: object evicted: description: Evicted indicates whether the hypervisor is evicted. (no instances left with active maintenance mode) diff --git a/config/crd/bases/kvm.cloud.sap_hypervisors.yaml b/config/crd/bases/kvm.cloud.sap_hypervisors.yaml index 311ba15..0069f2b 100644 --- a/config/crd/bases/kvm.cloud.sap_hypervisors.yaml +++ b/config/crd/bases/kvm.cloud.sap_hypervisors.yaml @@ -110,6 +110,15 @@ spec: items: type: string type: array + allowedProjects: + default: [] + description: |- + AllowedProjects defines which openstack projects are allowed to schedule + instances on this hypervisor. The values of this list should be project + uuids. If left empty, all projects are allowed. + items: + type: string + type: array createCertManagerCertificate: default: false description: |- @@ -167,6 +176,7 @@ spec: type: string required: - aggregates + - allowedProjects - createCertManagerCertificate - customTraits - evacuateOnReboot @@ -184,8 +194,20 @@ spec: items: type: string type: array + allocation: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + default: {} + description: |- + Auto-discovered capacity currently allocated by instances + on the hypervisor. Note that this does not include reserved capacity. + type: object capabilities: - description: The capabilities of the hypervisors as reported by libvirt. + description: Auto-discovered capabilities as reported by libvirt. properties: cpuArch: default: unknown @@ -208,6 +230,19 @@ spec: pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + default: {} + description: |- + Auto-discovered capacity available in total on the hypervisor. + The remaining physical capacity can be calculated + as Capacity - Allocation. + type: object conditions: description: Represents the Hypervisor node conditions. items: @@ -265,6 +300,40 @@ spec: - type type: object type: array + domainCapabilities: + description: |- + Auto-discovered domain capabilities relevant to check if a VM + can be scheduled on the hypervisor. + properties: + arch: + default: unknown + description: The available domain cpu architecture. + type: string + hypervisorType: + default: unknown + description: The supported type of virtualization for domains, + such as "ch". + type: string + supportedCpuModes: + default: [] + description: Supported cpu modes for domains, such as "host-passthrough". + items: + type: string + type: array + supportedDevices: + default: [] + description: Supported devices for domains, such as "video". + items: + type: string + type: array + supportedFeatures: + default: [] + description: Supported features for domains, such as "sev" or + "sgx". + items: + type: string + type: array + type: object evicted: description: Evicted indicates whether the hypervisor is evicted. (no instances left with active maintenance mode) From 52d9308090a6c6680c4710b256c51633ca41c5f8 Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Mon, 29 Dec 2025 12:57:49 +0100 Subject: [PATCH 2/3] Kubebuilder annotations --- api/v1/hypervisor_types.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/api/v1/hypervisor_types.go b/api/v1/hypervisor_types.go index b6bf608..91d15a3 100644 --- a/api/v1/hypervisor_types.go +++ b/api/v1/hypervisor_types.go @@ -210,20 +210,20 @@ type Capabilities struct { // Domain capabilities of the hypervisor as reported by libvirt. // These details are relevant to check if a VM can be scheduled on the hypervisor. type DomainCapabilities struct { - // +kubebuilder:default:=unknown // The available domain cpu architecture. - Arch string `json:"arch,omitempty"` // +kubebuilder:default:=unknown + Arch string `json:"arch,omitempty"` // The supported type of virtualization for domains, such as "ch". + // +kubebuilder:default:=unknown HypervisorType string `json:"hypervisorType,omitempty"` - // +kubebuilder:default:={} // Supported devices for domains, such as "video". - SupportedDevices []string `json:"supportedDevices,omitempty"` // +kubebuilder:default:={} + SupportedDevices []string `json:"supportedDevices,omitempty"` // Supported cpu modes for domains, such as "host-passthrough". - SupportedCpuModes []string `json:"supportedCpuModes,omitempty"` // +kubebuilder:default:={} + SupportedCpuModes []string `json:"supportedCpuModes,omitempty"` // Supported features for domains, such as "sev" or "sgx". + // +kubebuilder:default:={} SupportedFeatures []string `json:"supportedFeatures,omitempty"` } @@ -243,21 +243,23 @@ type HypervisorStatus struct { Instances []Instance `json:"instances,omitempty"` // Auto-discovered capabilities as reported by libvirt. + // +kubebuilder:validation:Optional Capabilities Capabilities `json:"capabilities"` // Auto-discovered domain capabilities relevant to check if a VM // can be scheduled on the hypervisor. + // +kubebuilder:validation:Optional DomainCapabilities DomainCapabilities `json:"domainCapabilities"` - // +kubebuilder:default:={} // Auto-discovered capacity available in total on the hypervisor. // The remaining physical capacity can be calculated // as Capacity - Allocation. + // +kubebuilder:default:={} Capacity map[string]resource.Quantity `json:"capacity,omitempty"` - // +kubebuilder:default:={} // Auto-discovered capacity currently allocated by instances // on the hypervisor. Note that this does not include reserved capacity. + // +kubebuilder:default:={} Allocation map[string]resource.Quantity `json:"allocation,omitempty"` // +kubebuilder:default:=0 From 37950dd7ff29f7c119ac0af9bb8c955270cf6e4c Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Tue, 30 Dec 2025 11:50:55 +0100 Subject: [PATCH 3/3] Add domain information list with numa cell allocation --- api/v1/hypervisor_types.go | 80 +++++++++-- api/v1/zz_generated.deepcopy.go | 78 +++++++++-- applyconfigurations/api/v1/capabilities.go | 20 ++- applyconfigurations/api/v1/cell.go | 42 ++++++ applyconfigurations/api/v1/domaininfo.go | 73 ++++++++++ .../api/v1/hypervisorstatus.go | 35 ++--- .../crds/hypervisor-crd.yaml | 129 ++++++++++++++---- .../crd/bases/kvm.cloud.sap_hypervisors.yaml | 129 ++++++++++++++---- 8 files changed, 476 insertions(+), 110 deletions(-) create mode 100644 applyconfigurations/api/v1/cell.go create mode 100644 applyconfigurations/api/v1/domaininfo.go diff --git a/api/v1/hypervisor_types.go b/api/v1/hypervisor_types.go index 91d15a3..a2d1a0b 100644 --- a/api/v1/hypervisor_types.go +++ b/api/v1/hypervisor_types.go @@ -196,6 +196,15 @@ type OperatingSystemStatus struct { GardenLinuxFeatures []string `json:"gardenLinuxFeatures,omitempty"` } +// Cell represents a single cell of the host's topology. +type Cell struct { + // ID is the identifier of the cell. + ID int `json:"id"` + // The cell's capacity, such as the number of cpus, memory, and hugepages. + // +kubebuilder:default:={} + Capacity map[string]resource.Quantity `json:"capacity,omitempty"` +} + // Capabilities of the hypervisor as reported by libvirt. type Capabilities struct { // +kubebuilder:default:=unknown @@ -205,6 +214,9 @@ type Capabilities struct { HostMemory resource.Quantity `json:"memory,omitempty"` // Total host cpus available as a sum of cpus over all numa cells. HostCpus resource.Quantity `json:"cpus,omitempty"` + // The host's cell topology (a.k.a. numa cells). + // +kubebuilder:validation:Optional + HostTopology []Cell `json:"hostTopology,omitempty"` } // Domain capabilities of the hypervisor as reported by libvirt. @@ -213,20 +225,73 @@ type DomainCapabilities struct { // The available domain cpu architecture. // +kubebuilder:default:=unknown Arch string `json:"arch,omitempty"` + // The supported type of virtualization for domains, such as "ch". // +kubebuilder:default:=unknown HypervisorType string `json:"hypervisorType,omitempty"` - // Supported devices for domains, such as "video". + + // Supported devices for domains. + // + // The format of this list is the device type, and if specified, a specific + // model. For example, the take the following xml domain device definition: + // + // + // + // The corresponding entries in this list would be "video" and "video/nvidia". + // // +kubebuilder:default:={} SupportedDevices []string `json:"supportedDevices,omitempty"` - // Supported cpu modes for domains, such as "host-passthrough". + + // Supported cpu modes for domains. + // + // The format of this list is cpu mode, and if specified, a specific + // submode. For example, the take the following xml domain cpu definition: + // + // + // + // + // + // The corresponding entries in this list would be "host-passthrough" and + // "host-passthrough/migratable". + // // +kubebuilder:default:={} SupportedCpuModes []string `json:"supportedCpuModes,omitempty"` + // Supported features for domains, such as "sev" or "sgx". + // + // This is a flat list of supported features, meaning the following xml: + // + // + // + // + // + // + // Would correspond to the entries "sev" and "sgx" in this list. + // // +kubebuilder:default:={} SupportedFeatures []string `json:"supportedFeatures,omitempty"` } +// Domain information as reported by libvirt. +type DomainInfo struct { + // Name is the name of the domain. + Name string `json:"name"` + // UUID is the uuid of the domain. + UUID string `json:"uuid"` + // Resource allocation of the domain. + // This can include memory, cpu, and other. + Allocation map[string]resource.Quantity `json:"allocation,omitempty"` + // The memory numa cells of the domain, derived from the numa tune. + MemoryCells []int `json:"memoryCells,omitempty"` + // The cpu numa cells of the domain, derived from the numa information + // of the cpu mode. + CpuCells []int `json:"cpuCells,omitempty"` +} + // HypervisorStatus defines the observed state of Hypervisor type HypervisorStatus struct { // +kubebuilder:default:=unknown @@ -251,16 +316,9 @@ type HypervisorStatus struct { // +kubebuilder:validation:Optional DomainCapabilities DomainCapabilities `json:"domainCapabilities"` - // Auto-discovered capacity available in total on the hypervisor. - // The remaining physical capacity can be calculated - // as Capacity - Allocation. + // Auto-discovered domain infos as reported by libvirt (dumpxml). // +kubebuilder:default:={} - Capacity map[string]resource.Quantity `json:"capacity,omitempty"` - - // Auto-discovered capacity currently allocated by instances - // on the hypervisor. Note that this does not include reserved capacity. - // +kubebuilder:default:={} - Allocation map[string]resource.Quantity `json:"allocation,omitempty"` + DomainInfos []DomainInfo `json:"domainInfos,omitempty"` // +kubebuilder:default:=0 // Represent the num of instances diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 7336a32..d41ee12 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -32,6 +32,13 @@ func (in *Capabilities) DeepCopyInto(out *Capabilities) { *out = *in out.HostMemory = in.HostMemory.DeepCopy() out.HostCpus = in.HostCpus.DeepCopy() + if in.HostTopology != nil { + in, out := &in.HostTopology, &out.HostTopology + *out = make([]Cell, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Capabilities. @@ -44,6 +51,28 @@ func (in *Capabilities) DeepCopy() *Capabilities { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Cell) DeepCopyInto(out *Cell) { + *out = *in + if in.Capacity != nil { + in, out := &in.Capacity, &out.Capacity + *out = make(map[string]resource.Quantity, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cell. +func (in *Cell) DeepCopy() *Cell { + if in == nil { + return nil + } + out := new(Cell) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DomainCapabilities) DeepCopyInto(out *DomainCapabilities) { *out = *in @@ -74,6 +103,38 @@ func (in *DomainCapabilities) DeepCopy() *DomainCapabilities { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainInfo) DeepCopyInto(out *DomainInfo) { + *out = *in + if in.Allocation != nil { + in, out := &in.Allocation, &out.Allocation + *out = make(map[string]resource.Quantity, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + if in.MemoryCells != nil { + in, out := &in.MemoryCells, &out.MemoryCells + *out = make([]int, len(*in)) + copy(*out, *in) + } + if in.CpuCells != nil { + in, out := &in.CpuCells, &out.CpuCells + *out = make([]int, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainInfo. +func (in *DomainInfo) DeepCopy() *DomainInfo { + if in == nil { + return nil + } + out := new(DomainInfo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Eviction) DeepCopyInto(out *Eviction) { *out = *in @@ -291,18 +352,11 @@ func (in *HypervisorStatus) DeepCopyInto(out *HypervisorStatus) { } in.Capabilities.DeepCopyInto(&out.Capabilities) in.DomainCapabilities.DeepCopyInto(&out.DomainCapabilities) - if in.Capacity != nil { - in, out := &in.Capacity, &out.Capacity - *out = make(map[string]resource.Quantity, len(*in)) - for key, val := range *in { - (*out)[key] = val.DeepCopy() - } - } - if in.Allocation != nil { - in, out := &in.Allocation, &out.Allocation - *out = make(map[string]resource.Quantity, len(*in)) - for key, val := range *in { - (*out)[key] = val.DeepCopy() + if in.DomainInfos != nil { + in, out := &in.DomainInfos, &out.DomainInfos + *out = make([]DomainInfo, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.Traits != nil { diff --git a/applyconfigurations/api/v1/capabilities.go b/applyconfigurations/api/v1/capabilities.go index 7b87dd8..57d6da5 100644 --- a/applyconfigurations/api/v1/capabilities.go +++ b/applyconfigurations/api/v1/capabilities.go @@ -9,9 +9,10 @@ import ( // CapabilitiesApplyConfiguration represents a declarative configuration of the Capabilities type for use // with apply. type CapabilitiesApplyConfiguration struct { - HostCpuArch *string `json:"cpuArch,omitempty"` - HostMemory *resource.Quantity `json:"memory,omitempty"` - HostCpus *resource.Quantity `json:"cpus,omitempty"` + HostCpuArch *string `json:"cpuArch,omitempty"` + HostMemory *resource.Quantity `json:"memory,omitempty"` + HostCpus *resource.Quantity `json:"cpus,omitempty"` + HostTopology []CellApplyConfiguration `json:"hostTopology,omitempty"` } // CapabilitiesApplyConfiguration constructs a declarative configuration of the Capabilities type for use with @@ -43,3 +44,16 @@ func (b *CapabilitiesApplyConfiguration) WithHostCpus(value resource.Quantity) * b.HostCpus = &value return b } + +// WithHostTopology adds the given value to the HostTopology field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the HostTopology field. +func (b *CapabilitiesApplyConfiguration) WithHostTopology(values ...*CellApplyConfiguration) *CapabilitiesApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithHostTopology") + } + b.HostTopology = append(b.HostTopology, *values[i]) + } + return b +} diff --git a/applyconfigurations/api/v1/cell.go b/applyconfigurations/api/v1/cell.go new file mode 100644 index 0000000..e749966 --- /dev/null +++ b/applyconfigurations/api/v1/cell.go @@ -0,0 +1,42 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" +) + +// CellApplyConfiguration represents a declarative configuration of the Cell type for use +// with apply. +type CellApplyConfiguration struct { + ID *int `json:"id,omitempty"` + Capacity map[string]resource.Quantity `json:"capacity,omitempty"` +} + +// CellApplyConfiguration constructs a declarative configuration of the Cell type for use with +// apply. +func Cell() *CellApplyConfiguration { + return &CellApplyConfiguration{} +} + +// WithID sets the ID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ID field is set to the value of the last call. +func (b *CellApplyConfiguration) WithID(value int) *CellApplyConfiguration { + b.ID = &value + return b +} + +// WithCapacity puts the entries into the Capacity field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Capacity field, +// overwriting an existing map entries in Capacity field with the same key. +func (b *CellApplyConfiguration) WithCapacity(entries map[string]resource.Quantity) *CellApplyConfiguration { + if b.Capacity == nil && len(entries) > 0 { + b.Capacity = make(map[string]resource.Quantity, len(entries)) + } + for k, v := range entries { + b.Capacity[k] = v + } + return b +} diff --git a/applyconfigurations/api/v1/domaininfo.go b/applyconfigurations/api/v1/domaininfo.go new file mode 100644 index 0000000..b521f81 --- /dev/null +++ b/applyconfigurations/api/v1/domaininfo.go @@ -0,0 +1,73 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + resource "k8s.io/apimachinery/pkg/api/resource" +) + +// DomainInfoApplyConfiguration represents a declarative configuration of the DomainInfo type for use +// with apply. +type DomainInfoApplyConfiguration struct { + Name *string `json:"name,omitempty"` + UUID *string `json:"uuid,omitempty"` + Allocation map[string]resource.Quantity `json:"allocation,omitempty"` + MemoryCells []int `json:"memoryCells,omitempty"` + CpuCells []int `json:"cpuCells,omitempty"` +} + +// DomainInfoApplyConfiguration constructs a declarative configuration of the DomainInfo type for use with +// apply. +func DomainInfo() *DomainInfoApplyConfiguration { + return &DomainInfoApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *DomainInfoApplyConfiguration) WithName(value string) *DomainInfoApplyConfiguration { + b.Name = &value + return b +} + +// WithUUID sets the UUID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UUID field is set to the value of the last call. +func (b *DomainInfoApplyConfiguration) WithUUID(value string) *DomainInfoApplyConfiguration { + b.UUID = &value + return b +} + +// WithAllocation puts the entries into the Allocation field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Allocation field, +// overwriting an existing map entries in Allocation field with the same key. +func (b *DomainInfoApplyConfiguration) WithAllocation(entries map[string]resource.Quantity) *DomainInfoApplyConfiguration { + if b.Allocation == nil && len(entries) > 0 { + b.Allocation = make(map[string]resource.Quantity, len(entries)) + } + for k, v := range entries { + b.Allocation[k] = v + } + return b +} + +// WithMemoryCells adds the given value to the MemoryCells field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the MemoryCells field. +func (b *DomainInfoApplyConfiguration) WithMemoryCells(values ...int) *DomainInfoApplyConfiguration { + for i := range values { + b.MemoryCells = append(b.MemoryCells, values[i]) + } + return b +} + +// WithCpuCells adds the given value to the CpuCells field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the CpuCells field. +func (b *DomainInfoApplyConfiguration) WithCpuCells(values ...int) *DomainInfoApplyConfiguration { + for i := range values { + b.CpuCells = append(b.CpuCells, values[i]) + } + return b +} diff --git a/applyconfigurations/api/v1/hypervisorstatus.go b/applyconfigurations/api/v1/hypervisorstatus.go index 2e12c8e..a82dcf0 100644 --- a/applyconfigurations/api/v1/hypervisorstatus.go +++ b/applyconfigurations/api/v1/hypervisorstatus.go @@ -3,7 +3,6 @@ package v1 import ( - resource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/client-go/applyconfigurations/meta/v1" ) @@ -16,8 +15,7 @@ type HypervisorStatusApplyConfiguration struct { Instances []InstanceApplyConfiguration `json:"instances,omitempty"` Capabilities *CapabilitiesApplyConfiguration `json:"capabilities,omitempty"` DomainCapabilities *DomainCapabilitiesApplyConfiguration `json:"domainCapabilities,omitempty"` - Capacity map[string]resource.Quantity `json:"capacity,omitempty"` - Allocation map[string]resource.Quantity `json:"allocation,omitempty"` + DomainInfos []DomainInfoApplyConfiguration `json:"domainInfos,omitempty"` NumInstances *int `json:"numInstances,omitempty"` HypervisorID *string `json:"hypervisorId,omitempty"` ServiceID *string `json:"serviceId,omitempty"` @@ -88,30 +86,15 @@ func (b *HypervisorStatusApplyConfiguration) WithDomainCapabilities(value *Domai return b } -// WithCapacity puts the entries into the Capacity field in the declarative configuration +// WithDomainInfos adds the given value to the DomainInfos field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, the entries provided by each call will be put on the Capacity field, -// overwriting an existing map entries in Capacity field with the same key. -func (b *HypervisorStatusApplyConfiguration) WithCapacity(entries map[string]resource.Quantity) *HypervisorStatusApplyConfiguration { - if b.Capacity == nil && len(entries) > 0 { - b.Capacity = make(map[string]resource.Quantity, len(entries)) - } - for k, v := range entries { - b.Capacity[k] = v - } - return b -} - -// WithAllocation puts the entries into the Allocation field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, the entries provided by each call will be put on the Allocation field, -// overwriting an existing map entries in Allocation field with the same key. -func (b *HypervisorStatusApplyConfiguration) WithAllocation(entries map[string]resource.Quantity) *HypervisorStatusApplyConfiguration { - if b.Allocation == nil && len(entries) > 0 { - b.Allocation = make(map[string]resource.Quantity, len(entries)) - } - for k, v := range entries { - b.Allocation[k] = v +// If called multiple times, values provided by each call will be appended to the DomainInfos field. +func (b *HypervisorStatusApplyConfiguration) WithDomainInfos(values ...*DomainInfoApplyConfiguration) *HypervisorStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithDomainInfos") + } + b.DomainInfos = append(b.DomainInfos, *values[i]) } return b } diff --git a/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml b/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml index 1708302..f8b5bf4 100644 --- a/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml +++ b/charts/openstack-hypervisor-operator/crds/hypervisor-crd.yaml @@ -193,18 +193,6 @@ spec: items: type: string type: array - allocation: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - default: {} - description: |- - Auto-discovered capacity currently allocated by instances - on the hypervisor. Note that this does not include reserved capacity. - type: object capabilities: description: Auto-discovered capabilities as reported by libvirt. properties: @@ -220,6 +208,29 @@ spec: numa cells. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + hostTopology: + description: The host's cell topology (a.k.a. numa cells). + items: + description: Cell represents a single cell of the host's topology. + properties: + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + default: {} + description: The cell's capacity, such as the number of + cpus, memory, and hugepages. + type: object + id: + description: ID is the identifier of the cell. + type: integer + required: + - id + type: object + type: array memory: anyOf: - type: integer @@ -229,19 +240,6 @@ spec: pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - default: {} - description: |- - Auto-discovered capacity available in total on the hypervisor. - The remaining physical capacity can be calculated - as Capacity - Allocation. - type: object conditions: description: Represents the Hypervisor node conditions. items: @@ -315,24 +313,97 @@ spec: type: string supportedCpuModes: default: [] - description: Supported cpu modes for domains, such as "host-passthrough". + description: |- + Supported cpu modes for domains. + + The format of this list is cpu mode, and if specified, a specific + submode. For example, the take the following xml domain cpu definition: + + + + + + The corresponding entries in this list would be "host-passthrough" and + "host-passthrough/migratable". items: type: string type: array supportedDevices: default: [] - description: Supported devices for domains, such as "video". + description: |- + Supported devices for domains. + + The format of this list is the device type, and if specified, a specific + model. For example, the take the following xml domain device definition: + + + + The corresponding entries in this list would be "video" and "video/nvidia". items: type: string type: array supportedFeatures: default: [] - description: Supported features for domains, such as "sev" or - "sgx". + description: |- + Supported features for domains, such as "sev" or "sgx". + + This is a flat list of supported features, meaning the following xml: + + + + + + + Would correspond to the entries "sev" and "sgx" in this list. items: type: string type: array type: object + domainInfos: + default: [] + description: Auto-discovered domain infos as reported by libvirt (dumpxml). + items: + description: Domain information as reported by libvirt. + properties: + allocation: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Resource allocation of the domain. + This can include memory, cpu, and other. + type: object + cpuCells: + description: |- + The cpu numa cells of the domain, derived from the numa information + of the cpu mode. + items: + type: integer + type: array + memoryCells: + description: The memory numa cells of the domain, derived from + the numa tune. + items: + type: integer + type: array + name: + description: Name is the name of the domain. + type: string + uuid: + description: UUID is the uuid of the domain. + type: string + required: + - name + - uuid + type: object + type: array evicted: description: Evicted indicates whether the hypervisor is evicted. (no instances left with active maintenance mode) diff --git a/config/crd/bases/kvm.cloud.sap_hypervisors.yaml b/config/crd/bases/kvm.cloud.sap_hypervisors.yaml index 0069f2b..056cfcf 100644 --- a/config/crd/bases/kvm.cloud.sap_hypervisors.yaml +++ b/config/crd/bases/kvm.cloud.sap_hypervisors.yaml @@ -194,18 +194,6 @@ spec: items: type: string type: array - allocation: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - default: {} - description: |- - Auto-discovered capacity currently allocated by instances - on the hypervisor. Note that this does not include reserved capacity. - type: object capabilities: description: Auto-discovered capabilities as reported by libvirt. properties: @@ -221,6 +209,29 @@ spec: numa cells. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + hostTopology: + description: The host's cell topology (a.k.a. numa cells). + items: + description: Cell represents a single cell of the host's topology. + properties: + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + default: {} + description: The cell's capacity, such as the number of + cpus, memory, and hugepages. + type: object + id: + description: ID is the identifier of the cell. + type: integer + required: + - id + type: object + type: array memory: anyOf: - type: integer @@ -230,19 +241,6 @@ spec: pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - default: {} - description: |- - Auto-discovered capacity available in total on the hypervisor. - The remaining physical capacity can be calculated - as Capacity - Allocation. - type: object conditions: description: Represents the Hypervisor node conditions. items: @@ -316,24 +314,97 @@ spec: type: string supportedCpuModes: default: [] - description: Supported cpu modes for domains, such as "host-passthrough". + description: |- + Supported cpu modes for domains. + + The format of this list is cpu mode, and if specified, a specific + submode. For example, the take the following xml domain cpu definition: + + + + + + The corresponding entries in this list would be "host-passthrough" and + "host-passthrough/migratable". items: type: string type: array supportedDevices: default: [] - description: Supported devices for domains, such as "video". + description: |- + Supported devices for domains. + + The format of this list is the device type, and if specified, a specific + model. For example, the take the following xml domain device definition: + + + + The corresponding entries in this list would be "video" and "video/nvidia". items: type: string type: array supportedFeatures: default: [] - description: Supported features for domains, such as "sev" or - "sgx". + description: |- + Supported features for domains, such as "sev" or "sgx". + + This is a flat list of supported features, meaning the following xml: + + + + + + + Would correspond to the entries "sev" and "sgx" in this list. items: type: string type: array type: object + domainInfos: + default: [] + description: Auto-discovered domain infos as reported by libvirt (dumpxml). + items: + description: Domain information as reported by libvirt. + properties: + allocation: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Resource allocation of the domain. + This can include memory, cpu, and other. + type: object + cpuCells: + description: |- + The cpu numa cells of the domain, derived from the numa information + of the cpu mode. + items: + type: integer + type: array + memoryCells: + description: The memory numa cells of the domain, derived from + the numa tune. + items: + type: integer + type: array + name: + description: Name is the name of the domain. + type: string + uuid: + description: UUID is the uuid of the domain. + type: string + required: + - name + - uuid + type: object + type: array evicted: description: Evicted indicates whether the hypervisor is evicted. (no instances left with active maintenance mode)