From 53d631c6acd6c660d16583babad19904febc8686 Mon Sep 17 00:00:00 2001 From: Martin Necas Date: Tue, 5 Nov 2024 08:31:28 +0100 Subject: [PATCH] MTV-1536 | Use CDI for disk transfer in cold migration feature toggle Issue: This is alternative to https://github.com/kubev2v/forklift/pull/1109 which removes the virt-v2v disk transfer and replaces it with te CDI. The main dissadvatage of that solution is that in case customer will have problems with the new CDI method there won't be any easy way how to go back, to the virt-v2v. Fix: This change adds a new feature toggle `feature_vsphere_cold_cdi` in the forklift controller. By default the feature will be enabled and in case there will be problems such as disk corruption the user can disable it. Signed-off-by: Martin Necas --- operator/roles/forkliftcontroller/defaults/main.yml | 1 + .../templates/controller/deployment-controller.yml.j2 | 6 ++++++ pkg/apis/forklift/v1beta1/BUILD.bazel | 1 + pkg/apis/forklift/v1beta1/plan.go | 5 +++-- pkg/controller/plan/adapter/vsphere/builder.go | 2 +- pkg/controller/plan/adapter/vsphere/client.go | 2 +- pkg/controller/plan/kubevirt.go | 2 +- pkg/controller/plan/migration.go | 4 ++-- pkg/controller/plan/scheduler/vsphere/scheduler.go | 2 +- .../webhooks/validating-webhook/admitters/plan-admitter.go | 2 +- pkg/settings/features.go | 4 ++++ 11 files changed, 22 insertions(+), 9 deletions(-) diff --git a/operator/roles/forkliftcontroller/defaults/main.yml b/operator/roles/forkliftcontroller/defaults/main.yml index 25bbfa486..99032d16a 100644 --- a/operator/roles/forkliftcontroller/defaults/main.yml +++ b/operator/roles/forkliftcontroller/defaults/main.yml @@ -6,6 +6,7 @@ app_namespace: "{{ lookup( 'env', 'WATCH_NAMESPACE') or 'konveyor-forklift' }}" feature_ui_plugin: true feature_validation: true feature_volume_populator: true +feature_vsphere_v2v_transport: false k8s_cluster: false feature_auth_required: true diff --git a/operator/roles/forkliftcontroller/templates/controller/deployment-controller.yml.j2 b/operator/roles/forkliftcontroller/templates/controller/deployment-controller.yml.j2 index 7082b2b8b..9ee5748cc 100644 --- a/operator/roles/forkliftcontroller/templates/controller/deployment-controller.yml.j2 +++ b/operator/roles/forkliftcontroller/templates/controller/deployment-controller.yml.j2 @@ -114,6 +114,12 @@ spec: {% if virt_customize_configmap_name is defined %} - name: VIRT_CUSTOMIZE_MAP value: {{ virt_customize_configmap_name }} +{% endif %} + - name: FEATURE_VSPHERE_VIRT_V2V_TRANSPORT +{% if feature_vsphere_v2v_transport|bool %} + value: "true" +{% else %} + value: "false" {% endif %} {% if controller_profile_kind is defined and controller_profile_path is defined and controller_profile_duration is defined %} - name: PROFILE_KIND diff --git a/pkg/apis/forklift/v1beta1/BUILD.bazel b/pkg/apis/forklift/v1beta1/BUILD.bazel index 82b3bbad3..208cba326 100644 --- a/pkg/apis/forklift/v1beta1/BUILD.bazel +++ b/pkg/apis/forklift/v1beta1/BUILD.bazel @@ -24,6 +24,7 @@ go_library( "//pkg/apis/forklift/v1beta1/ref", "//pkg/lib/condition", "//pkg/lib/error", + "//pkg/settings", "//vendor/k8s.io/api/core/v1:core", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", "//vendor/k8s.io/apimachinery/pkg/runtime", diff --git a/pkg/apis/forklift/v1beta1/plan.go b/pkg/apis/forklift/v1beta1/plan.go index e9274c8bb..847c9148c 100644 --- a/pkg/apis/forklift/v1beta1/plan.go +++ b/pkg/apis/forklift/v1beta1/plan.go @@ -22,6 +22,7 @@ import ( "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" libcnd "github.com/konveyor/forklift-controller/pkg/lib/condition" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" + "github.com/konveyor/forklift-controller/pkg/settings" core "k8s.io/api/core/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -97,7 +98,7 @@ type Plan struct { // just use virt-v2v directly to convert the vm while copying data over. In other // cases, we use CDI to transfer disks to the destination cluster and then use // virt-v2v-in-place to convert these disks after cutover. -func (p *Plan) VSphereColdLocal() (bool, error) { +func (p *Plan) VSphereUseV2vTransport() (bool, error) { source := p.Referenced.Provider.Source if source == nil { return false, liberr.New("Cannot analyze plan, source provider is missing.") @@ -109,7 +110,7 @@ func (p *Plan) VSphereColdLocal() (bool, error) { switch source.Type() { case VSphere: - return !p.Spec.Warm && destination.IsHost(), nil + return !p.Spec.Warm && destination.IsHost() && settings.Settings.VsphereV2vTransport, nil case Ova: return true, nil default: diff --git a/pkg/controller/plan/adapter/vsphere/builder.go b/pkg/controller/plan/adapter/vsphere/builder.go index 86dffca4d..fdacf08a3 100644 --- a/pkg/controller/plan/adapter/vsphere/builder.go +++ b/pkg/controller/plan/adapter/vsphere/builder.go @@ -434,7 +434,7 @@ func (r *Builder) DataVolumes(vmRef ref.Ref, secret *core.Secret, _ *core.Config if disk.Datastore.ID == ds.ID { storageClass := mapped.Destination.StorageClass var dvSource cdi.DataVolumeSource - coldLocal, vErr := r.Context.Plan.VSphereColdLocal() + coldLocal, vErr := r.Context.Plan.VSphereUseV2vTransport() if vErr != nil { err = vErr return diff --git a/pkg/controller/plan/adapter/vsphere/client.go b/pkg/controller/plan/adapter/vsphere/client.go index 41fc0b7c3..0eedac1cd 100644 --- a/pkg/controller/plan/adapter/vsphere/client.go +++ b/pkg/controller/plan/adapter/vsphere/client.go @@ -277,7 +277,7 @@ func (r *Client) getChangeIds(vmRef ref.Ref, snapshotId string, hosts util.Hosts } func (r *Client) getClient(vm *model.VM, hosts util.HostsFunc) (client *vim25.Client, err error) { - if coldLocal, vErr := r.Plan.VSphereColdLocal(); vErr == nil && coldLocal { + if coldLocal, vErr := r.Plan.VSphereUseV2vTransport(); vErr == nil && coldLocal { // when virt-v2v runs the migration, forklift-controller should interact only // with the component that serves the SDK endpoint of the provider client = r.client.Client diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index d3b1fb8d0..43a7ebf25 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -1685,7 +1685,7 @@ func (r *KubeVirt) guestConversionPod(vm *plan.VMStatus, vmVolumes []cnv.Volume, nonRoot := true allowPrivilageEscalation := false // virt-v2v image - coldLocal, vErr := r.Context.Plan.VSphereColdLocal() + coldLocal, vErr := r.Context.Plan.VSphereUseV2vTransport() if vErr != nil { err = vErr return diff --git a/pkg/controller/plan/migration.go b/pkg/controller/plan/migration.go index f3c9e0530..3f99eb1fe 100644 --- a/pkg/controller/plan/migration.go +++ b/pkg/controller/plan/migration.go @@ -1658,7 +1658,7 @@ func (r *Migration) updateConversionProgress(vm *plan.VMStatus, step *plan.Step) break } - coldLocal, err := r.Context.Plan.VSphereColdLocal() + coldLocal, err := r.Context.Plan.VSphereUseV2vTransport() switch { case err != nil: return liberr.Wrap(err) @@ -1853,7 +1853,7 @@ type Predicate struct { // Evaluate predicate flags. func (r *Predicate) Evaluate(flag libitr.Flag) (allowed bool, err error) { - coldLocal, vErr := r.context.Plan.VSphereColdLocal() + coldLocal, vErr := r.context.Plan.VSphereUseV2vTransport() if vErr != nil { err = vErr return diff --git a/pkg/controller/plan/scheduler/vsphere/scheduler.go b/pkg/controller/plan/scheduler/vsphere/scheduler.go index 82cb722b8..8ca1b94ec 100644 --- a/pkg/controller/plan/scheduler/vsphere/scheduler.go +++ b/pkg/controller/plan/scheduler/vsphere/scheduler.go @@ -197,7 +197,7 @@ func (r *Scheduler) buildPending() (err error) { } func (r *Scheduler) cost(vm *model.VM, vmStatus *plan.VMStatus) int { - coldLocal, _ := r.Plan.VSphereColdLocal() + coldLocal, _ := r.Plan.VSphereUseV2vTransport() if coldLocal { switch vmStatus.Phase { case CreateVM, PostHook, Completed: diff --git a/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go b/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go index afa3b4881..6180d693c 100644 --- a/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go +++ b/pkg/forklift-api/webhooks/validating-webhook/admitters/plan-admitter.go @@ -109,7 +109,7 @@ func (admitter *PlanAdmitter) validateLUKS() error { return err } - coldLocal, vErr := admitter.plan.VSphereColdLocal() + coldLocal, vErr := admitter.plan.VSphereUseV2vTransport() if vErr != nil { log.Error(vErr, "Could not analyze plan, failing") return vErr diff --git a/pkg/settings/features.go b/pkg/settings/features.go index 58264d48f..311d26ca2 100644 --- a/pkg/settings/features.go +++ b/pkg/settings/features.go @@ -5,6 +5,7 @@ const ( FeatureOvirtWarmMigration = "FEATURE_OVIRT_WARM_MIGRATION" FeatureRetainPrecopyImporterPods = "FEATURE_RETAIN_PRECOPY_IMPORTER_PODS" FeatureVsphereIncrementalBackup = "FEATURE_VSPHERE_INCREMENTAL_BACKUP" + FeatureVsphereV2vTransport = "FEATURE_VSPHERE_VIRT_V2V_TRANSPORT" ) // Feature gates. @@ -16,6 +17,8 @@ type Features struct { RetainPrecopyImporterPods bool // Whether to use changeID-based incremental backup workflow (with a version of CDI that supports it) VsphereIncrementalBackup bool + // Whether to use changeID-based incremental backup workflow (with a version of CDI that supports it) + VsphereV2vTransport bool } // Load settings. @@ -23,5 +26,6 @@ func (r *Features) Load() (err error) { r.OvirtWarmMigration = getEnvBool(FeatureOvirtWarmMigration, false) r.RetainPrecopyImporterPods = getEnvBool(FeatureRetainPrecopyImporterPods, false) r.VsphereIncrementalBackup = getEnvBool(FeatureVsphereIncrementalBackup, false) + r.VsphereV2vTransport = getEnvBool(FeatureVsphereV2vTransport, false) return }