--- # ============================================================================== # VM Snapshot Backup Playbook # ============================================================================== # Create snapshots of VMs before risky operations # Supports KVM/libvirt VMs via hypervisor connection # ============================================================================== - name: Create VM Snapshots for Backup hosts: localhost gather_facts: true vars: hypervisor_uri: "qemu+ssh://grok@grok.home.serneels.xyz/system" snapshot_description: "Pre-maintenance backup" snapshot_prefix: "backup" target_vms: [] # Empty list means all running VMs tasks: - name: Display snapshot operation information ansible.builtin.debug: msg: - "=== VM Snapshot Backup Operation ===" - "Hypervisor: {{ hypervisor_uri }}" - "Date: {{ ansible_date_time.iso8601 }}" - "Target VMs: {{ target_vms | default('all running VMs') }}" tags: [always] - name: Validate target_vms variable ansible.builtin.assert: that: - target_vms is defined - target_vms is iterable fail_msg: "target_vms must be a list of VM names" tags: [always] # ========================================================================== # Get VM List # ========================================================================== - name: Get list of all running VMs ansible.builtin.shell: | ssh grokbox "sudo virsh list --name" register: all_vms_raw changed_when: false when: target_vms | length == 0 tags: [discover] - name: Parse running VMs list ansible.builtin.set_fact: discovered_vms: "{{ all_vms_raw.stdout_lines | select() | list }}" when: target_vms | length == 0 tags: [discover] - name: Set final VM list ansible.builtin.set_fact: vms_to_backup: "{{ target_vms if target_vms | length > 0 else discovered_vms }}" tags: [discover] - name: Display VMs to be backed up ansible.builtin.debug: msg: "VMs to backup: {{ vms_to_backup }}" tags: [discover] # ========================================================================== # Pre-flight Checks # ========================================================================== - name: Check if VMs exist and are running ansible.builtin.shell: | ssh grokbox "sudo virsh domstate {{ item }}" register: vm_states failed_when: vm_states.rc != 0 changed_when: false loop: "{{ vms_to_backup }}" tags: [validate] - name: Verify all VMs are running ansible.builtin.assert: that: - item.stdout == 'running' fail_msg: "VM {{ item.item }} is not running (state: {{ item.stdout }})" success_msg: "VM {{ item.item }} is running" loop: "{{ vm_states.results }}" tags: [validate] - name: Check for existing snapshots ansible.builtin.shell: | ssh grokbox "sudo virsh snapshot-list {{ item }} --name" register: existing_snapshots changed_when: false loop: "{{ vms_to_backup }}" tags: [validate] - name: Display existing snapshots ansible.builtin.debug: msg: - "VM: {{ item.item }}" - "Existing snapshots: {{ item.stdout_lines | default(['none']) | join(', ') }}" loop: "{{ existing_snapshots.results }}" tags: [validate] # ========================================================================== # Create Snapshots # ========================================================================== - name: Generate snapshot name with timestamp ansible.builtin.set_fact: snapshot_timestamp: "{{ ansible_date_time.epoch }}" tags: [snapshot] - name: Create VM snapshots ansible.builtin.shell: | ssh grokbox "sudo virsh snapshot-create-as {{ item }} \ --name '{{ snapshot_prefix }}_{{ snapshot_timestamp }}' \ --description '{{ snapshot_description }} - {{ ansible_date_time.iso8601 }}' \ --atomic" register: snapshot_create loop: "{{ vms_to_backup }}" tags: [snapshot] - name: Verify snapshot creation ansible.builtin.shell: | ssh grokbox "sudo virsh snapshot-info {{ item }} {{ snapshot_prefix }}_{{ snapshot_timestamp }}" register: snapshot_info changed_when: false loop: "{{ vms_to_backup }}" tags: [snapshot, verify] # ========================================================================== # Generate Backup Report # ========================================================================== - name: Create backup report directory ansible.builtin.file: path: "./stats/vm_backups" state: directory mode: '0755' tags: [report] - name: Generate backup report ansible.builtin.copy: content: | ================================================================================ VM SNAPSHOT BACKUP REPORT ================================================================================ Date: {{ ansible_date_time.iso8601 }} Hypervisor: {{ hypervisor_uri }} Snapshot Name: {{ snapshot_prefix }}_{{ snapshot_timestamp }} Description: {{ snapshot_description }} VMs Backed Up: {% for vm in vms_to_backup %} - {{ vm }} {% endfor %} Snapshot Details: {% for result in snapshot_info.results %} VM: {{ result.item }} {{ result.stdout }} {% endfor %} ROLLBACK INSTRUCTIONS ================================================================================ To restore a VM to this snapshot: 1. Stop the VM (if running): ssh grokbox "sudo virsh shutdown " 2. Revert to snapshot: ssh grokbox "sudo virsh snapshot-revert {{ snapshot_prefix }}_{{ snapshot_timestamp }}" 3. Start the VM: ssh grokbox "sudo virsh start " To delete this snapshot after verification: ssh grokbox "sudo virsh snapshot-delete {{ snapshot_prefix }}_{{ snapshot_timestamp }}" ================================================================================ END OF REPORT ================================================================================ dest: "./stats/vm_backups/backup_{{ snapshot_timestamp }}.txt" mode: '0644' tags: [report] # ========================================================================== # Display Summary # ========================================================================== - name: Display backup summary ansible.builtin.debug: msg: - "=== VM Snapshot Backup Complete ===" - "Snapshot Name: {{ snapshot_prefix }}_{{ snapshot_timestamp }}" - "VMs Backed Up: {{ vms_to_backup | length }}" - "Backup Report: ./stats/vm_backups/backup_{{ snapshot_timestamp }}.txt" - "" - "⚠️ IMPORTANT NOTES:" - "1. Snapshots are point-in-time copies" - "2. Test restoration procedure before relying on snapshots" - "3. Snapshots consume disk space - clean up old snapshots" - "4. For critical changes, consider full VM backups" - "" - "To restore: virsh snapshot-revert {{ snapshot_prefix }}_{{ snapshot_timestamp }}" tags: [always]