Fix snapshot role — XO REST API, fix playbook host targeting
- roles/snapshot/tasks/main.yml: replace xe CLI with XO REST API - POST /rest/v0/vms/<uuid>/actions/snapshot?sync=true - stores returned snapshot UUID as snapshot_id - baremetal: skips gracefully with warning - playbooks/snapshot_pre.yml: target linux_hosts only (was all) - playbooks/linux_patch.yml: remove snapshot + report roles (snapshot is separate step) - playbooks/site_maintenance.yml: remove bootstrap play (handled per-playbook), remove windows_patch import (WinRM not implemented)
This commit is contained in:
@@ -1,4 +1,10 @@
|
|||||||
---
|
---
|
||||||
|
# =============================================================================
|
||||||
|
# playbooks/linux_patch.yml
|
||||||
|
# Linux-only patch run. Snapshots should be taken separately via snapshot_pre.yml
|
||||||
|
# before running this playbook, or use site_maintenance.yml for the full sequence.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
- name: Bootstrap — ensure Python is available
|
- name: Bootstrap — ensure Python is available
|
||||||
hosts: linux_hosts
|
hosts: linux_hosts
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
@@ -9,7 +15,6 @@
|
|||||||
hosts: linux_hosts
|
hosts: linux_hosts
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
roles:
|
roles:
|
||||||
- snapshot
|
|
||||||
- preflight
|
- preflight
|
||||||
- linux_patch
|
- linux_patch
|
||||||
- report
|
#- report
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
---
|
---
|
||||||
- name: Bootstrap — ensure Python is available
|
# =============================================================================
|
||||||
hosts: all
|
# playbooks/site_maintenance.yml
|
||||||
gather_facts: false
|
# Full maintenance sequence:
|
||||||
tasks:
|
# 1. Snapshot guest VMs (linux_hosts via XO/Proxmox API)
|
||||||
- ansible.builtin.import_tasks: ../roles/preflight/tasks/bootstrap.yml
|
# 2. Preflight safety checks (linux_hosts + xcpng_hosts)
|
||||||
|
# 3. Linux patching (linux_hosts)
|
||||||
|
#
|
||||||
|
# Windows hosts are excluded — WinRM patching not yet implemented.
|
||||||
|
# XCP-NG pool updates are a separate template (xcpng_pool_update.yml)
|
||||||
|
# and should be run before this playbook during a maintenance window.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
- import_playbook: snapshot_pre.yml
|
- import_playbook: snapshot_pre.yml
|
||||||
- import_playbook: site_preflight.yml
|
- import_playbook: site_preflight.yml
|
||||||
- import_playbook: linux_patch.yml
|
- import_playbook: linux_patch.yml
|
||||||
- import_playbook: windows_patch.yml
|
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
---
|
---
|
||||||
|
# =============================================================================
|
||||||
|
# playbooks/snapshot_pre.yml
|
||||||
|
# Pre-patch snapshots for Linux guest VMs via hypervisor API.
|
||||||
|
# Targets linux_hosts only — snapshots taken per-VM before patching.
|
||||||
|
# XCP-NG: uses XO REST API (xcpng_vm_uuid required per host)
|
||||||
|
# Proxmox: uses community.general.proxmox_snap (proxmox_vmid required per host)
|
||||||
|
# Baremetal: snapshot tasks skipped automatically (no hypervisor_type match)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
- name: Bootstrap — ensure Python is available
|
- name: Bootstrap — ensure Python is available
|
||||||
hosts: all
|
hosts: linux_hosts
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
tasks:
|
tasks:
|
||||||
- ansible.builtin.import_tasks: ../roles/preflight/tasks/bootstrap.yml
|
- ansible.builtin.import_tasks: ../roles/preflight/tasks/bootstrap.yml
|
||||||
|
|
||||||
- name: Pre-patch snapshot
|
- name: Pre-patch snapshot
|
||||||
hosts: all
|
hosts: linux_hosts
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
roles:
|
roles:
|
||||||
- snapshot
|
- snapshot
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
---
|
---
|
||||||
|
# =============================================================================
|
||||||
|
# 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)
|
- name: Create pre-patch snapshot (Proxmox)
|
||||||
community.general.proxmox_snap:
|
community.general.proxmox_snap:
|
||||||
api_host: "{{ PROXMOX_HOST }}"
|
api_host: "{{ PROXMOX_HOST }}"
|
||||||
@@ -7,7 +25,8 @@
|
|||||||
api_token_secret: "{{ PROXMOX_TOKEN_SECRET }}"
|
api_token_secret: "{{ PROXMOX_TOKEN_SECRET }}"
|
||||||
vmid: "{{ proxmox_vmid }}"
|
vmid: "{{ proxmox_vmid }}"
|
||||||
state: present
|
state: present
|
||||||
snapname: "{{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}"
|
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 }}"
|
description: "Ansible pre-patch snapshot {{ ansible_date_time.iso8601 }}"
|
||||||
register: proxmox_snapshot_result
|
register: proxmox_snapshot_result
|
||||||
when: hypervisor_type == "proxmox"
|
when: hypervisor_type == "proxmox"
|
||||||
@@ -15,52 +34,60 @@
|
|||||||
|
|
||||||
- name: Store Proxmox snapshot name
|
- name: Store Proxmox snapshot name
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
snapshot_id: "{{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}"
|
snapshot_id: >-
|
||||||
|
{{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}
|
||||||
when: hypervisor_type == "proxmox"
|
when: hypervisor_type == "proxmox"
|
||||||
|
|
||||||
- name: Create pre-patch snapshot (XCP-NG)
|
# ─── XCP-NG snapshots via XO REST API ────────────────────────────────────────
|
||||||
ansible.builtin.shell: |
|
|
||||||
xe vm-snapshot vm={{ xcpng_vm_uuid }} new-name-label="{{ snapshot_name_prefix }}-{{ ansible_date_time.date }}-{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}"
|
- 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
|
register: xcpng_snapshot_result
|
||||||
changed_when: xcpng_snapshot_result.rc == 0
|
when: hypervisor_type == "xcpng" or hypervisor_type == "mixed"
|
||||||
when: hypervisor_type == "xcpng" and xcpng_host | length > 0
|
delegate_to: localhost
|
||||||
delegate_to: "{{ xcpng_host if xcpng_host | length > 0 else 'localhost' }}"
|
|
||||||
|
|
||||||
- name: Store XCP-NG snapshot UUID
|
- name: Store XCP-NG snapshot UUID
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
snapshot_id: "{{ xcpng_snapshot_result.stdout | trim }}"
|
snapshot_id: "{{ xcpng_snapshot_result.json | regex_replace('\"', '') }}"
|
||||||
when: hypervisor_type == "xcpng"
|
when:
|
||||||
|
- hypervisor_type == "xcpng" or hypervisor_type == "mixed"
|
||||||
|
- xcpng_snapshot_result is not skipped
|
||||||
|
- xcpng_snapshot_result.json is defined
|
||||||
|
|
||||||
- name: Verify snapshot was created (Proxmox)
|
# ─── Verify and assert ───────────────────────────────────────────────────────
|
||||||
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 }}"
|
|
||||||
snapname: "{{ snapshot_id }}"
|
|
||||||
state: present
|
|
||||||
register: proxmox_snap_verify
|
|
||||||
when: hypervisor_type == "proxmox"
|
|
||||||
delegate_to: localhost
|
|
||||||
|
|
||||||
- name: Verify snapshot was created (XCP-NG)
|
- name: Assert snapshot was created
|
||||||
ansible.builtin.shell: |
|
|
||||||
xe snapshot-list uuid={{ snapshot_id }} | grep uuid
|
|
||||||
register: xcpng_snap_verify
|
|
||||||
changed_when: false
|
|
||||||
failed_when: xcpng_snap_verify.stdout == ""
|
|
||||||
when: hypervisor_type == "xcpng" and xcpng_host | length > 0
|
|
||||||
delegate_to: "{{ xcpng_host if xcpng_host | length > 0 else 'localhost' }}"
|
|
||||||
|
|
||||||
- name: Assert snapshot exists before proceeding
|
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- snapshot_id is defined
|
- snapshot_id is defined
|
||||||
- snapshot_id != ""
|
- snapshot_id | length > 0
|
||||||
fail_msg: "SNAPSHOT: Failed to create or verify snapshot for {{ inventory_hostname }}. Aborting patch run."
|
fail_msg: >-
|
||||||
success_msg: "Snapshot verified: {{ snapshot_id }}"
|
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 ID
|
- name: Log snapshot result
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: "Pre-patch snapshot created: {{ snapshot_id }} for {{ inventory_hostname }}"
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user