Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
723327c
wip
loktev-d Oct 6, 2025
58498a2
wip
loktev-d Oct 20, 2025
ee98848
Merge branch 'main' into feat/vmclass/core-fractions-percentage
loktev-d Oct 21, 2025
0cab994
wip
loktev-d Oct 21, 2025
6e978ef
remove formating changes
loktev-d Oct 21, 2025
b033f17
wip
loktev-d Oct 21, 2025
28a0f91
wip
loktev-d Oct 21, 2025
1c477ad
wip
loktev-d Oct 21, 2025
774de40
wip
loktev-d Oct 21, 2025
8e15550
Apply suggestion from @z9r5
loktev-d Oct 22, 2025
5458ff6
Revert "Apply suggestion from @z9r5"
loktev-d Oct 22, 2025
b71d209
wip
loktev-d Oct 22, 2025
ccd15e2
wip
loktev-d Oct 22, 2025
fa19dff
wip
loktev-d Oct 22, 2025
b07e3c5
wip
loktev-d Oct 22, 2025
7dcb2b6
wip
loktev-d Oct 22, 2025
5c68812
Merge branch 'main' into feat/vmclass/core-fractions-percentage
loktev-d Oct 22, 2025
eff1fa1
wip
loktev-d Oct 22, 2025
35bdc0d
wip
loktev-d Oct 22, 2025
8293305
wip
loktev-d Oct 22, 2025
918fad1
wip
loktev-d Oct 22, 2025
da2a944
wip
loktev-d Oct 22, 2025
56cff28
wip
loktev-d Oct 22, 2025
2e16faa
wip
loktev-d Oct 23, 2025
9471568
fix conversion path
loktev-d Oct 23, 2025
d75022e
fix conversion path
loktev-d Oct 23, 2025
2c32efd
wip
loktev-d Oct 23, 2025
a83c386
wip
loktev-d Oct 23, 2025
3f5f025
fix cli mod
loktev-d Oct 23, 2025
824e6f4
fix e2e
loktev-d Oct 23, 2025
8a59dbf
fix e2e
loktev-d Oct 24, 2025
e0c3094
Merge branch 'main' into feat/vmclass/core-fractions-percentage
loktev-d Oct 24, 2025
18d5aa7
fix e2e
loktev-d Oct 24, 2025
f7de531
fix conflict
loktev-d Oct 27, 2025
afd04f3
fix hook
loktev-d Oct 27, 2025
8770fdf
fix hook
loktev-d Oct 27, 2025
8706c6c
wip
loktev-d Oct 27, 2025
90fa31d
wip
loktev-d Oct 27, 2025
a23c107
wip
loktev-d Oct 27, 2025
b789649
wip
loktev-d Oct 27, 2025
09f092a
wip
loktev-d Oct 27, 2025
3a3a22f
wip
loktev-d Oct 27, 2025
e8c8bfe
wip
loktev-d Oct 27, 2025
0bf3422
wip
loktev-d Oct 27, 2025
95551fe
wip
loktev-d Oct 28, 2025
5c6f3a9
revert vmclass generic
loktev-d Oct 28, 2025
5baf89c
wip
loktev-d Oct 28, 2025
f21d532
wip
loktev-d Oct 29, 2025
4fe37f5
wip
loktev-d Oct 29, 2025
b30271b
revert generic vmclass
loktev-d Oct 29, 2025
d0ce0b9
Merge branch 'main' into feat/vmclass/core-fractions-percentage
loktev-d Oct 29, 2025
76c399c
wip
loktev-d Oct 29, 2025
b4f781c
remove hook, patch on startup
loktev-d Oct 29, 2025
5f9e6ed
wip
loktev-d Oct 29, 2025
96e3c8d
fix rbac
loktev-d Oct 29, 2025
6573c6f
wip
loktev-d Oct 29, 2025
3855e46
fix discovery
loktev-d Oct 29, 2025
25ccd68
fix validation, add tests
loktev-d Oct 29, 2025
a4e9967
wip
loktev-d Oct 29, 2025
a1f50cb
wip
loktev-d Oct 29, 2025
2bbc175
go mod tidy
loktev-d Oct 30, 2025
38fb418
Merge branch 'main' into feat/vmclass/core-fractions-percentage
loktev-d Oct 30, 2025
b015808
fix transformers
loktev-d Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions api/core/v1alpha3/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true

