diff --git a/pkg/apis/forklift/v1beta1/host.go b/pkg/apis/forklift/v1beta1/host.go index 1eb0597d4..81f7084e7 100644 --- a/pkg/apis/forklift/v1beta1/host.go +++ b/pkg/apis/forklift/v1beta1/host.go @@ -67,6 +67,8 @@ type HostList struct { Items []Host `json:"items"` } +type HostsFunc func() (map[string]*Host, error) + func init() { SchemeBuilder.Register(&Host{}, &HostList{}) } 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/doc.go b/pkg/controller/plan/adapter/base/doc.go index 395466171..9d66d035a 100644 --- a/pkg/controller/plan/adapter/base/doc.go +++ b/pkg/controller/plan/adapter/base/doc.go @@ -103,13 +103,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 api.HostsFunc) (string, error) // Remove all warm migration snapshots. - RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy) error + RemoveSnapshots(vmRef ref.Ref, precopies []planapi.Precopy, hostsFunc api.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 api.HostsFunc) (err error) // Close connections to the provider API. Close() // Finalize migrations diff --git a/pkg/controller/plan/adapter/ocp/client.go b/pkg/controller/plan/adapter/ocp/client.go index 44576f9d2..536356a6b 100644 --- a/pkg/controller/plan/adapter/ocp/client.go +++ b/pkg/controller/plan/adapter/ocp/client.go @@ -4,6 +4,7 @@ import ( "context" "time" + "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" @@ -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 v1beta1.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 v1beta1.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 v1beta1.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..9567c4651 100644 --- a/pkg/controller/plan/adapter/openstack/client.go +++ b/pkg/controller/plan/adapter/openstack/client.go @@ -5,6 +5,7 @@ import ( "strings" "time" + "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" @@ -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 v1beta1.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 v1beta1.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 v1beta1.HostsFunc) error { return nil } diff --git a/pkg/controller/plan/adapter/ova/client.go b/pkg/controller/plan/adapter/ova/client.go index b94ef9003..fda614f31 100644 --- a/pkg/controller/plan/adapter/ova/client.go +++ b/pkg/controller/plan/adapter/ova/client.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-logr/logr" + "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" @@ -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 v1beta1.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 v1beta1.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 v1beta1.HostsFunc) (err error) { return } diff --git a/pkg/controller/plan/adapter/ovirt/client.go b/pkg/controller/plan/adapter/ovirt/client.go index 5a2394ff7..db0e1d5f9 100644 --- a/pkg/controller/plan/adapter/ovirt/client.go +++ b/pkg/controller/plan/adapter/ovirt/client.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "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" @@ -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 v1beta1.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 v1beta1.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 v1beta1.HostsFunc) (err error) { n := len(precopies) previous := "" current := precopies[n-1].Snapshot diff --git a/pkg/controller/plan/adapter/vsphere/client.go b/pkg/controller/plan/adapter/vsphere/client.go index 6ad372a98..928dcf4f4 100644 --- a/pkg/controller/plan/adapter/vsphere/client.go +++ b/pkg/controller/plan/adapter/vsphere/client.go @@ -6,6 +6,7 @@ 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" @@ -19,7 +20,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" + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" ) @@ -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 v1beta1.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 v1beta1.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 v1beta1.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 v1beta1.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 v1beta1.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 v1beta1.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 v1beta1.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 eab03ea05..6b84823d9 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.", @@ -1200,6 +1201,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 { @@ -1715,7 +1763,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 }