diff --git a/pkg/controller/plan/adapter/base/doc.go b/pkg/controller/plan/adapter/base/doc.go index f468a8455..7a2d5395b 100644 --- a/pkg/controller/plan/adapter/base/doc.go +++ b/pkg/controller/plan/adapter/base/doc.go @@ -63,7 +63,7 @@ type Builder interface { // check whether the builder supports Volume Populators SupportsVolumePopulators() bool // Build populator volumes - PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcNames []string, err error) + PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) ([]*core.PersistentVolumeClaim, error) // Transferred bytes PopulatorTransferredBytes(persistentVolumeClaim *core.PersistentVolumeClaim) (transferredBytes int64, err error) // Set the populator PVC labels diff --git a/pkg/controller/plan/adapter/ocp/builder.go b/pkg/controller/plan/adapter/ocp/builder.go index 1c32d52de..ca0982fc0 100644 --- a/pkg/controller/plan/adapter/ocp/builder.go +++ b/pkg/controller/plan/adapter/ocp/builder.go @@ -604,7 +604,7 @@ func (r *Builder) SupportsVolumePopulators() bool { return false } -func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcNames []string, err error) { +func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcs []*core.PersistentVolumeClaim, err error) { err = planbase.VolumePopulatorNotSupportedError return } diff --git a/pkg/controller/plan/adapter/openstack/builder.go b/pkg/controller/plan/adapter/openstack/builder.go index 2bc5415bd..e36fa3afc 100644 --- a/pkg/controller/plan/adapter/openstack/builder.go +++ b/pkg/controller/plan/adapter/openstack/builder.go @@ -870,20 +870,18 @@ func (r *Builder) SupportsVolumePopulators() bool { return true } -func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcNames []string, err error) { +func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcs []*core.PersistentVolumeClaim, err error) { workload := &model.Workload{} err = r.Source.Inventory.Find(workload, vmRef) if err != nil { err = liberr.Wrap(err) return } - images, err := r.getImagesFromVolumes(workload) if err != nil { err = liberr.Wrap(err) return } - if workload.ImageID != "" { var image model.Image image, err = r.getVMSnapshotImage(workload) @@ -893,56 +891,64 @@ func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, } images = append(images, image) } - for _, image := range images { - if image.Status != string(ImageStatusActive) { r.Log.Info("the image is not ready yet", "image", image.Name) continue } - - originalVolumeDiskId := image.Name - if imageProperty, ok := image.Properties[forkliftPropertyOriginalVolumeID]; ok { - originalVolumeDiskId = imageProperty.(string) + if pvc, pvcErr := r.getCorrespondingPvc(image, workload, vmRef, annotations, secretName); pvcErr == nil { + pvcs = append(pvcs, pvc) + } else { + err = pvcErr + return } + } + return +} - _, err = r.getVolumePopulator(image.Name) - if err != nil { - if !k8serr.IsNotFound(err) { - err = liberr.Wrap(err) - return - } - } +func (r *Builder) getCorrespondingPvc(image model.Image, workload *model.Workload, vmRef ref.Ref, annotations map[string]string, secretName string) (pvc *core.PersistentVolumeClaim, err error) { + populatorCR, err := r.ensureVolumePopulator(workload, &image, secretName) + if err != nil { + return + } + return r.ensureVolumePopulatorPVC(workload, &image, annotations, populatorCR.Name) +} - var populatorName string - populatorName, err = r.createVolumePopulatorCR(image, secretName, vmRef.ID) - if err != nil { +func (r *Builder) ensureVolumePopulator(workload *model.Workload, image *model.Image, secretName string) (populatorCR *api.OpenstackVolumePopulator, err error) { + volumePopulatorCR, err := r.getVolumePopulatorCR(image.Name) + if err != nil { + if !k8serr.IsNotFound(err) { err = liberr.Wrap(err) return } + return r.createVolumePopulatorCR(*image, secretName, workload.ID) + } + populatorCR = &volumePopulatorCR + return +} +func (r *Builder) ensureVolumePopulatorPVC(workload *model.Workload, image *model.Image, annotations map[string]string, populatorName string) (pvc *core.PersistentVolumeClaim, err error) { + if pvc, err = r.getVolumePopulatorPVC(image.ID); err != nil { + if !k8serr.IsNotFound(err) { + err = liberr.Wrap(err) + return + } + originalVolumeDiskId := image.Name + if imageProperty, ok := image.Properties[forkliftPropertyOriginalVolumeID]; ok { + originalVolumeDiskId = imageProperty.(string) + } storageClassName := r.Context.Map.Storage.Spec.Map[0].Destination.StorageClass - volumeType := r.getVolumeType(workload, originalVolumeDiskId) - if volumeType != "" { + if volumeType := r.getVolumeType(workload, originalVolumeDiskId); volumeType != "" { storageClassName, err = r.getStorageClassName(workload, volumeType) if err != nil { err = liberr.Wrap(err) return } } - - var pvc *core.PersistentVolumeClaim - pvc, err = r.persistentVolumeClaimWithSourceRef(image, storageClassName, populatorName, annotations) - if err != nil { - if !k8serr.IsAlreadyExists(err) { - err = liberr.Wrap(err, "couldn't build the PVC", - "image", image.Name, "storageClassName", storageClassName, "populatorName", populatorName) - return - } - err = nil - continue + if pvc, err = r.persistentVolumeClaimWithSourceRef(*image, storageClassName, populatorName, annotations); err != nil { + err = liberr.Wrap(err) + return } - pvcNames = append(pvcNames, pvc.Name) } return } @@ -989,8 +995,8 @@ func (r *Builder) getImagesFromVolumes(workload *model.Workload) (images []model return } -func (r *Builder) createVolumePopulatorCR(image model.Image, secretName, vmId string) (name string, err error) { - populatorCR := &api.OpenstackVolumePopulator{ +func (r *Builder) createVolumePopulatorCR(image model.Image, secretName, vmId string) (populatorCR *api.OpenstackVolumePopulator, err error) { + populatorCR = &api.OpenstackVolumePopulator{ ObjectMeta: meta.ObjectMeta{ Name: image.Name, Namespace: r.Plan.Spec.TargetNamespace, @@ -1005,14 +1011,9 @@ func (r *Builder) createVolumePopulatorCR(image model.Image, secretName, vmId st } err = r.Context.Client.Create(context.TODO(), populatorCR, &client.CreateOptions{}) if err != nil { - if !k8serr.IsAlreadyExists(err) { - err = liberr.Wrap(err) - return - } else { - err = nil - } + err = liberr.Wrap(err) + return } - name = populatorCR.Name return } @@ -1077,12 +1078,18 @@ func (r *Builder) getVolumeAndAccessMode(storageClassName string) ([]core.Persis } // Get the OpenstackVolumePopulator CustomResource based on the image name. -func (r *Builder) getVolumePopulator(name string) (populatorCr api.OpenstackVolumePopulator, err error) { +func (r *Builder) getVolumePopulatorCR(name string) (populatorCr api.OpenstackVolumePopulator, err error) { populatorCr = api.OpenstackVolumePopulator{} err = r.Destination.Client.Get(context.TODO(), client.ObjectKey{Namespace: r.Plan.Spec.TargetNamespace, Name: name}, &populatorCr) return } +func (r *Builder) getVolumePopulatorPVC(name string) (populatorPvc *core.PersistentVolumeClaim, err error) { + populatorPvc = &core.PersistentVolumeClaim{} + err = r.Destination.Client.Get(context.TODO(), client.ObjectKey{Namespace: r.Plan.Spec.TargetNamespace, Name: name}, populatorPvc) + return +} + func (r *Builder) persistentVolumeClaimWithSourceRef(image model.Image, storageClassName string, populatorName string, annotations map[string]string) (pvc *core.PersistentVolumeClaim, err error) { @@ -1134,6 +1141,9 @@ func (r *Builder) persistentVolumeClaimWithSourceRef(image model.Image, storageC } err = r.Client.Create(context.TODO(), pvc, &client.CreateOptions{}) + if err != nil { + err = liberr.Wrap(err) + } return } @@ -1142,7 +1152,7 @@ func (r *Builder) PopulatorTransferredBytes(persistentVolumeClaim *core.Persiste if err != nil { return } - populatorCr, err := r.getVolumePopulator(image.Name) + populatorCr, err := r.getVolumePopulatorCR(image.Name) if err != nil { return } @@ -1194,7 +1204,7 @@ func (r *Builder) SetPopulatorDataSourceLabels(vmRef ref.Ref, pvcs []core.Persis } migrationID := string(r.Plan.Status.Migration.ActiveSnapshot().Migration.UID) for _, image := range images { - populatorCr, err := r.getVolumePopulator(image.Name) + populatorCr, err := r.getVolumePopulatorCR(image.Name) if err != nil { continue } diff --git a/pkg/controller/plan/adapter/openstack/client.go b/pkg/controller/plan/adapter/openstack/client.go index 2c4ccdbfb..1a02a25ee 100644 --- a/pkg/controller/plan/adapter/openstack/client.go +++ b/pkg/controller/plan/adapter/openstack/client.go @@ -192,226 +192,19 @@ func (r *Client) PreTransferActions(vmRef ref.Ref) (ready bool, err error) { vmRef.String()) return } - // VM Snapshot - vmSnapshotImage, err := r.getVmSnapshotImage(vm) - if err != nil { - if !errors.Is(err, ResourceNotFoundError) { - err = liberr.Wrap(err) - r.Log.Error(err, "trying to retrieve the VM snapshot image info", - "vm", vm.Name) - return - } - r.Log.Info("creating the VM snapshot image", "vm", vm.Name) - vmSnapshotImage, err = r.createVmSnapshotImage(vm) - if err != nil { - err = liberr.Wrap(err) - r.Log.Error(err, "trying to create the VM snapshot image", - "vm", vm.Name) - return - } - } - switch vmSnapshotImage.Status { - case ImageStatusActive: - r.Log.Info("the VM snapshot image is ready!", - "vm", vm.Name, "image", vmSnapshotImage.Name, "imageID", vmSnapshotImage.ID) - case ImageStatusImporting, ImageStatusQueued, ImageStatusUploading, ImageStatusSaving: - r.Log.Info("the VM snapshot image is not ready yet, skipping...", - "vm", vm.Name, "image", vmSnapshotImage.Name, "imageID", vmSnapshotImage.ID) - return - default: - err = liberr.New("unexpected VM snapshot image status") - r.Log.Error(err, "checking the VM snapshot image", - "vm", vm.Name, "image", vmSnapshotImage.Name, "imageID", vmSnapshotImage.ID, "status", vmSnapshotImage.Status) + ready, err = r.ensureVmSnapshot(vm) + if err != nil || !ready { return } - // Images from VM Volumes - var imagesFromVolumes []libclient.Image - imagesFromVolumes, err = r.getImagesFromVolumes(vm) - if err != nil { - err = liberr.Wrap(err) - r.Log.Error(err, "error while trying to get the images from the VM volumes", - "vm", vm.Name) + ready, err = r.ensureImagesFromVolumesReady(vm) + if err != nil || ready { return } - imagesFromVolumesMap := map[string]string{} - for _, image := range imagesFromVolumes { - imagesFromVolumesMap[image.ID] = image.Name - } - r.Log.Info("the images from volumes are", - "vm", vm.Name, "images", imagesFromVolumesMap) - - ready = true - for _, image := range imagesFromVolumes { - switch image.Status { - case ImageStatusQueued, ImageStatusUploading, ImageStatusSaving: - r.Log.Info("the image is still being processed", - "vm", vm.Name, "image", image.Name, "status", image.Status) - ready = false - case ImageStatusActive: - err = r.updateImageProperty(vm, &image) - if err != nil { - return - } - r.Log.Info("the image properties have been updated", - "vm", vm.Name, "image", image.Name, "properties", image.Properties) - inventoryImage := &model.Image{} - err = r.Context.Source.Inventory.Find(inventoryImage, ref.Ref{ID: image.ID}) - if err != nil { - if !errors.As(err, &model.NotFoundError{}) { - return - } - ready = false - err = nil - r.Log.Info("the image does not exist in the inventory, waiting...", - "vm", vm.Name, "image", image.Name, "properties", image.Properties) - continue - } - if _, ok := inventoryImage.Properties[forkliftPropertyOriginalVolumeID]; !ok { - r.Log.Info("image properties have not been synchronized, waiting...", - "vm", vm.Name, "image", inventoryImage.Name, "properties", inventoryImage.Properties) - ready = false - continue - } - r.Log.Info("the image properties are in sync, cleaning the image", - "vm", vm.Name, "image", inventoryImage.Name, "properties", inventoryImage.Properties) - originalVolumeID := inventoryImage.Properties[forkliftPropertyOriginalVolumeID].(string) - go func() { - err := r.cleanup(vm, originalVolumeID) - if err != nil { - r.Log.Error(err, "failed to cleanup") - } - }() - - default: - err = liberr.New("unexpected image status") - r.Log.Error(err, "checking the image from volume", - "vm", vm.Name, "image", image.Name, "status", image.Status) - } - } - if len(vm.AttachedVolumes) != len(imagesFromVolumes) { - r.Log.Info("not all the images have been created", - "vm", vm.Name, "images", imagesFromVolumesMap, "attachedVolumes", vm.AttachedVolumes) - ready = false - } - if ready { - r.Log.Info("all steps finished!", "vm", vm.Name) - return - } - // Snapshots from VM Volumes - var snapshotsFromVolumes []libclient.Snapshot - snapshotsFromVolumes, err = r.getSnapshotsFromVolumes(vm) - if err != nil { - err = liberr.Wrap(err) - return - } - snapshotsFromVolumesMap := map[string]string{} - for _, snapshot := range snapshotsFromVolumes { - snapshotsFromVolumesMap[snapshot.ID] = snapshot.VolumeID - } - r.Log.Info("the snapshots from volumes are", - "vm", vm.Name, "snapshots", snapshotsFromVolumesMap) - - for _, snapshot := range snapshotsFromVolumes { - switch snapshot.Status { - case SnapshotStatusCreating: - r.Log.Info("the snapshot is still being created, skipping...", - "vm", vm.Name, "snapshot", snapshot.Name) - case SnapshotStatusAvailable: - _, err = r.getVolumeFromSnapshot(vm, snapshot.ID) - if err != nil { - if !errors.Is(err, ResourceNotFoundError) { - err = liberr.Wrap(err) - r.Log.Error(err, "trying to get the snapshot info from the volume VM snapshot", - "vm", vm.Name, "snapshot", snapshot.Name) - return - } - imageName := getImageFromVolumeName(r.Context, vm.ID, snapshot.VolumeID) - var image *libclient.Image - image, err = r.getImage(ref.Ref{Name: imageName}) - if err == nil { - r.Log.Info("skipping the snapshot creation, the image already exists", - "vm", vm.Name, "snapshot", snapshot.Name) - continue - } else { - if !errors.Is(err, ResourceNotFoundError) { - err = liberr.Wrap(err) - r.Log.Error(err, "trying to get the image info from the snapshot", - "vm", vm.Name, "image", image.Name) - return - } - r.Log.Info("creating the volume from snapshot", - "vm", vm.Name, "snapshot", snapshot.Name) - _, err = r.createVolumeFromSnapshot(vm, snapshot.ID) - if err != nil { - err = liberr.Wrap(err) - r.Log.Error(err, "trying to create a volume from the VM snapshot", - "vm", vm.Name, "snapshot", snapshot.Name) - return - - } - } - } - case SnapshotStatusDeleted, SnapshotStatusDeleting: - r.Log.Info("the snapshot is being deleted, skipping...", - "vm", vm.Name, "snapshot", snapshot.Name) - default: - err = liberr.New("unexpected snapshot status") - r.Log.Error(err, "checking the snapshot", - "vm", vm.Name, "snapshot", snapshot.Name, "status", snapshot.Status) - return - } - } - // Volumes from VM Snapshots - var volumesFromSnapshots []libclient.Volume - volumesFromSnapshots, err = r.getVolumesFromSnapshots(vm) + err = r.ensureSnapshotsFromVolumes(vm) if err != nil { - err = liberr.Wrap(err) return } - volumesFromSnapshotsMap := map[string]string{} - for _, volume := range volumesFromSnapshots { - volumesFromSnapshotsMap[volume.ID] = volume.SnapshotID - } - r.Log.Info("the volumes from snapshots are", - "vm", vm.Name, "snapshots", volumesFromSnapshotsMap) - - for _, volume := range volumesFromSnapshots { - switch volume.Status { - case VolumeStatusCreating: - r.Log.Info("the volume is still being created", - "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) - case VolumeStatusUploading: - r.Log.Info("the volume is still uploading to the image, skipping...", - "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) - case VolumeStatusAvailable: - _, err = r.getImageFromVolume(vm, volume.ID) - if err != nil { - if !errors.Is(err, ResourceNotFoundError) { - err = liberr.Wrap(err) - r.Log.Error(err, "while trying to get the image from the volume", - "vm", vm.Name, "volume", volume.Name, "snaphsot", volume.SnapshotID) - return - } - r.Log.Info("creating the image from the volume", - "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) - _, err = r.createImageFromVolume(vm, volume.ID) - if err != nil { - err = liberr.Wrap(err) - r.Log.Error(err, "while trying to create the image from the volume", - "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) - return - } - } - case VolumeStatusDeleting: - r.Log.Info("the volume is being deleted", - "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) - default: - err = UnexpectedVolumeStatusError - r.Log.Error(err, "checking the volume", - "vm", vm.Name, "volume", volume.Name, "status", volume.Status) - return - } - } + err = r.ensureVolumesFromSnapshots(vm) return } @@ -979,3 +772,237 @@ func (r *Client) getImagesFromVolumes(vm *libclient.VM) (images []libclient.Imag } return } + +func (r *Client) ensureVmSnapshot(vm *libclient.VM) (ready bool, err error) { + vmSnapshotImage, err := r.getVmSnapshotImage(vm) + if err != nil { + if !errors.Is(err, ResourceNotFoundError) { + err = liberr.Wrap(err) + r.Log.Error(err, "trying to retrieve the VM snapshot image info", + "vm", vm.Name) + return + } + r.Log.Info("creating the VM snapshot image", "vm", vm.Name) + vmSnapshotImage, err = r.createVmSnapshotImage(vm) + if err != nil { + err = liberr.Wrap(err) + r.Log.Error(err, "trying to create the VM snapshot image", + "vm", vm.Name) + return + } + } + switch vmSnapshotImage.Status { + case ImageStatusActive: + r.Log.Info("the VM snapshot image is ready!", + "vm", vm.Name, "image", vmSnapshotImage.Name, "imageID", vmSnapshotImage.ID) + ready = true + case ImageStatusImporting, ImageStatusQueued, ImageStatusUploading, ImageStatusSaving: + r.Log.Info("the VM snapshot image is not ready yet, skipping...", + "vm", vm.Name, "image", vmSnapshotImage.Name, "imageID", vmSnapshotImage.ID) + return + default: + err = liberr.New("unexpected VM snapshot image status") + r.Log.Error(err, "checking the VM snapshot image", + "vm", vm.Name, "image", vmSnapshotImage.Name, "imageID", vmSnapshotImage.ID, "status", vmSnapshotImage.Status) + return + } + return +} + +func (r *Client) ensureImagesFromVolumesReady(vm *libclient.VM) (ready bool, err error) { + var imagesFromVolumes []libclient.Image + if imagesFromVolumes, err = r.getImagesFromVolumes(vm); err != nil { + err = liberr.Wrap(err) + r.Log.Error(err, "error while trying to get the images from the VM volumes", + "vm", vm.Name) + return + } + if len(vm.AttachedVolumes) != len(imagesFromVolumes) { + r.Log.Info("not all the images have been created", + "vm", vm.Name, "attachedVolumes", vm.AttachedVolumes, "imagesFromVolumes", imagesFromVolumes) + return + } + for _, image := range imagesFromVolumes { + imageReady, imageReadyErr := r.ensureImageFromVolumeReady(vm, &image) + switch { + case imageReadyErr != nil: + err = liberr.Wrap(imageReadyErr) + return + case !imageReady: + r.Log.Info("found an image that is not ready", + "vm", vm.Name, "image", image.Name) + return + case imageReady: + originalVolumeID := image.Properties[forkliftPropertyOriginalVolumeID].(string) + go func() { + // executing this in a non-blocking mode + err := r.cleanup(vm, originalVolumeID) + if err != nil { + r.Log.Error(err, "failed to cleanup snapshot and volume", + "vm", vm.Name, "volumeId", originalVolumeID) + } + }() + } + } + ready = true + r.Log.Info("all steps finished!", "vm", vm.Name) + return +} + +func (r *Client) ensureImageFromVolumeReady(vm *libclient.VM, image *libclient.Image) (ready bool, err error) { + switch image.Status { + case ImageStatusQueued, ImageStatusUploading, ImageStatusSaving: + r.Log.Info("the image is still being processed", + "vm", vm.Name, "image", image.Name, "status", image.Status) + case ImageStatusActive: + err = r.updateImageProperty(vm, image) + if err != nil { + return + } + r.Log.Info("the image properties have been updated", + "vm", vm.Name, "image", image.Name, "properties", image.Properties) + var imageUpToDate bool + imageUpToDate, err = r.ensureImageUpToDate(vm, image) + if err != nil || !imageUpToDate { + return + } + r.Log.Info("the image properties are in sync, cleaning the image", + "vm", vm.Name, "image", image.Name, "properties", image.Properties) + ready = true + default: + err = liberr.New("unexpected image status") + r.Log.Error(err, "checking the image from volume", + "vm", vm.Name, "image", image.Name, "status", image.Status) + } + return +} + +func (r *Client) ensureImageUpToDate(vm *libclient.VM, image *libclient.Image) (upToDate bool, err error) { + inventoryImage := &model.Image{} + if err = r.Context.Source.Inventory.Find(inventoryImage, ref.Ref{ID: image.ID}); err != nil { + if errors.As(err, &model.NotFoundError{}) { + err = nil + r.Log.Info("the image does not exist in the inventory, waiting...", + "vm", vm.Name, "image", image.Name, "properties", image.Properties) + } + return + } + if _, upToDate = inventoryImage.Properties[forkliftPropertyOriginalVolumeID]; !upToDate { + r.Log.Info("image properties have not been synchronized, waiting...", + "vm", vm.Name, "image", inventoryImage.Name, "properties", inventoryImage.Properties) + } + return + +} + +func (r *Client) ensureSnapshotsFromVolumes(vm *libclient.VM) (err error) { + var snapshotsFromVolumes []libclient.Snapshot + if snapshotsFromVolumes, err = r.getSnapshotsFromVolumes(vm); err != nil { + err = liberr.Wrap(err) + return + } + for _, snapshot := range snapshotsFromVolumes { + switch snapshot.Status { + case SnapshotStatusCreating: + r.Log.Info("the snapshot is still being created, skipping...", + "vm", vm.Name, "snapshot", snapshot.Name) + case SnapshotStatusAvailable: + err = r.ensureVolumeFromSnapshot(vm, &snapshot) + case SnapshotStatusDeleted, SnapshotStatusDeleting: + r.Log.Info("the snapshot is being deleted, skipping...", + "vm", vm.Name, "snapshot", snapshot.Name) + default: + err = liberr.New("unexpected snapshot status") + r.Log.Error(err, "checking the snapshot", + "vm", vm.Name, "snapshot", snapshot.Name, "status", snapshot.Status) + return + } + } + return +} + +func (r *Client) ensureVolumeFromSnapshot(vm *libclient.VM, snapshot *libclient.Snapshot) (err error) { + if _, err = r.getVolumeFromSnapshot(vm, snapshot.ID); err != nil { + if !errors.Is(err, ResourceNotFoundError) { + err = liberr.Wrap(err) + r.Log.Error(err, "trying to get the snapshot info from the volume VM snapshot", + "vm", vm.Name, "snapshot", snapshot.Name) + return + } + imageName := getImageFromVolumeName(r.Context, vm.ID, snapshot.VolumeID) + var image *libclient.Image + image, err = r.getImage(ref.Ref{Name: imageName}) + if err == nil { + r.Log.Info("skipping the snapshot creation, the image already exists", + "vm", vm.Name, "snapshot", snapshot.Name) + } else { + if !errors.Is(err, ResourceNotFoundError) { + err = liberr.Wrap(err) + r.Log.Error(err, "trying to get the image info from the snapshot", + "vm", vm.Name, "image", image.Name) + return + } + r.Log.Info("creating the volume from snapshot", + "vm", vm.Name, "snapshot", snapshot.Name) + _, err = r.createVolumeFromSnapshot(vm, snapshot.ID) + if err != nil { + err = liberr.Wrap(err) + r.Log.Error(err, "trying to create a volume from the VM snapshot", + "vm", vm.Name, "snapshot", snapshot.Name) + return + + } + } + } + return +} + +func (r *Client) ensureVolumesFromSnapshots(vm *libclient.VM) (err error) { + var volumesFromSnapshots []libclient.Volume + if volumesFromSnapshots, err = r.getVolumesFromSnapshots(vm); err != nil { + err = liberr.Wrap(err) + return + } + for _, volume := range volumesFromSnapshots { + switch volume.Status { + case VolumeStatusCreating: + r.Log.Info("the volume is still being created", + "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) + case VolumeStatusUploading: + r.Log.Info("the volume is still uploading to the image, skipping...", + "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) + case VolumeStatusAvailable: + err = r.ensureImageFromVolume(vm, &volume) + case VolumeStatusDeleting: + r.Log.Info("the volume is being deleted", + "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) + default: + err = UnexpectedVolumeStatusError + r.Log.Error(err, "checking the volume", + "vm", vm.Name, "volume", volume.Name, "status", volume.Status) + return + } + } + return +} + +func (r *Client) ensureImageFromVolume(vm *libclient.VM, volume *libclient.Volume) (err error) { + if _, err = r.getImageFromVolume(vm, volume.ID); err != nil { + if !errors.Is(err, ResourceNotFoundError) { + err = liberr.Wrap(err) + r.Log.Error(err, "while trying to get the image from the volume", + "vm", vm.Name, "volume", volume.Name, "snaphsot", volume.SnapshotID) + return + } + r.Log.Info("creating the image from the volume", + "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) + _, err = r.createImageFromVolume(vm, volume.ID) + if err != nil { + err = liberr.Wrap(err) + r.Log.Error(err, "while trying to create the image from the volume", + "vm", vm.Name, "volume", volume.Name, "snapshot", volume.SnapshotID) + return + } + } + return +} diff --git a/pkg/controller/plan/adapter/ova/builder.go b/pkg/controller/plan/adapter/ova/builder.go index e33482d72..5c6b84dd3 100644 --- a/pkg/controller/plan/adapter/ova/builder.go +++ b/pkg/controller/plan/adapter/ova/builder.go @@ -539,7 +539,7 @@ func (r *Builder) SupportsVolumePopulators() bool { return false } -func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcNames []string, err error) { +func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcs []*core.PersistentVolumeClaim, err error) { err = planbase.VolumePopulatorNotSupportedError return } diff --git a/pkg/controller/plan/adapter/ovirt/builder.go b/pkg/controller/plan/adapter/ovirt/builder.go index fa43bd12e..3e9ba26cc 100644 --- a/pkg/controller/plan/adapter/ovirt/builder.go +++ b/pkg/controller/plan/adapter/ovirt/builder.go @@ -701,7 +701,7 @@ func (r *Builder) SupportsVolumePopulators() bool { return !r.Context.Plan.Spec.Warm && r.Context.Plan.Provider.Destination.IsHost() } -func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcNames []string, err error) { +func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcs []*core.PersistentVolumeClaim, err error) { workload := &model.Workload{} err = r.Source.Inventory.Find(workload, vmRef) if err != nil { @@ -737,7 +737,7 @@ func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, err = nil continue } - pvcNames = append(pvcNames, pvc.Name) + pvcs = append(pvcs, pvc) } } return diff --git a/pkg/controller/plan/adapter/vsphere/builder.go b/pkg/controller/plan/adapter/vsphere/builder.go index 6ab8461d2..77240d5da 100644 --- a/pkg/controller/plan/adapter/vsphere/builder.go +++ b/pkg/controller/plan/adapter/vsphere/builder.go @@ -835,7 +835,7 @@ func (r *Builder) SupportsVolumePopulators() bool { return false } -func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcNames []string, err error) { +func (r *Builder) PopulatorVolumes(vmRef ref.Ref, annotations map[string]string, secretName string) (pvcs []*core.PersistentVolumeClaim, err error) { err = planbase.VolumePopulatorNotSupportedError return } diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 84603c7e0..1784d0bac 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -471,7 +471,7 @@ func (r *KubeVirt) DataVolumes(vm *plan.VMStatus) (dataVolumes []cdi.DataVolume, return } -func (r *KubeVirt) PopulatorVolumes(vmRef ref.Ref) (pvcNames []string, err error) { +func (r *KubeVirt) PopulatorVolumes(vmRef ref.Ref) (pvcs []*core.PersistentVolumeClaim, err error) { secret, err := r.ensureSecret(vmRef, r.copyDataFromProviderSecret) if err != nil { err = liberr.Wrap(err) @@ -535,8 +535,14 @@ func (r *KubeVirt) EnsureDataVolumes(vm *plan.VMStatus, dataVolumes []cdi.DataVo return } -func (r *KubeVirt) EnsurePopulatorVolumes(vm *plan.VMStatus, pvcNames []string) (err error) { - err = r.createPodToBindPVCs(vm, pvcNames) +func (r *KubeVirt) EnsurePopulatorVolumes(vm *plan.VMStatus, pvcs []*core.PersistentVolumeClaim) (err error) { + var pendingPvcNames []string + for _, pvc := range pvcs { + if pvc.Status.Phase == core.ClaimPending { + pendingPvcNames = append(pendingPvcNames, pvc.Name) + } + } + err = r.createPodToBindPVCs(vm, pendingPvcNames) if err != nil { err = liberr.Wrap(err) } @@ -2058,7 +2064,7 @@ func (r *KubeVirt) CreatePvcForNfs(pvcName string) (err error) { r.Log.Error(err, "Failed to get OVA plan PVC") return false, err } - return pvc.Status.Phase == "Bound", nil + return pvc.Status.Phase == core.ClaimBound, nil }); err != nil { r.Log.Error(err, "Failed to bind OVA PVC to PV ") diff --git a/pkg/controller/plan/migration.go b/pkg/controller/plan/migration.go index 845dab646..b76a1e28f 100644 --- a/pkg/controller/plan/migration.go +++ b/pkg/controller/plan/migration.go @@ -700,9 +700,8 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { } if r.builder.SupportsVolumePopulators() { - var pvcNames []string - pvcNames, err = r.kubevirt.PopulatorVolumes(vm.Ref) - if err != nil { + var pvcs []*core.PersistentVolumeClaim + if pvcs, err = r.kubevirt.PopulatorVolumes(vm.Ref); err != nil { if !errors.As(err, &web.ProviderNotReadyError{}) { r.Log.Error(err, "error creating volumes", "vm", vm.Name) step.AddError(err.Error()) @@ -712,7 +711,7 @@ func (r *Migration) execute(vm *plan.VMStatus) (err error) { return } } - err = r.kubevirt.EnsurePopulatorVolumes(vm, pvcNames) + err = r.kubevirt.EnsurePopulatorVolumes(vm, pvcs) if err != nil { if !errors.As(err, &web.ProviderNotReadyError{}) { step.AddError(err.Error())