// Package v1alpha3 is the v1alpha3 version of the API.
// +groupName=virtualization.deckhouse.io
package v1alpha3
64 changes: 64 additions & 0 deletions api/core/v1alpha3/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha3

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"

"github.com/deckhouse/virtualization/api/core"
)

const Version = "v1alpha3"

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: core.GroupName, Version: Version}

// VirtualMachineClassGVK is group version kind for VirtualMachineClass
var VirtualMachineClassGVK = schema.GroupVersionKind{Group: SchemeGroupVersion.Group, Version: SchemeGroupVersion.Version, Kind: VirtualMachineClassKind}

// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

func GroupVersionResource(resource string) schema.GroupVersionResource {
return SchemeGroupVersion.WithResource(resource)
}

var (
// SchemeBuilder tbd
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme tbd
AddToScheme = SchemeBuilder.AddToScheme
)

// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&VirtualMachineClass{},
&VirtualMachineClassList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
244 changes: 244 additions & 0 deletions api/core/v1alpha3/virtual_machine_class.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// +kubebuilder:object:generate=true
// +groupName=virtualization.deckhouse.io
package v1alpha3

import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
VirtualMachineClassKind = "VirtualMachineClass"
VirtualMachineClassResource = "virtualmachineclasses"
)

// VirtualMachineClass resource describes CPU requirements, node placement, and sizing policy for VM resources.
// A resource cannot be deleted as long as it is used in one of the VMs.
//
// +kubebuilder:object:root=true
// +kubebuilder:metadata:labels={heritage=deckhouse,module=virtualization,backup.deckhouse.io/cluster-config=true}
// +kubebuilder:subresource:status
// +kubebuilder:resource:categories={virtualization-cluster},scope=Cluster,shortName={vmc,vmclass},singular=virtualmachineclass
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="VirtualMachineClass phase."
// +kubebuilder:printcolumn:name="IsDefault",type="string",JSONPath=".metadata.annotations.virtualmachineclass\\.virtualization\\.deckhouse\\.io\\/is-default-class",description="Default class for virtual machines without specified class."
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time of resource creation."
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type VirtualMachineClass struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec VirtualMachineClassSpec `json:"spec"`
Status VirtualMachineClassStatus `json:"status,omitempty"`
}

// VirtualMachineClassList contains a list of VirtualMachineClasses.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type VirtualMachineClassList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`

// Items provides a list of VirtualMachineClasses.
Items []VirtualMachineClass `json:"items"`
}

type VirtualMachineClassSpec struct {
NodeSelector NodeSelector `json:"nodeSelector,omitempty"`
// Tolerations are the same as `spec.tolerations` for [pods](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/).
// These tolerations will be merged with the tolerations specified in the VirtualMachine resource. VirtualMachine tolerations have a higher priority.
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// +kubebuilder:validation:Required
CPU CPU `json:"cpu"`
SizingPolicies []SizingPolicy `json:"sizingPolicies,omitempty"`
}

// NodeSelector defines the nodes targeted for VM scheduling.
type NodeSelector struct {
// A map of {key,value} pairs.
// A single {key,value} pair in the matchLabels map is equivalent to an element of matchExpressions whose key field is "key", operator is "In", and the value array contains only "value".
// The requirements are ANDed.
MatchLabels map[string]string `json:"matchLabels,omitempty"`
// A list of node selector requirements by node's labels.
MatchExpressions []corev1.NodeSelectorRequirement `json:"matchExpressions,omitempty"`
}

