From 7661eca91769751644d5b8acd22c9b1e358ceb38 Mon Sep 17 00:00:00 2001 From: Arik Hadas Date: Tue, 11 Jun 2024 13:01:51 +0300 Subject: [PATCH] vsphere: perform CBT operations on hosts when needed Previously, when running warm migrations from a vSphere provider with vCenter as its SDK endpoint, we ran create-snapshot, set-checkpoints and remove-snapshots on vCenter, even when there was a network defined for an ESXi host. That cause CDI to fail: the identifier of a snapshot that was recievd from vCenter was different than the identifier of the same snapshot in ESXi and therefore CDI, that interacts with ESXi hosts in this scenario, failed to find the snapshot. Now, when running CBT operations we check whether the disk(s) will be transferred from vCenter or ESXi, and perform the aforementioned CBT operations on the corresponding component. Signed-off-by: Arik Hadas --- pkg/controller/plan/BUILD.bazel | 1 + pkg/controller/plan/adapter/base/BUILD.bazel | 1 + pkg/controller/plan/adapter/base/doc.go | 7 +- pkg/controller/plan/adapter/ocp/BUILD.bazel | 1 + pkg/controller/plan/adapter/ocp/client.go | 7 +- .../plan/adapter/openstack/client.go | 7 +- pkg/controller/plan/adapter/ova/BUILD.bazel | 1 + pkg/controller/plan/adapter/ova/client.go | 7 +- pkg/controller/plan/adapter/ovirt/client.go | 7 +- .../plan/adapter/vsphere/BUILD.bazel | 1 + pkg/controller/plan/adapter/vsphere/client.go | 147 +++++++++++++++--- pkg/controller/plan/migration.go | 60 ++++++- pkg/controller/plan/util/utils.go | 3 + 13 files changed, 209 insertions(+), 41 deletions(-) diff --git a/pkg/controller/plan/BUILD.bazel b/pkg/controller/plan/BUILD.bazel index 363f98ac6..8172e0ac9 100644 --- a/pkg/controller/plan/BUILD.bazel +++ b/pkg/controller/plan/BUILD.bazel @@ -28,6 +28,7 @@ go_library( "//pkg/controller/plan/handler", "//pkg/controller/plan/scheduler", "//pkg/controller/provider/web", + "//pkg/controller/provider/web/vsphere", "//pkg/controller/validation", "//pkg/lib/client/openshift", "//pkg/lib/condition", diff --git a/pkg/controller/plan/adapter/base/BUILD.bazel b/pkg/controller/plan/adapter/base/BUILD.bazel index e4d0cea04..1b82caa96 100644 --- a/pkg/controller/plan/adapter/base/BUILD.bazel +++ b/pkg/controller/plan/adapter/base/BUILD.bazel @@ -10,6 +10,7 @@ go_library( "//pkg/apis/forklift/v1beta1/plan", "//pkg/apis/forklift/v1beta1/ref", "//pkg/controller/plan/context", + "//pkg/controller/plan/util", "//pkg/lib/error", "//vendor/k8s.io/api/core/v1:core", "//vendor/kubevirt.io/api/core/v1:core", diff --git a/pkg/controller/plan/adapter/base/doc.go b/pkg/controller/plan/adapter/base/doc.go index 395466171..af9c8c9cf 100644 --- a/pkg/controller/plan/adapter/base/doc.go +++ b/pkg/controller/plan/adapter/base/doc.go @@ -5,6 +5,7 @@ import ( planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" core "k8s.io/api/core/v1" cnv "kubevirt.io/api/core/v1" @@ -103,13 +104,13 @@ type Client interface { // Return whether the source VM is powered off. PoweredOff(vmRef ref.Ref) (bool, error) // Create a snapshot of the source VM. - CreateSnapshot(vmRef ref.Ref) (string, error) + CreateSnapshot(vmRef ref.Ref, hostsFunc util.HostsFunc) (string, error) // Remove all warm migration snapshots. - RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) error + RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hostsFunc util.HostsFunc) error // Check if a snapshot is ready to transfer. CheckSnapshotReady(vmRef ref.Ref, snapshot string) (ready bool, err error) // Set DataVolume checkpoints. - SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool) (err error) + SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool, hostsFunc util.HostsFunc) (err error) // Close connections to the provider API. Close() // Finalize migrations diff --git a/pkg/controller/plan/adapter/ocp/BUILD.bazel b/pkg/controller/plan/adapter/ocp/BUILD.bazel index 866ccb325..d097e5931 100644 --- a/pkg/controller/plan/adapter/ocp/BUILD.bazel +++ b/pkg/controller/plan/adapter/ocp/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//pkg/apis/forklift/v1beta1/ref", "//pkg/controller/plan/adapter/base", "//pkg/controller/plan/context", + "//pkg/controller/plan/util", "//pkg/controller/provider/web", "//pkg/lib/client/openshift", "//pkg/lib/error", diff --git a/pkg/controller/plan/adapter/ocp/client.go b/pkg/controller/plan/adapter/ocp/client.go index 44576f9d2..0c2564994 100644 --- a/pkg/controller/plan/adapter/ocp/client.go +++ b/pkg/controller/plan/adapter/ocp/client.go @@ -7,6 +7,7 @@ import ( planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" "github.com/konveyor/forklift-controller/pkg/settings" core "k8s.io/api/core/v1" @@ -34,7 +35,7 @@ func (r *Client) Close() { } // CreateSnapshot implements base.Client -func (r *Client) CreateSnapshot(vmRef ref.Ref) (string, error) { +func (r *Client) CreateSnapshot(vmRef ref.Ref, hostsFunc util.HostsFunc) (string, error) { return "", nil } @@ -123,12 +124,12 @@ func (r *Client) PoweredOff(vmRef ref.Ref) (bool, error) { } // RemoveSnapshots implements base.Client -func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) error { +func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hostsFunc util.HostsFunc) error { return nil } // SetCheckpoints implements base.Client -func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool) (err error) { +func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool, hostsFunc util.HostsFunc) (err error) { return nil } diff --git a/pkg/controller/plan/adapter/openstack/client.go b/pkg/controller/plan/adapter/openstack/client.go index 5603133bc..61a751ebd 100644 --- a/pkg/controller/plan/adapter/openstack/client.go +++ b/pkg/controller/plan/adapter/openstack/client.go @@ -8,6 +8,7 @@ import ( planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" model "github.com/konveyor/forklift-controller/pkg/controller/provider/web/openstack" libclient "github.com/konveyor/forklift-controller/pkg/lib/client/openstack" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" @@ -109,12 +110,12 @@ func (r *Client) PoweredOff(vmRef ref.Ref) (off bool, err error) { } // Create a snapshot of the source VM. -func (r *Client) CreateSnapshot(vmRef ref.Ref) (imageID string, err error) { +func (r *Client) CreateSnapshot(vmRef ref.Ref, hostsFunc util.HostsFunc) (imageID string, err error) { return } // Remove all warm migration snapshots. -func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) (err error) { +func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hostsFunc util.HostsFunc) (err error) { return } @@ -124,7 +125,7 @@ func (r *Client) CheckSnapshotReady(vmRef ref.Ref, imageID string) (ready bool, } // Set DataVolume checkpoints. -func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool) error { +func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool, hostsFunc util.HostsFunc) error { return nil } diff --git a/pkg/controller/plan/adapter/ova/BUILD.bazel b/pkg/controller/plan/adapter/ova/BUILD.bazel index 0795708f6..7e226422e 100644 --- a/pkg/controller/plan/adapter/ova/BUILD.bazel +++ b/pkg/controller/plan/adapter/ova/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//pkg/apis/forklift/v1beta1/ref", "//pkg/controller/plan/adapter/base", "//pkg/controller/plan/context", + "//pkg/controller/plan/util", "//pkg/controller/provider/model/ova", "//pkg/controller/provider/web", "//pkg/controller/provider/web/base", diff --git a/pkg/controller/plan/adapter/ova/client.go b/pkg/controller/plan/adapter/ova/client.go index b94ef9003..c890eab66 100644 --- a/pkg/controller/plan/adapter/ova/client.go +++ b/pkg/controller/plan/adapter/ova/client.go @@ -9,6 +9,7 @@ import ( planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" libweb "github.com/konveyor/forklift-controller/pkg/lib/inventory/web" core "k8s.io/api/core/v1" cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" @@ -45,12 +46,12 @@ func (r *Client) connect() (err error) { } // Create a VM snapshot and return its ID. -func (r *Client) CreateSnapshot(vmRef ref.Ref) (snapshot string, err error) { +func (r *Client) CreateSnapshot(vmRef ref.Ref, hostsFunc util.HostsFunc) (snapshot string, err error) { return } // Remove all warm migration snapshots. -func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) (err error) { +func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hostsFunc util.HostsFunc) (err error) { return } @@ -60,7 +61,7 @@ func (r *Client) CheckSnapshotReady(vmRef ref.Ref, snapshot string) (ready bool, } // Set DataVolume checkpoints. -func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool) (err error) { +func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool, hostsFunc util.HostsFunc) (err error) { return } diff --git a/pkg/controller/plan/adapter/ovirt/client.go b/pkg/controller/plan/adapter/ovirt/client.go index 5a2394ff7..c665dc978 100644 --- a/pkg/controller/plan/adapter/ovirt/client.go +++ b/pkg/controller/plan/adapter/ovirt/client.go @@ -11,6 +11,7 @@ import ( planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" "github.com/konveyor/forklift-controller/pkg/controller/provider/container/ovirt" "github.com/konveyor/forklift-controller/pkg/controller/provider/web" model "github.com/konveyor/forklift-controller/pkg/controller/provider/web/ovirt" @@ -39,7 +40,7 @@ type Client struct { } // Create a VM snapshot and return its ID. -func (r *Client) CreateSnapshot(vmRef ref.Ref) (snapshot string, err error) { +func (r *Client) CreateSnapshot(vmRef ref.Ref, hostsFunc util.HostsFunc) (snapshot string, err error) { _, vmService, err := r.getVM(vmRef) if err != nil { err = liberr.Wrap(err) @@ -74,7 +75,7 @@ func (r *Client) CreateSnapshot(vmRef ref.Ref) (snapshot string, err error) { } // Remove all warm migration snapshots. -func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) (err error) { +func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hostsFunc util.HostsFunc) (err error) { // Snapshot removal is done in Finalize to avoid race conditions return } @@ -110,7 +111,7 @@ func (r *Client) CheckSnapshotReady(vmRef ref.Ref, snapshot string) (ready bool, } // Set DataVolume checkpoints. -func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool) (err error) { +func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool, hostsFunc util.HostsFunc) (err error) { n := len(precopies) previous := "" current := precopies[n-1].Snapshot diff --git a/pkg/controller/plan/adapter/vsphere/BUILD.bazel b/pkg/controller/plan/adapter/vsphere/BUILD.bazel index 1303c6c6a..380efa315 100644 --- a/pkg/controller/plan/adapter/vsphere/BUILD.bazel +++ b/pkg/controller/plan/adapter/vsphere/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//pkg/apis/forklift/v1beta1/ref", "//pkg/controller/plan/adapter/base", "//pkg/controller/plan/context", + "//pkg/controller/plan/util", "//pkg/controller/provider/container/vsphere", "//pkg/controller/provider/model/vsphere", "//pkg/controller/provider/web", diff --git a/pkg/controller/plan/adapter/vsphere/client.go b/pkg/controller/plan/adapter/vsphere/client.go index 6ad372a98..e78b1e1e6 100644 --- a/pkg/controller/plan/adapter/vsphere/client.go +++ b/pkg/controller/plan/adapter/vsphere/client.go @@ -6,9 +6,11 @@ import ( liburl "net/url" "strconv" + "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" planapi "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/util" model "github.com/konveyor/forklift-controller/pkg/controller/provider/web/vsphere" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" "github.com/konveyor/forklift-controller/pkg/settings" @@ -19,8 +21,10 @@ import ( "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" + core "k8s.io/api/core/v1" "k8s.io/utils/ptr" cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" ) const ( @@ -31,13 +35,14 @@ const ( // vSphere VM Client type Client struct { *plancontext.Context - client *govmomi.Client + client *govmomi.Client + hostClients map[string]*govmomi.Client } // Create a VM snapshot and return its ID. -func (r *Client) CreateSnapshot(vmRef ref.Ref) (id string, err error) { +func (r *Client) CreateSnapshot(vmRef ref.Ref, hosts util.HostsFunc) (id string, err error) { r.Log.V(1).Info("Creating snapshot", "vmRef", vmRef) - vm, err := r.getVM(vmRef) + vm, err := r.getVM(vmRef, hosts) if err != nil { return } @@ -63,7 +68,7 @@ func (r *Client) CheckSnapshotReady(vmRef ref.Ref, snapshot string) (ready bool, } // Remove all warm migration snapshots. -func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) (err error) { +func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hosts util.HostsFunc) (err error) { r.Log.V(1).Info("RemoveSnapshot", "vmRef", vmRef, @@ -76,16 +81,16 @@ func (r *Client) RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) (er // only necessary to clean up the last snapshot if this feature is enabled, // because all others will have already been cleaned up. lastSnapshot := precopies[len(precopies)-1].Snapshot - err = r.removeSnapshot(vmRef, lastSnapshot, false) + err = r.removeSnapshot(vmRef, lastSnapshot, false, hosts) } else { rootSnapshot := precopies[0].Snapshot - err = r.removeSnapshot(vmRef, rootSnapshot, true) + err = r.removeSnapshot(vmRef, rootSnapshot, true, hosts) } return } // Set DataVolume checkpoints. -func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool) (err error) { +func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, datavolumes []cdi.DataVolume, final bool, hosts util.HostsFunc) (err error) { n := len(precopies) previous := "" current := precopies[n-1].Snapshot @@ -103,7 +108,7 @@ func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, data if settings.Settings.VsphereIncrementalBackup && previous != "" { var changeIds map[string]string - changeIds, err = r.getChangeIds(vmRef, previous) + changeIds, err = r.getChangeIds(vmRef, previous, hosts) if err != nil { return } @@ -115,7 +120,7 @@ func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, data }) dv.Spec.FinalCheckpoint = final } - err = r.removeSnapshot(vmRef, previous, false) + err = r.removeSnapshot(vmRef, previous, false, hosts) if err != nil { return } @@ -134,7 +139,7 @@ func (r *Client) SetCheckpoints(vmRef ref.Ref, precopies []planapi.Precopy, data // Get the power state of the VM. func (r *Client) PowerState(vmRef ref.Ref) (state planapi.VMPowerState, err error) { - vm, err := r.getVM(vmRef) + vm, err := r.getVM(vmRef, nullableHosts) if err != nil { return } @@ -156,7 +161,7 @@ func (r *Client) PowerState(vmRef ref.Ref) (state planapi.VMPowerState, err erro // Power on the VM. func (r *Client) PowerOn(vmRef ref.Ref) (err error) { - vm, err := r.getVM(vmRef) + vm, err := r.getVM(vmRef, nullableHosts) if err != nil { return } @@ -177,7 +182,7 @@ func (r *Client) PowerOn(vmRef ref.Ref) (err error) { // Power off the VM. Requires guest tools to be installed. func (r *Client) PowerOff(vmRef ref.Ref) (err error) { - vm, err := r.getVM(vmRef) + vm, err := r.getVM(vmRef, nullableHosts) if err != nil { return } @@ -199,7 +204,7 @@ func (r *Client) PowerOff(vmRef ref.Ref) (err error) { // Determine whether the VM has been powered off. func (r *Client) PoweredOff(vmRef ref.Ref) (poweredOff bool, err error) { - vm, err := r.getVM(vmRef) + vm, err := r.getVM(vmRef, nullableHosts) if err != nil { return } @@ -219,6 +224,11 @@ func (r *Client) Close() { r.client.CloseIdleConnections() r.client = nil } + for _, client := range r.hostClients { + _ = client.Logout(context.TODO()) + client.CloseIdleConnections() + } + r.hostClients = nil } func (c *Client) Finalize(vms []*planapi.VMStatus, planName string) { @@ -230,8 +240,8 @@ func (r *Client) PreTransferActions(vmRef ref.Ref) (ready bool, err error) { } // Get the changeId for a VM snapshot. -func (r *Client) getChangeIds(vmRef ref.Ref, snapshotId string) (changeIdMapping map[string]string, err error) { - vm, err := r.getVM(vmRef) +func (r *Client) getChangeIds(vmRef ref.Ref, snapshotId string, hosts util.HostsFunc) (changeIdMapping map[string]string, err error) { + vm, err := r.getVM(vmRef, hosts) if err != nil { return } @@ -266,8 +276,96 @@ func (r *Client) getChangeIds(vmRef ref.Ref, snapshotId string) (changeIdMapping return } +func (r *Client) getClient(vm *model.VM, hosts util.HostsFunc) (client *vim25.Client, err error) { + if el9, el9Err := r.Plan.VSphereUsesEl9VirtV2v(); el9Err == nil && el9 { + // when virt-v2v/el9 runs the migration, forklift-controller should interact only + // with the component that serves the SDK endpoint of the provider + client = r.client.Client + return + } + + if r.Source.Provider.Spec.Settings[v1beta1.SDK] == v1beta1.ESXI { + // when migrating from ESXi host, we use the client of the SDK endpoint of the provider, + // there's no need in a different client (the ESXi host is the only component involved in the migration) + client = r.client.Client + return + } + + host := &model.Host{} + if err = r.Source.Inventory.Get(host, vm.Host); err != nil { + err = liberr.Wrap(err, "host", vm.Host) + return + } + + if cachedClient, found := r.hostClients[host.ID]; found { + // return the cached client for the ESXi host + client = cachedClient.Client + return + } + + if hostMap, hostsErr := hosts(); hostsErr == nil { + if hostDef, found := hostMap[host.ID]; found { + // create a new client for the ESXi host we are going to transfer the disk(s) from, and cache it + client, err = r.getHostClient(hostDef, host) + } else { + // there is no network defined for the ESXi host, so we will transfer the disk(s) from vCenter and + // thus there is no need in a client for the ESXi host but we use the client for vCenter instead + client = r.client.Client + } + } else { + err = liberr.Wrap(hostsErr) + } + return +} + +func (r *Client) getHostClient(hostDef *v1beta1.Host, host *model.Host) (client *vim25.Client, err error) { + url, err := liburl.Parse("https://" + hostDef.Spec.IpAddress + "/sdk") + if err != nil { + err = liberr.Wrap(err) + return + } + + ref := hostDef.Spec.Secret + secret := &core.Secret{} + err = r.Get( + context.TODO(), + k8sclient.ObjectKey{ + Namespace: ref.Namespace, + Name: ref.Name, + }, + secret) + if err != nil { + err = liberr.Wrap(err) + return + } + + url.User = liburl.UserPassword(string(secret.Data["user"]), string(secret.Data["password"])) + soapClient := soap.NewClient(url, r.getInsecureSkipVerifyFlag()) + soapClient.SetThumbprint(url.Host, host.Thumbprint) + vimClient, err := vim25.NewClient(context.TODO(), soapClient) + if err != nil { + err = liberr.Wrap(err) + return + } + hostClient := &govmomi.Client{ + SessionManager: session.NewManager(vimClient), + Client: vimClient, + } + if err = hostClient.Login(context.TODO(), url.User); err != nil { + err = liberr.Wrap(err) + return + } + + if r.hostClients == nil { + r.hostClients = make(map[string]*govmomi.Client) + } + r.hostClients[host.ID] = hostClient + client = hostClient.Client + return +} + // Get the VM by ref. -func (r *Client) getVM(vmRef ref.Ref) (vsphereVm *object.VirtualMachine, err error) { +func (r *Client) getVM(vmRef ref.Ref, hosts util.HostsFunc) (vsphereVm *object.VirtualMachine, err error) { vm := &model.VM{} err = r.Source.Inventory.Find(vm, vmRef) if err != nil { @@ -275,7 +373,12 @@ func (r *Client) getVM(vmRef ref.Ref) (vsphereVm *object.VirtualMachine, err err return } - searchIndex := object.NewSearchIndex(r.client.Client) + client, err := r.getClient(vm, hosts) + if err != nil { + return + } + + searchIndex := object.NewSearchIndex(client) vsphereRef, err := searchIndex.FindByUuid(context.TODO(), nil, vm.UUID, true, ptr.To(false)) if err != nil { err = liberr.Wrap(err) @@ -288,18 +391,22 @@ func (r *Client) getVM(vmRef ref.Ref) (vsphereVm *object.VirtualMachine, err err vmRef.String())) return } - vsphereVm = object.NewVirtualMachine(r.client.Client, vsphereRef.Reference()) + vsphereVm = object.NewVirtualMachine(client, vsphereRef.Reference()) + return +} + +func nullableHosts() (hosts map[string]*v1beta1.Host, err error) { return } // Remove a VM snapshot and optionally its children. -func (r *Client) removeSnapshot(vmRef ref.Ref, snapshot string, children bool) (err error) { +func (r *Client) removeSnapshot(vmRef ref.Ref, snapshot string, children bool, hosts util.HostsFunc) (err error) { r.Log.Info("Removing snapshot", "vmRef", vmRef, "snapshot", snapshot, "children", children) - vm, err := r.getVM(vmRef) + vm, err := r.getVM(vmRef, hosts) if err != nil { return } diff --git a/pkg/controller/plan/migration.go b/pkg/controller/plan/migration.go index 56efb1748..c633ae80e 100644 --- a/pkg/controller/plan/migration.go +++ b/pkg/controller/plan/migration.go @@ -19,9 +19,11 @@ import ( plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" "github.com/konveyor/forklift-controller/pkg/controller/plan/scheduler" "github.com/konveyor/forklift-controller/pkg/controller/provider/web" + model "github.com/konveyor/forklift-controller/pkg/controller/provider/web/vsphere" libcnd "github.com/konveyor/forklift-controller/pkg/lib/condition" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" libitr "github.com/konveyor/forklift-controller/pkg/lib/itinerary" + libref "github.com/konveyor/forklift-controller/pkg/lib/ref" "github.com/konveyor/forklift-controller/pkg/settings" batchv1 "k8s.io/api/batch/v1" core "k8s.io/api/core/v1" @@ -490,7 +492,7 @@ func (r *Migration) removeWarmSnapshots(vm *plan.VMStatus) { if vm.Warm == nil { return } - if err := r.provider.RemoveSnapshots(vm.Ref, vm.Warm.Precopies); err != nil { + if err := r.provider.RemoveSnapshots(vm.Ref, vm.Warm.Precopies, r.loadHosts); err != nil { r.Log.Error( err, "Failed to clean up warm migration snapshots.", @@ -789,7 +791,7 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { } } if vm.Warm != nil { - err = r.provider.SetCheckpoints(vm.Ref, vm.Warm.Precopies, dataVolumes, false) + err = r.provider.SetCheckpoints(vm.Ref, vm.Warm.Precopies, dataVolumes, false, r.loadHosts) if err != nil { step.AddError(err.Error()) err = nil @@ -961,8 +963,7 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { break } var snapshot string - snapshot, err = r.provider.CreateSnapshot(vm.Ref) - if err != nil { + if snapshot, err = r.provider.CreateSnapshot(vm.Ref, r.loadHosts); err != nil { if errors.As(err, &web.ProviderNotReadyError{}) || errors.As(err, &web.ConflictError{}) { return } @@ -1078,7 +1079,7 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { return } if step.MarkedCompleted() { - err = r.provider.RemoveSnapshots(vm.Ref, vm.Warm.Precopies) + err = r.provider.RemoveSnapshots(vm.Ref, vm.Warm.Precopies, r.loadHosts) if err != nil { r.Log.Info( "Failed to clean up warm migration snapshots.", @@ -1201,6 +1202,53 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { return } +// Load host CRs. +func (r *Migration) loadHosts() (hosts map[string]*v1beta1.Host, err error) { + list := &v1beta1.HostList{} + err = r.List( + context.TODO(), + list, + &client.ListOptions{ + Namespace: r.Source.Provider.Namespace, + }, + ) + if err != nil { + err = liberr.Wrap(err) + return + } + hostMap := map[string]*v1beta1.Host{} + for i := range list.Items { + host := &list.Items[i] + ref := host.Spec.Ref + if !libref.Equals(&host.Spec.Provider, &r.Plan.Spec.Provider.Source) { + continue + } + + if !host.Status.HasCondition(libcnd.Ready) { + continue + } + // it's not that great to have a vSphere-specific entity here but as we don't + // intend to do the same for other providers, doing it here for simplicity + m := &model.Host{} + pErr := r.Source.Inventory.Find(m, ref) + if pErr != nil { + if errors.As(pErr, &web.NotFoundError{}) { + continue + } else { + err = pErr + return + } + } + ref.ID = m.ID + ref.Name = m.Name + hostMap[ref.ID] = host + } + + hosts = hostMap + + return +} + func (r *Migration) resetPrecopyTasks(vm *plan.VMStatus, step *plan.Step) { step.Completed = nil for _, task := range step.Tasks { @@ -1716,7 +1764,7 @@ func (r *Migration) setDataVolumeCheckpoints(vm *plan.VMStatus) (err error) { for _, disk := range disks { dvs = append(dvs, *disk.DataVolume) } - err = r.provider.SetCheckpoints(vm.Ref, vm.Warm.Precopies, dvs, vm.Phase == AddFinalCheckpoint) + err = r.provider.SetCheckpoints(vm.Ref, vm.Warm.Precopies, dvs, vm.Phase == AddFinalCheckpoint, r.loadHosts) if err != nil { return } diff --git a/pkg/controller/plan/util/utils.go b/pkg/controller/plan/util/utils.go index dbafdf24c..ae1e8c335 100644 --- a/pkg/controller/plan/util/utils.go +++ b/pkg/controller/plan/util/utils.go @@ -3,6 +3,7 @@ package util import ( "math" + api "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" "github.com/konveyor/forklift-controller/pkg/settings" core "k8s.io/api/core/v1" ) @@ -31,3 +32,5 @@ func CalculateSpaceWithOverhead(requestedSpace int64, volumeMode *core.Persisten } return spaceWithOverhead } + +type HostsFunc func() (map[string]*api.Host, error)