--- # ============================================================================= # roles/snapshot/tasks/main.yml # # Creates pre-patch snapshots for guest VMs. # Runs against linux_hosts — each host needs xcpng_vm_uuid or proxmox_vmid set. # # Hypervisor dispatch: # proxmox → community.general.proxmox_snap (delegate_to: localhost) # xcpng → XO REST API snapshot action (delegate_to: localhost) # baremetal → skipped entirely # # Required vars: # XCP-NG: xcpng_vm_uuid, XO_URL, XO_TOKEN (from variable group) # Proxmox: proxmox_vmid, PROXMOX_HOST, PROXMOX_TOKEN_ID, PROXMOX_TOKEN_SECRET # ============================================================================= # ─── Proxmox snapshots ──────────────────────────────────────────────────────── - name: Create pre-patch snapshot (Proxmox) community.general.proxmox_snap: api_host: "{{ PROXMOX_HOST }}" api_user: "{{ PROXMOX_TOKEN_ID | split('!') | first }}" api_token_id: "{{ PROXMOX_TOKEN_ID | split('!') | last }}" api_token_secret: "{{ PROXMOX_TOKEN_SECRET }}" vmid: "{{ proxmox_vmid }}" state: present snapname: >- {{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }} description: "Ansible pre-patch snapshot {{ ansible_date_time.iso8601 }}" register: proxmox_snapshot_result when: hypervisor_type == "proxmox" delegate_to: localhost - name: Store Proxmox snapshot name ansible.builtin.set_fact: snapshot_id: >- {{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }} when: hypervisor_type == "proxmox" # ─── XCP-NG snapshots via XO REST API ──────────────────────────────────────── - name: Create pre-patch snapshot (XCP-NG via XO REST API) ansible.builtin.uri: url: "{{ XO_URL }}/rest/v0/vms/{{ xcpng_vm_uuid }}/actions/snapshot?sync=true" method: POST headers: Cookie: "authenticationToken={{ XO_TOKEN }}" Content-Type: "application/json" Accept: "application/json" body_format: json body: name_label: >- {{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }} validate_certs: "{{ xo_validate_certs | default(true) }}" status_code: [200, 201] timeout: 120 register: xcpng_snapshot_result when: hypervisor_type == "xcpng" or hypervisor_type == "mixed" delegate_to: localhost - name: Store XCP-NG snapshot UUID ansible.builtin.set_fact: snapshot_id: "{{ xcpng_snapshot_result.json | regex_replace('\"', '') }}" when: - hypervisor_type == "xcpng" or hypervisor_type == "mixed" - xcpng_snapshot_result is not skipped - xcpng_snapshot_result.json is defined # ─── Verify and assert ─────────────────────────────────────────────────────── - name: Assert snapshot was created ansible.builtin.assert: that: - snapshot_id is defined - snapshot_id | length > 0 fail_msg: >- SNAPSHOT FAILED: Could not create or verify snapshot for {{ inventory_hostname }}. Aborting to protect against unsnapshotted patch run. success_msg: "Snapshot OK: {{ snapshot_id }} for {{ inventory_hostname }}" when: hypervisor_type != "baremetal" - name: Log snapshot result ansible.builtin.debug: msg: "Pre-patch snapshot created: {{ snapshot_id }} for {{ inventory_hostname }}" when: hypervisor_type != "baremetal" - name: Log snapshot skipped (baremetal) ansible.builtin.debug: msg: >- Snapshot skipped for {{ inventory_hostname }} — hypervisor_type is baremetal. Ensure change approval is in place before patching. when: hypervisor_type == "baremetal"