--- # Proxmox VE Management Playbook # Inventory and health check for VMs, LXC containers, storage, and recent tasks # Usage: ansible-playbook playbooks/proxmox_management.yml -i hosts.ini # Usage: ansible-playbook playbooks/proxmox_management.yml -i hosts.ini -e action=snapshot -e vm_id=100 - name: Proxmox VE Management hosts: pve gather_facts: yes become: false vars: action: "{{ action | default('status') }}" vm_id: "{{ vm_id | default('') }}" report_dir: "/tmp/health_reports" tasks: # ---------- Report directory ---------- - name: Ensure health report directory exists ansible.builtin.file: path: "{{ report_dir }}" state: directory mode: '0755' delegate_to: localhost run_once: true # ---------- Status mode ---------- - name: Get PVE version ansible.builtin.command: pveversion register: pve_version changed_when: false failed_when: false when: action == 'status' - name: Get node resource summary ansible.builtin.shell: | pvesh get /nodes/$(hostname)/status --output-format json 2>/dev/null || \ echo '{"error": "pvesh not available"}' register: node_status_raw changed_when: false failed_when: false when: action == 'status' - name: List all VMs ansible.builtin.command: qm list register: vm_list changed_when: false failed_when: false when: action == 'status' - name: List all LXC containers ansible.builtin.command: pct list register: lxc_list changed_when: false failed_when: false when: action == 'status' - name: Count running VMs ansible.builtin.shell: qm list 2>/dev/null | grep -c running || echo "0" register: running_vm_count changed_when: false failed_when: false when: action == 'status' - name: Count running LXC containers ansible.builtin.shell: pct list 2>/dev/null | grep -c running || echo "0" register: running_lxc_count changed_when: false failed_when: false when: action == 'status' - name: Get storage pool status ansible.builtin.shell: | pvesh get /nodes/$(hostname)/storage --output-format json 2>/dev/null | python3 << 'PYEOF' || pvesm status 2>/dev/null || echo "Storage info unavailable" import sys, json try: pools = json.load(sys.stdin) except Exception: sys.exit(1) print('{:<20} {:<15} {:>8} {:>14}'.format('Storage', 'Type', 'Used%', 'Avail (GiB)')) print('-' * 62) for p in pools: name = p.get('storage', 'n/a') stype = p.get('type', 'n/a') total = p.get('total', 0) used = p.get('used', 0) avail = p.get('avail', 0) pct = round(used / total * 100, 1) if total and total > 0 else 0.0 avail_gib = round(avail / 1024**3, 2) print('{:<20} {:<15} {:>7}% {:>13} GiB'.format(name, stype, pct, avail_gib)) PYEOF register: storage_status changed_when: false failed_when: false when: action == 'status' - name: Get last 10 task log entries ansible.builtin.shell: | pvesh get /nodes/$(hostname)/tasks --limit 10 --output-format json 2>/dev/null | python3 << 'PYEOF' || echo "Task log unavailable" import sys, json, datetime try: tasks = json.load(sys.stdin) except Exception: sys.exit(1) print('{:<22} {:<12} {}'.format('Timestamp', 'Status', 'UPID')) print('-' * 80) for t in tasks: upid = t.get('upid', 'n/a') status = t.get('status', 'n/a') starttime = t.get('starttime', 0) try: ts = datetime.datetime.fromtimestamp(starttime).strftime('%Y-%m-%d %H:%M:%S') except Exception: ts = str(starttime) print('{:<22} {:<12} {}'.format(ts, status, upid[:60])) PYEOF register: task_log changed_when: false failed_when: false when: action == 'status' # ---------- Status summary ---------- - name: Display Proxmox status summary ansible.builtin.debug: msg: | ============================================================ Proxmox VE Status — {{ inventory_hostname }} ============================================================ PVE Version : {{ pve_version.stdout | default('n/a') }} Running VMs : {{ running_vm_count.stdout | default('0') | trim }} Running LXCs : {{ running_lxc_count.stdout | default('0') | trim }} --- Node Resource Summary (JSON) --- {{ node_status_raw.stdout | default('{}') | from_json | to_nice_json if (node_status_raw.stdout | default('') | length > 0 and node_status_raw.stdout | default('') is search('{')) else node_status_raw.stdout | default('unavailable') }} --- VMs (qm list) --- {{ vm_list.stdout | default('none') }} --- LXC Containers (pct list) --- {{ lxc_list.stdout | default('none') }} --- Storage Pools --- {{ storage_status.stdout | default('unavailable') }} --- Recent Tasks (last 10) --- {{ task_log.stdout | default('unavailable') }} ============================================================ when: action == 'status' # ---------- Write JSON report ---------- - name: Write Proxmox health JSON report ansible.builtin.copy: content: "{{ report_data | to_nice_json }}" dest: "{{ report_dir }}/proxmox_{{ ansible_date_time.date }}.json" vars: report_data: timestamp: "{{ ansible_date_time.iso8601 }}" host: "{{ inventory_hostname }}" pve_version: "{{ pve_version.stdout | default('n/a') | trim }}" running_vms: "{{ running_vm_count.stdout | default('0') | trim }}" running_lxcs: "{{ running_lxc_count.stdout | default('0') | trim }}" vm_list: "{{ vm_list.stdout | default('') }}" lxc_list: "{{ lxc_list.stdout | default('') }}" storage_status: "{{ storage_status.stdout | default('') }}" task_log: "{{ task_log.stdout | default('') }}" node_status_raw: "{{ node_status_raw.stdout | default('') }}" delegate_to: localhost run_once: true changed_when: false when: action == 'status' # ---------- Snapshot mode ---------- - name: Create VM snapshot ansible.builtin.shell: > qm snapshot {{ vm_id }} "ansible-snap-{{ ansible_date_time.epoch }}" --description "Ansible automated snapshot" register: snapshot_result changed_when: true failed_when: false when: - action == 'snapshot' - vm_id | string | length > 0 - name: Display snapshot result ansible.builtin.debug: msg: | Snapshot created on {{ inventory_hostname }} VM ID : {{ vm_id }} Result: {{ (snapshot_result | default({})).stdout | default('') }} {{ (snapshot_result | default({})).stderr | default('') }} when: - action == 'snapshot' - vm_id | string | length > 0