// CPU defines the requirements for the virtual CPU model.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message=".spec.cpu is immutable"
// +kubebuilder:validation:XValidation:rule="self.type == 'HostPassthrough' || self.type == 'Host' ? !has(self.model) && !has(self.features) && !has(self.discovery) : true",message="HostPassthrough and Host cannot have model, features or discovery"
// +kubebuilder:validation:XValidation:rule="self.type == 'Discovery' ? !has(self.model) && !has(self.features) : true",message="Discovery cannot have model or features"
// +kubebuilder:validation:XValidation:rule="self.type == 'Model' ? has(self.model) && !has(self.features) && !has(self.discovery) : true",message="Model requires model and cannot have features or discovery"
// +kubebuilder:validation:XValidation:rule="self.type == 'Features' ? has(self.features) && !has(self.model) && !has(self.discovery): true",message="Features requires features and cannot have model or discovery"
type CPU struct {
// +kubebuilder:validation:Required
Type CPUType `json:"type"`
// CPU model name. For more information about CPU models and topology, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology).
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:example=IvyBridge
Model string `json:"model,omitempty"`
// List of CPU instructions (features) required when type=Features.
// For more information about CPU features, refer to the [libvirt docs](https://libvirt.org/formatdomain.html#cpu-model-and-topology).
//
// +kubebuilder:validation:MinItems=1
// +kubebuilder:example={mmx, vmx, sse2}
Features []string `json:"features,omitempty"`
// Create a CPU model based on intersecting CPU features for selected nodes.
Discovery CpuDiscovery `json:"discovery,omitempty"`
}

type CpuDiscovery struct {
// A selection of nodes to be used as the basis for creating a universal CPU model.
NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"`
}

// SizingPolicy defines a policy for allocating computational resources to VMs.
// It is represented as a list.
// The cores.min - cores.max ranges for different elements of the list must not overlap.
type SizingPolicy struct {
// Memory sizing policy.
Memory *SizingPolicyMemory `json:"memory,omitempty"`
// Allowed values of the `coreFraction` parameter in percentages (e.g., "5%", "10%", "25%", "50%", "100%").
CoreFractions []CoreFractionValue `json:"coreFractions,omitempty"`
// Allowed values of the `dedicatedCores` parameter.
DedicatedCores []bool `json:"dedicatedCores,omitempty"`
// The policy applies for a specified range of the number of CPU cores.
Cores *SizingPolicyCores `json:"cores,omitempty"`
}

// CoreFractionValue represents CPU core fraction as a percentage string (e.g., "5%", "10%", "25%", "50%", "100%").
// +kubebuilder:validation:Pattern=`^([1-9]|[1-9][0-9]|100)%?$`
type CoreFractionValue string

type SizingPolicyMemory struct {
MemoryMinMax `json:",inline"`
// Memory size discretization step. For example, the combination of `min=2Gi, `max=4Gi` and `step=1Gi` allows to set the virtual machine memory size to 2Gi, 3Gi, or 4Gi.
//
// +kubebuilder:example="512Mi"
Step resource.Quantity `json:"step,omitempty"`

// Amount of memory per CPU core.
PerCore SizingPolicyMemoryPerCore `json:"perCore,omitempty"`
}

type SizingPolicyMemoryPerCore struct {
MemoryMinMax `json:",inline"`
}

type MemoryMinMax struct {
// Minimum amount of memory.
//
// +kubebuilder:example="1Gi"
Min resource.Quantity `json:"min,omitempty"`
// Maximum amount of memory.
//
// +kubebuilder:example="8Gi"
Max resource.Quantity `json:"max,omitempty"`
}

// +kubebuilder:validation:XValidation:rule="self.max > self.min",message="The maximum must be greater than the minimum"
// +kubebuilder:validation:XValidation:rule="has(self.step) ? self.max > self.step : true",message="The maximum must be greater than the step"
type SizingPolicyCores struct {
// Minimum number of CPU cores.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:Minimum=1
// +kubebuilder:example=1
Min int `json:"min"`
// Maximum number of CPU cores.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:Maximum=1024
// +kubebuilder:example=10
Max int `json:"max"`
// Discretization step for the CPU core number. For example, the combination of `min=2`, `max=10`, and `step=4` allows to set the number of virtual machine CPU cores to 2, 6, or 10.
//
// +kubebuilder:validation:Minimum=1
// +kubebuilder:example=1
Step int `json:"step,omitempty"`
}

