diff --git a/api/core/v1alpha2/virtual_machine_operation.go b/api/core/v1alpha2/virtual_machine_operation.go index 1f0344e180..4c2670c2ab 100644 --- a/api/core/v1alpha2/virtual_machine_operation.go +++ b/api/core/v1alpha2/virtual_machine_operation.go @@ -49,6 +49,7 @@ type VirtualMachineOperation struct { // +kubebuilder:validation:XValidation:rule="self.type == 'Migrate' ? !has(self.force) || !self.force : true",message="The `Migrate` operation cannot be performed forcibly." // +kubebuilder:validation:XValidation:rule="self.type == 'Restore' ? has(self.restore) : true",message="Restore requires restore field." // +kubebuilder:validation:XValidation:rule="self.type == 'Clone' ? has(self.clone) : true",message="Clone requires clone field." +// +kubebuilder:validation:XValidation:rule="!(has(self.migrate)) || self.type == 'Migrate'",message="spec.migrate can only be set when spec.type is 'Migrate'" type VirtualMachineOperationSpec struct { Type VMOPType `json:"type"` // Name of the virtual machine the operation is performed for. @@ -63,6 +64,8 @@ type VirtualMachineOperationSpec struct { Restore *VirtualMachineOperationRestoreSpec `json:"restore,omitempty"` // Clone defines the clone operation. Clone *VirtualMachineOperationCloneSpec `json:"clone,omitempty"` + // Migrate defines the Migrate operation. + Migrate *VirtualMachineOperationMigrateSpec `json:"migrate,omitempty"` } // VirtualMachineOperationRestoreSpec defines the restore operation. @@ -84,6 +87,14 @@ type VirtualMachineOperationCloneSpec struct { Customization *VirtualMachineOperationCloneCustomization `json:"customization,omitempty"` } +// VirtualMachineOperationMigrateSpec defines the restore operation. +type VirtualMachineOperationMigrateSpec struct { + // NodeSelector is a selector which must be true for the virtual machine to fit on a node. + // Selector which must match a node's labels for the virtual machine to be scheduled on + // that node. + NodeSelector map[string]string `json:"nodeSelector,omitempty"` +} + // +kubebuilder:validation:XValidation:rule="!has(self.namePrefix) || (size(self.namePrefix) >= 1 && size(self.namePrefix) <= 59)",message="namePrefix length must be between 1 and 59 characters if set" // +kubebuilder:validation:XValidation:rule="!has(self.nameSuffix) || (size(self.nameSuffix) >= 1 && size(self.nameSuffix) <= 59)",message="nameSuffix length must be between 1 and 59 characters if set" // VirtualMachineOperationCloneCustomization defines customization options for cloning. @@ -162,7 +173,7 @@ const ( // * `Start`: Start the virtual machine. // * `Stop`: Stop the virtual machine. // * `Restart`: Restart the virtual machine. -// * `Migrate` (deprecated): Migrate the virtual machine to another node where it can be started. +// * `Migrate`: Migrate the virtual machine to another node where it can be started. // * `Evict`: Migrate the virtual machine to another node where it can be started. // * `Restore`: Restore the virtual machine from a snapshot. // * `Clone`: Clone the virtual machine to a new virtual machine. diff --git a/api/core/v1alpha2/zz_generated.deepcopy.go b/api/core/v1alpha2/zz_generated.deepcopy.go index e1ad5e58e5..c64f428875 100644 --- a/api/core/v1alpha2/zz_generated.deepcopy.go +++ b/api/core/v1alpha2/zz_generated.deepcopy.go @@ -2659,6 +2659,29 @@ func (in *VirtualMachineOperationList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VirtualMachineOperationMigrateSpec) DeepCopyInto(out *VirtualMachineOperationMigrateSpec) { + *out = *in + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualMachineOperationMigrateSpec. +func (in *VirtualMachineOperationMigrateSpec) DeepCopy() *VirtualMachineOperationMigrateSpec { + if in == nil { + return nil + } + out := new(VirtualMachineOperationMigrateSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VirtualMachineOperationRestoreSpec) DeepCopyInto(out *VirtualMachineOperationRestoreSpec) { *out = *in @@ -2693,6 +2716,11 @@ func (in *VirtualMachineOperationSpec) DeepCopyInto(out *VirtualMachineOperation *out = new(VirtualMachineOperationCloneSpec) (*in).DeepCopyInto(*out) } + if in.Migrate != nil { + in, out := &in.Migrate, &out.Migrate + *out = new(VirtualMachineOperationMigrateSpec) + (*in).DeepCopyInto(*out) + } return } diff --git a/crds/virtualmachineoperations.yaml b/crds/virtualmachineoperations.yaml index e617c8a5ac..9fa92d97a9 100644 --- a/crds/virtualmachineoperations.yaml +++ b/crds/virtualmachineoperations.yaml @@ -161,6 +161,18 @@ spec: * Effect on `Restart` and `Stop`: operation performs immediately. * Effect on `Evict` and `Migrate`: enable the AutoConverge feature to force migration via CPU throttling if the `PreferSafe` or `PreferForced` policies are used for live migration. type: boolean + migrate: + description: Migrate defines the Migrate operation. + properties: + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector is a selector which must be true for the virtual machine to fit on a node. + Selector which must match a node's labels for the virtual machine to be scheduled on + that node. + type: object + type: object restore: description: Restore defines the restore operation. properties: @@ -191,7 +203,7 @@ spec: * `Start`: Start the virtual machine. * `Stop`: Stop the virtual machine. * `Restart`: Restart the virtual machine. - * `Migrate` (deprecated): Migrate the virtual machine to another node where it can be started. + * `Migrate`: Migrate the virtual machine to another node where it can be started. * `Evict`: Migrate the virtual machine to another node where it can be started. * `Restore`: Restore the virtual machine from a snapshot. * `Clone`: Clone the virtual machine to a new virtual machine. @@ -227,6 +239,8 @@ spec: rule: "self.type == 'Restore' ? has(self.restore) : true" - message: Clone requires clone field. rule: "self.type == 'Clone' ? has(self.clone) : true" + - message: spec.migrate can only be set when spec.type is 'Migrate' + rule: "!(has(self.migrate)) || self.type == 'Migrate'" status: properties: conditions: diff --git a/images/virtualization-artifact/pkg/controller/vmop/migration/internal/service/migration.go b/images/virtualization-artifact/pkg/controller/vmop/migration/internal/service/migration.go index 138a7da43d..902c766c62 100644 --- a/images/virtualization-artifact/pkg/controller/vmop/migration/internal/service/migration.go +++ b/images/virtualization-artifact/pkg/controller/vmop/migration/internal/service/migration.go @@ -19,6 +19,7 @@ package service import ( "context" "fmt" + "maps" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -51,7 +52,7 @@ func (s MigrationService) IsApplicableForRunPolicy(runPolicy v1alpha2.RunPolicy) } func (s MigrationService) CreateMigration(ctx context.Context, vmop *v1alpha2.VirtualMachineOperation) error { - return client.IgnoreAlreadyExists(s.client.Create(ctx, &virtv1.VirtualMachineInstanceMigration{ + vmim := &virtv1.VirtualMachineInstanceMigration{ TypeMeta: metav1.TypeMeta{ APIVersion: virtv1.SchemeGroupVersion.String(), Kind: "VirtualMachineInstanceMigration", @@ -73,7 +74,14 @@ func (s MigrationService) CreateMigration(ctx context.Context, vmop *v1alpha2.Vi Spec: virtv1.VirtualMachineInstanceMigrationSpec{ VMIName: vmop.Spec.VirtualMachine, }, - })) + } + + if vmop.Spec.Migrate != nil && vmop.Spec.Migrate.NodeSelector != nil { + vmim.Spec.AddedNodeSelector = make(map[string]string, len(vmop.Spec.Migrate.NodeSelector)) + maps.Copy(vmim.Spec.AddedNodeSelector, vmop.Spec.Migrate.NodeSelector) + } + + return client.IgnoreAlreadyExists(s.client.Create(ctx, vmim)) } func (s MigrationService) DeleteMigration(ctx context.Context, vmop *v1alpha2.VirtualMachineOperation) error {