---
# Forklift/MTV Post-Migration Hook Playbook
# This playbook runs inside the hook-runner container created by MTV
# It converts FA LUN (FADA volumes) to PXD volumes after VM migration

- name: FA LUN to PXD Volume Conversion - Main
  hosts: localhost
  connection: local
  gather_facts: no

  vars:
    # ============================================================================
    # Configuration Variables
    # ============================================================================

    # Forklift namespace - derived from plan at runtime, overridable here if needed
    forklift_namespace: "{{ plan.map.storage.namespace | default('openshift-mtv') }}"

    # Converter Image
    converter_image: "docker.io/portworx/fa-pxd-converter:v0.0.1"
    fa_api_version: "2.9"

    # Conversion Options for FADA->PXD copy
    xcopy_jobs: 512
    poke_workers: 16
    stats_interval: 30


    # Forklift API
    forklift_api_group: "forklift.konveyor.io"

  tasks:
    # Step 1: Load MTV-provided variables
    - name: Load Plan variables
      include_vars:
        file: plan.yml
        name: plan

    - name: Load Workload variables
      include_vars:
        file: workload.yml
        name: workload

    - name: Debug - Display workload.yml contents
      debug:
        var: workload

    - name: Debug - Display plan.yml contents
      debug:
        var: plan

    # Step 2: Extract migration information
    # MTV creates one hook pod per VM, each with its own workload.yml
    - name: Set migration facts from plan and workload
      set_fact:
        migration_namespace: "{{ plan.targetnamespace }}"
        vm_name: "{{ workload.vm.vm1.vm0.name }}"
        vm_id: "{{ workload.vm.vm1.vm0.id }}"

    # Step 3: Get PVCs for this specific VM
    - name: Get all PVCs in migration namespace
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolumeClaim
        namespace: "{{ migration_namespace }}"
      register: all_pvcs

    - name: Debug - Display workload information
      debug:
        msg: |-
          Workload VM ID: {{ vm_id }}
          Workload VM Name: {{ vm_name }}
          Migration Namespace: {{ migration_namespace }}
          Total PVCs found in namespace: {{ all_pvcs.resources | length }}

    - name: Filter PVCs with Forklift dataSource for this VM
      set_fact:
        vm_pvcs: "{{ all_pvcs.resources |
                     selectattr('spec.dataSource', 'defined') |
                     selectattr('spec.dataSource.apiGroup', 'defined') |
                     selectattr('spec.dataSource.apiGroup', 'equalto', forklift_api_group) |
                     selectattr('metadata.labels.vmID', 'defined') |
                     selectattr('metadata.labels.vmID', 'equalto', vm_id) |
                     selectattr('status.phase', 'equalto', 'Bound') |
                     rejectattr('metadata.deletionTimestamp', 'defined') |
                     list }}"

    - name: Display found PVCs
      debug:
        msg: "Found {{ vm_pvcs | length }} Bound Forklift PVC(s) for VM {{ vm_name }} (vmID: {{ vm_id }})"

    - name: Fail if no PVCs found
      fail:
        msg: "No Bound Forklift PVCs found for VM {{ vm_name }} (vmID: {{ vm_id }})"
      when: vm_pvcs | length == 0

    # Step 4: Derive FA configuration from Forklift StorageMap secret, per PVC
    - name: Get StorageMap referenced by the Plan
      kubernetes.core.k8s_info:
        api_version: forklift.konveyor.io/v1beta1
        kind: StorageMap
        name: "{{ plan.map.storage.name }}"
        namespace: "{{ forklift_namespace }}"
      register: storage_map_info

    - name: Fail if StorageMap not found
      fail:
        msg: "StorageMap '{{ plan.map.storage.name }}' not found"
      when: storage_map_info.resources | length == 0

    - name: Build datastore name-to-ID lookup from StorageMap status.references
      set_fact:
        datastore_name_to_id: "{{ dict(storage_map_info.resources[0].status.references | map(attribute='name') | zip(storage_map_info.resources[0].status.references | map(attribute='id'))) }}"

    - name: Display datastore name-to-ID lookup
      debug:
        msg: "Datastore lookup: {{ datastore_name_to_id }}"

    - name: Initialize per-PVC FA config dict
      set_fact:
        pvc_fa_config: {}

    - name: Get offload secret for each PVC
      vars:
        pvc_datastore: "{{ item.metadata.annotations['forklift.konveyor.io/disk-source'] | regex_search('\\[([^\\]]+)\\]', '\\1') | first }}"
        pvc_datastore_id: "{{ datastore_name_to_id[pvc_datastore] }}"
        storage_entry: "{{ storage_map_info.resources[0].spec.map | selectattr('source.id', 'equalto', pvc_datastore_id) | first }}"
      kubernetes.core.k8s_info:
        api_version: v1
        kind: Secret
        name: "{{ storage_entry.offloadPlugin.vsphereXcopyConfig.secretRef }}"
        namespace: "{{ forklift_namespace }}"
      register: pvc_secret_results
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"

    - name: Fail if any offload secret not found
      fail:
        msg: "Secret for PVC '{{ item.item.metadata.name }}' not found"
      loop: "{{ pvc_secret_results.results }}"
      loop_control:
        label: "{{ item.item.metadata.name }}"
      when: item.resources | length == 0

    - name: Build per-PVC FA config dict
      vars:
        secret_data: "{{ item.resources[0].data }}"
        pvc_name: "{{ item.item.metadata.name }}"
        pvc_config:
          fa_endpoint: "{{ secret_data.STORAGE_HOSTNAME | b64decode }}"
          fa_api_token: "{{ secret_data.STORAGE_TOKEN | b64decode }}"
          pxd_storage_class: "{{ secret_data.PXD_STORAGE_CLASS | b64decode }}"
      set_fact:
        pvc_fa_config: "{{ pvc_fa_config | combine({pvc_name: pvc_config}) }}"
      loop: "{{ pvc_secret_results.results }}"
      loop_control:
        label: "{{ item.item.metadata.name }}"

    - name: Display derived per-PVC FA configuration
      debug:
        msg: |-
          PVC: {{ item.key }}
          FlashArray Endpoint: {{ item.value.fa_endpoint }}
          FlashArray API Version: {{ fa_api_version }}
          PXD Storage Class: {{ item.value.pxd_storage_class }}
      loop: "{{ pvc_fa_config | dict2items }}"
      loop_control:
        label: "{{ item.key }}"

    - name: Display hook execution information
      debug:
        msg: |-
          ========================================
          Forklift Post-Migration Hook: FA to PXD Conversion
          ========================================
          VM Name: {{ vm_name }}
          VM ID: {{ vm_id }}
          Target Namespace: {{ migration_namespace }}
          Converter Image: {{ converter_image }}
          XCOPY Jobs: {{ xcopy_jobs }}
          Poke Workers: {{ poke_workers }}
          Stats Interval: {{ stats_interval }}s
          ========================================

    - name: Debug - Display all PVC details
      debug:
        msg: |-
          PVC: {{ item.metadata.name }}
          StorageClass: {{ item.spec.storageClassName }}
          vmID Label: {{ item.metadata.labels.vmID | default('NOT SET') }}
          Phase: {{ item.status.phase }}
          DataSource: {{ item.spec.dataSource.kind | default('NONE') }}
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    - name: Display PVC to PV mappings
      debug:
        msg: "PVC: {{ item.metadata.name }} -> PV: {{ item.spec.volumeName }}"
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    # Step 5: Create destination PXD PVCs for each source FA PVC
    # Note: RBAC resources (ServiceAccount, ClusterRoleBinding) must be pre-created
    # by cluster admin using: kubectl apply -f kubernetes-resources.yaml
    - name: Create destination PXD PVC for each source FA PVC
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: PersistentVolumeClaim
          metadata:
            name: "pxd-{{ item.metadata.name }}"
            namespace: "{{ migration_namespace }}"
            labels:
              source-pvc: "{{ item.metadata.name }}"
              vm: "{{ vm_name }}"
          spec:
            accessModes:
              - ReadWriteMany
            storageClassName: "{{ pvc_fa_config[item.metadata.name].pxd_storage_class }}"
            volumeMode: Block
            resources:
              requests:
                storage: "{{ item.spec.resources.requests.storage }}"
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    # Step 5b: Wait for PXD PVCs to bind and immediately set Retain policy, in case StorageClass has reclaimPolicy: Delete
    # We must set Retain BEFORE any deletion operations to prevent data loss
    - name: Wait for PXD PVC to bind
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "pxd-{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      register: pxd_pvc_status
      until:
        - pxd_pvc_status.resources | length > 0
        - pxd_pvc_status.resources[0].status.phase == "Bound"
      retries: 60
      delay: 5
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    - name: Set PXD PV to Retain reclaim policy immediately
      kubernetes.core.k8s:
        api_version: v1
        kind: PersistentVolume
        name: "{{ item.resources[0].spec.volumeName }}"
        definition:
          spec:
            persistentVolumeReclaimPolicy: Retain
      loop: "{{ pxd_pvc_status.results }}"
      loop_control:
        label: "{{ item.resources[0].spec.volumeName }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    - name: Verify Retain policy was set
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolume
        name: "{{ item.resources[0].spec.volumeName }}"
      register: pv_verify
      failed_when: pv_verify.resources[0].spec.persistentVolumeReclaimPolicy != "Retain"
      loop: "{{ pxd_pvc_status.results }}"
      loop_control:
        label: "{{ item.resources[0].spec.volumeName }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    - name: Wait for destination PXD PVCs to be Bound
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "pxd-{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0
      register: dest_pvc_status
      until: dest_pvc_status.resources[0].status.phase == "Bound"
      retries: 30
      delay: 10

    - name: Display destination PXD PVC to PV mappings
      debug:
        msg: "Source PVC: {{ item.metadata.name }} -> Destination PXD PVC: pxd-{{ item.metadata.name }}"
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    # Before deleting FA PVCs, we need to stop the VM (if running) to release the PVCs
    - name: Get VirtualMachine object
      kubernetes.core.k8s_info:
        api_version: kubevirt.io/v1
        kind: VirtualMachine
        name: "{{ vm_name }}"
        namespace: "{{ migration_namespace }}"
      register: vm_object
      when: vm_pvcs | length > 0

    # Step 6: VM State Check
    - name: Save VM running state
      set_fact:
        vm_was_running: "{{ vm_object.resources[0].spec.running | default(false) }}"
      when:
        - vm_pvcs | length > 0
        - vm_object.resources | length > 0

    - name: Display VM state
      debug:
        msg: "VM {{ vm_name }} running state: {{ vm_was_running | default(false) }}"
      when: vm_pvcs | length > 0

    # Step 7: VM Stop
    - name: Stop VirtualMachine by setting spec.running to false
      kubernetes.core.k8s:
        api_version: kubevirt.io/v1
        kind: VirtualMachine
        name: "{{ vm_name }}"
        namespace: "{{ migration_namespace }}"
        definition:
          spec:
            running: false
      when:
        - vm_pvcs | length > 0
        - vm_was_running | default(false)

    # Step 8: VMI Termination
    - name: Wait for VirtualMachineInstance to terminate
      kubernetes.core.k8s_info:
        api_version: kubevirt.io/v1
        kind: VirtualMachineInstance
        name: "{{ vm_name }}"
        namespace: "{{ migration_namespace }}"
      register: vmi_status
      until: vmi_status.resources | length == 0
      retries: 60
      delay: 5
      when: vm_pvcs | length > 0

    - name: Verify VMI is terminated
      debug:
        msg: "VirtualMachineInstance {{ vm_name }} terminated successfully - PVCs are now safe to convert"
      when:
        - vm_pvcs | length > 0
        - vmi_status.resources | length == 0

    # Step 9: Create conversion Pods for each source PVC
    - name: Create conversion Pod for each source PVC
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: Pod
          metadata:
            name: "fa-pxd-convert-{{ item.metadata.name }}"
            namespace: "{{ migration_namespace }}"
            labels:
              app: fa-pxd-converter
              source-pvc: "{{ item.metadata.name }}"
              dest-pvc: "pxd-{{ item.metadata.name }}"
              vm: "{{ vm_name }}"
          spec:
            hostNetwork: true
            hostPID: true
            serviceAccountName: fa-pxd-converter
            containers:
                - name: converter
                  image: "{{ converter_image }}"
                  imagePullPolicy: Always
                  env:
                  # FlashArray configuration (derived from StorageMap secret)
                  - name: FA_IP
                    value: "{{ pvc_fa_config[item.metadata.name].fa_endpoint }}"
                  - name: FA_API_VER
                    value: "{{ fa_api_version }}"
                  - name: FA_API_TOKEN
                    value: "{{ pvc_fa_config[item.metadata.name].fa_api_token }}"
                  # Volume information (required)
                  - name: SOURCE_PV_NAME
                    value: "{{ item.spec.volumeName }}"
                  - name: DEST_PV_NAME
                    value: "{{ pxd_pvc_status.results[loop_index].resources[0].spec.volumeName }}"
                  # Migration options (optional)
                  - name: XCOPY_JOBS
                    value: "{{ xcopy_jobs | default(512) }}"
                  - name: POKE_WORKERS
                    value: "{{ poke_workers | default(16) }}"
                  - name: STATS_INTERVAL
                    value: "{{ stats_interval | default(30) }}"
                  volumeMounts:
                  - name: host-root
                    mountPath: /host
                  - name: host-dev
                    mountPath: /dev
                  volumeDevices:
                  - name: source-volume
                    devicePath: /dev/fa-source
                  - name: dest-volume
                    devicePath: /dev/pxd-dest
                  securityContext:
                    privileged: true
            volumes:
            - name: host-root
              hostPath:
                path: /
            - name: host-dev
              hostPath:
                path: /dev
            - name: source-volume
              persistentVolumeClaim:
                claimName: "{{ item.metadata.name }}"
            - name: dest-volume
              persistentVolumeClaim:
                claimName: "pxd-{{ item.metadata.name }}"
            restartPolicy: Never
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
        index_var: loop_index
      when: vm_pvcs | length > 0
      register: conversion_jobs

    # Step 10: Wait for conversion Pods to complete
    - name: Wait for conversion Pods to complete
      kubernetes.core.k8s_info:
        api_version: v1
        kind: Pod
        name: "fa-pxd-convert-{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0
      register: pod_status
      until: >
        (pod_status.resources | length == 0) or
        (pod_status.resources | length > 0 and
        pod_status.resources[0].status.phase in ['Succeeded', 'Failed'])
      retries: 10
      delay: 30

    - name: Check for failed Pods
      fail:
        msg: |-
          Conversion Pod fa-pxd-convert-{{ item.metadata.name }} FAILED!
          Pod status: {{ pod_status.results[loop_index].resources[0].status | default('No status available') }}

          Check the Pod logs for details:
            kubectl describe pod fa-pxd-convert-{{ item.metadata.name }} -n {{ migration_namespace }}
            kubectl logs fa-pxd-convert-{{ item.metadata.name }} -n {{ migration_namespace }}
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
        index_var: loop_index
      when:
        - vm_pvcs | length > 0
        - pod_status.results[loop_index].resources[0].status.phase is defined
        - pod_status.results[loop_index].resources[0].status.phase == 'Failed'

    - name: Display Pod completion status
      debug:
        msg: "Data copy Pod fa-pxd-convert-{{ item.metadata.name }} completed successfully"
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    # Now that data copy is complete and Pods are done, we can safely rebind
    # First, delete the Pods to release the PVCs
    - name: Delete conversion Pods to release PVCs
      kubernetes.core.k8s:
        api_version: v1
        kind: Pod
        name: "fa-pxd-convert-{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
        state: absent
        wait: yes
        wait_timeout: 120
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    - name: Wait for Pods to be fully deleted
      kubernetes.core.k8s_info:
        api_version: v1
        kind: Pod
        name: "fa-pxd-convert-{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      register: converter_pods_deleted
      until: converter_pods_deleted.resources | length == 0
      retries: 30
      delay: 2
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    - name: Get temporary PXD PVC details for rebinding
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "pxd-{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      register: pxd_pvc_info
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    - name: Delete temporary PXD PVCs (PVs will be retained)
      kubernetes.core.k8s:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "pxd-{{ item.item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
        state: absent
        wait: yes
        wait_timeout: 120
      loop: "{{ pxd_pvc_info.results }}"
      loop_control:
        label: "pxd-{{ item.item.metadata.name }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    # Before deleting FA PVCs, we need to delete any migration pods that are still mounting them
    - name: Get all pods in migration namespace
      kubernetes.core.k8s_info:
        api_version: v1
        kind: Pod
        namespace: "{{ migration_namespace }}"
      register: all_pods
      when: vm_pvcs | length > 0

    - name: Build list of PVC names for filtering
      set_fact:
        pvc_names_list: "{{ vm_pvcs | map(attribute='metadata.name') | list }}"
      when: vm_pvcs | length > 0

    - name: Find migration pods using each FA PVC
      set_fact:
        migration_pods_to_delete: "{{ migration_pods_to_delete | default([]) + [pod_item] }}"
      loop: "{{ all_pods.resources | default([]) }}"
      loop_control:
        loop_var: pod_item
        label: "{{ pod_item.metadata.name }}"
      vars:
        # Get list of PVC names used by this pod's volumes
        pod_pvc_names: "{{ pod_item.spec.volumes | default([]) | selectattr('persistentVolumeClaim', 'defined') | map(attribute='persistentVolumeClaim.claimName') | list }}"
        # Check if any of the pod's PVCs match our FA PVCs
        has_matching_pvc: "{{ (pod_pvc_names | intersect(pvc_names_list) | length > 0) }}"
      when:
        - vm_pvcs | length > 0
        - pod_item.status.phase == "Succeeded"
        - has_matching_pvc

    - name: Display migration pods to delete
      debug:
        msg: "Found {{ migration_pods_to_delete | default([]) | length }} completed migration pod(s) to delete"
      when: vm_pvcs | length > 0

    - name: Delete completed migration pods that are using FA PVCs
      kubernetes.core.k8s:
        api_version: v1
        kind: Pod
        name: "{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
        state: absent
        wait: yes
        wait_timeout: 60
      loop: "{{ migration_pods_to_delete | default([]) }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when:
        - vm_pvcs | length > 0
        - migration_pods_to_delete is defined
        - migration_pods_to_delete | length > 0

    # Step 11: Delete original FA PVCs
    - name: Delete original FA PVCs
      kubernetes.core.k8s:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
        state: absent
        wait: yes
        wait_timeout: 120
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    # Step 12: Rebind PXD PVs to original PVC namess
    - name: Recreate PVCs with original names bound to PXD PVs
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: PersistentVolumeClaim
          metadata:
            name: "{{ item.item.metadata.name }}"
            namespace: "{{ migration_namespace }}"
            labels: "{{ item.item.metadata.labels }}"
            annotations: "{{ item.item.metadata.annotations }}"
            ownerReferences: "{{ item.item.metadata.ownerReferences | default([]) }}"
          spec:
            accessModes:
              - ReadWriteMany
            storageClassName: "{{ pvc_fa_config[item.item.metadata.name].pxd_storage_class }}"
            volumeMode: Block
            volumeName: "{{ item.resources[0].spec.volumeName }}"
            resources:
              requests:
                storage: "{{ item.resources[0].spec.resources.requests.storage }}"
      loop: "{{ pxd_pvc_info.results }}"
      loop_control:
        label: "{{ item.item.metadata.name }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    - name: Get recreated PVC details (for UID)
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "{{ item.item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      register: recreated_pvc_info
      loop: "{{ pxd_pvc_info.results }}"
      loop_control:
        label: "{{ item.item.metadata.name }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    - name: Update PV claimRef to point to recreated PVC
      kubernetes.core.k8s:
        api_version: v1
        kind: PersistentVolume
        name: "{{ item.item.resources[0].spec.volumeName }}"
        definition:
          spec:
            claimRef:
              apiVersion: v1
              kind: PersistentVolumeClaim
              name: "{{ item.item.item.metadata.name }}"
              namespace: "{{ migration_namespace }}"
              uid: "{{ item.resources[0].metadata.uid }}"
              resourceVersion: "{{ item.resources[0].metadata.resourceVersion }}"
      loop: "{{ recreated_pvc_info.results }}"
      loop_control:
        label: "{{ item.item.resources[0].spec.volumeName }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    - name: Verify PVCs are bound to PXD PVs
      kubernetes.core.k8s_info:
        api_version: v1
        kind: PersistentVolumeClaim
        name: "{{ item.metadata.name }}"
        namespace: "{{ migration_namespace }}"
      register: final_pvc_status
      until: >
        final_pvc_status.resources | length > 0 and
        final_pvc_status.resources[0].status.phase == "Bound"
      retries: 30
      delay: 2
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
      when: vm_pvcs | length > 0

    - name: Verify PVC is bound to correct PXD PV
      assert:
        that:
          - final_pvc_status.results[loop_index].resources[0].spec.volumeName == pxd_pvc_info.results[loop_index].resources[0].spec.volumeName
        fail_msg: |-
          PVC {{ item.metadata.name }} is not bound to the expected PXD PV!
          Expected: {{ pxd_pvc_info.results[loop_index].resources[0].spec.volumeName }}
          Actual: {{ final_pvc_status.results[loop_index].resources[0].spec.volumeName }}
      loop: "{{ vm_pvcs }}"
      loop_control:
        label: "{{ item.metadata.name }}"
        index_var: loop_index
      when: vm_pvcs | length > 0

    - name: Display rebinding verification
      debug:
        msg: "✓ PVC {{ item.item.metadata.name }} is bound to PV {{ item.resources[0].spec.volumeName }}"
      loop: "{{ final_pvc_status.results }}"
      loop_control:
        label: "{{ item.item.metadata.name }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    - name: Verify labels, annotations, and ownerReferences were preserved
      debug:
        msg: |-
          PVC {{ item.resources[0].metadata.name }} metadata verification:
          Labels preserved:
            - vmID: {{ item.resources[0].metadata.labels.vmID | default('MISSING!') }}
            - app: {{ item.resources[0].metadata.labels.app | default('MISSING!') }}
            - migration: {{ item.resources[0].metadata.labels.migration | default('MISSING!') }}
            - plan: {{ item.resources[0].metadata.labels.plan | default('MISSING!') }}
          Annotations preserved:
            - disk-index: {{ item.resources[0].metadata.annotations['forklift.konveyor.io/disk-index'] | default('MISSING!') }}
            - disk-source: {{ item.resources[0].metadata.annotations['forklift.konveyor.io/disk-source'] | default('MISSING!') }}
          OwnerReferences preserved:
            - Count: {{ item.resources[0].metadata.ownerReferences | default([]) | length }}
            - VM Owner: {{ 'YES' if (item.resources[0].metadata.ownerReferences | default([]) | selectattr('kind', 'equalto', 'VirtualMachine') | list | length > 0) else 'MISSING!' }}
      loop: "{{ final_pvc_status.results }}"
      loop_control:
        label: "{{ item.resources[0].metadata.name }}"
      when:
        - vm_pvcs | length > 0
        - not item.skipped | default(false)

    # Step 13: Restart VM if it was running before
    - name: Restart VirtualMachine by setting spec.running to true
      kubernetes.core.k8s:
        api_version: kubevirt.io/v1
        kind: VirtualMachine
        name: "{{ vm_name }}"
        namespace: "{{ migration_namespace }}"
        definition:
          spec:
            running: true
      when:
        - vm_pvcs | length > 0
        - vm_was_running | default(false)

    - name: Wait for VirtualMachineInstance to be ready
      kubernetes.core.k8s_info:
        api_version: kubevirt.io/v1
        kind: VirtualMachineInstance
        name: "{{ vm_name }}"
        namespace: "{{ migration_namespace }}"
      register: vmi_ready_status
      until: >
        vmi_ready_status.resources | length > 0 and
        vmi_ready_status.resources[0].status.phase == "Running"
      retries: 60
      delay: 5
      when:
        - vm_pvcs | length > 0
        - vm_was_running | default(false)
      ignore_errors: yes

    - name: Display VM restart status
      debug:
        msg: "✓ VirtualMachine {{ vm_name }} restarted successfully with PXD volumes"
      when:
        - vm_pvcs | length > 0
        - vm_was_running | default(false)
        - vmi_ready_status.resources | length > 0

    # Step 14: Summary
    - name: Conversion and rebinding summary
      debug:
        msg: |-
          ========================================
          FA to PXD conversion completed successfully!
          ========================================
          Source FA PVCs: {{ vm_pvcs | length }}
          PXD volumes created: {{ vm_pvcs | length }}
          Data copied: {{ vm_pvcs | length }}
          PVCs rebound to PXD volumes: {{ vm_pvcs | length }}
          ========================================
          ✓ Original PVC names now point to PXD volumes!
          ✓ Pods handled data copy, playbook handled rebinding
          ========================================
          VM Status: {{ vm_ready.resources[0].status.printableStatus | default('Unknown') }}
          ========================================
          Next steps:
          1. Access VM console to verify boot
          2. Verify application functionality
          ========================================