// CPUType defines the CPU type, the following options are supported:
// * `Host`: Uses a virtual CPU with an instruction set closely matching the platform node's CPU.
// This provides high performance and functionality, as well as compatibility with "live" migration for nodes with similar processor types.
// For example, VM migration between nodes with Intel and AMD processors will not work.
// This is also true for different CPU generations, as their instruction set is different.
// * `HostPassthrough`: Uses the platform node's physical CPU directly, without any modifications.
// When using this class, the guest VM can only be transferred to a target node with a CPU exactly matching the source node's CPU.
// * `Discovery`: Create a virtual CPU based on instruction sets of physical CPUs for a selected set of nodes.
// * `Model`: CPU model. A CPU model is a named and previously defined set of supported CPU instructions.
// * `Features`: A required set of supported instructions for the CPU.
//
// +kubebuilder:validation:Enum={Host,HostPassthrough,Discovery,Model,Features}
type CPUType string

const (
CPUTypeHost CPUType = "Host"
CPUTypeHostPassthrough CPUType = "HostPassthrough"
CPUTypeDiscovery CPUType = "Discovery"
CPUTypeModel CPUType = "Model"
CPUTypeFeatures CPUType = "Features"
)

type VirtualMachineClassStatus struct {
Phase VirtualMachineClassPhase `json:"phase"`
CpuFeatures CpuFeatures `json:"cpuFeatures,omitempty"`
// List of nodes that support this CPU model.
// It is not displayed for the following types: `Host`, `HostPassthrough`.
//
// +kubebuilder:example={node-1, node-2}
AvailableNodes []string `json:"availableNodes,omitempty"`
// Maximum amount of free CPU and memory resources observed among all available nodes.
// +kubebuilder:example={"maxAllocatableResources: {\"cpu\": 1, \"memory\": \"10Gi\"}"}
MaxAllocatableResources corev1.ResourceList `json:"maxAllocatableResources,omitempty"`
// The latest detailed observations of the VirtualMachineClass resource.
Conditions []metav1.Condition `json:"conditions,omitempty"`
// Resource generation last processed by the controller.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}

// CpuFeatures
// Information on CPU features supported by this model.
// Shown only for `Features` or `Discovery` types.
type CpuFeatures struct {
// List of CPU features for this model.
//
// +kubebuilder:example={mmx, vmx, sse2}
Enabled []string `json:"enabled,omitempty"`
// List of unused processor features additionally available for a given group of nodes.
//
// +kubebuilder:example={ssse3, vme}
NotEnabledCommon []string `json:"notEnabledCommon,omitempty"`
}

// VirtualMachineClassPhase defines the current resource status:
// * `Pending`: The resource is not ready and waits until the suitable nodes supporting the required CPU model are available.
// * `Ready`: The resource is ready and available for use.
// * `Terminating`: The resource is terminating.
//
// +kubebuilder:validation:Enum={Pending,Ready,Terminating}
type VirtualMachineClassPhase string

const (
ClassPhasePending VirtualMachineClassPhase = "Pending"
ClassPhaseReady VirtualMachineClassPhase = "Ready"
ClassPhaseTerminating VirtualMachineClassPhase = "Terminating"
)
51 changes: 51 additions & 0 deletions api/core/v1alpha3/vmclasscondition/condition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
Copyright 2024 Flant JSC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package vmclasscondition

type Type string

func (t Type) String() string {
return string(t)
}

const (
TypeReady Type = "Ready"
TypeDiscovered Type = "Discovered"
TypeInUse Type = "InUse"
)

type Reason string

func (r Reason) String() string {
return string(r)
}

const (
// ReasonNoCpuFeaturesEnabled determines that processor functions are not available.
ReasonNoCpuFeaturesEnabled Reason = "NoCpuFeaturesEnabled"
// ReasonNoSuitableNodesFound determines that no suitable node has been found.
ReasonNoSuitableNodesFound Reason = "NoSuitableNodesFound"
// ReasonSuitableNodesFound determines that suitable node has been found.
ReasonSuitableNodesFound Reason = "SuitableNodesFound"

ReasonDiscoverySucceeded Reason = "DiscoverySucceeded"
ReasonDiscoverySkip Reason = "DiscoverySkip"
ReasonDiscoveryFailed Reason = "DiscoveryFailed"

// ReasonVMClassInUse is the event reason indicating that the VMClass is being used by a virtual machine.
ReasonVMClassInUse Reason = "VirtualMachineClassInUse"
)
Loading