- pve-backup-jobs.yml: each job's vmid list is now computed from homelab_services by backup.job instead of a hand-maintained CSV. Adding a service no longer needs a separate edit here (the forgotten-edit failure mode that left CT 148 emergency-bot without a backup). - roles/backup_audit/defaults: backup_audit_pbs_vmids derived from homelab_services by the monitoring.backup_audit_vmid flag; single shared freshness threshold backup_audit_pbs_max_age_hours (48). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uoq5AVK8mkBgg83Mq6o5V
128 lines
5.3 KiB
YAML
128 lines
5.3 KiB
YAML
# Списки VMID выводятся из реестра homelab_services по полю backup.job, а не
|
||
# перечисляются вручную. Раньше добавление сервиса требовало отдельной правки
|
||
# здесь, и о ней легко было забыть — CT 148 emergency-bot до сих пор без бэкапа
|
||
# именно по этой причине.
|
||
- name: Configure Proxmox backup jobs
|
||
hosts: mini-pc
|
||
gather_facts: false
|
||
vars:
|
||
pve_backup_jobs:
|
||
- id: homelab-pbs-daily-cloud
|
||
comment: Daily PBS backup for cloud-pc service containers
|
||
node: cloud-pc
|
||
vmid: >-
|
||
{{ homelab_services.values() | selectattr('backup.job', 'defined')
|
||
| selectattr('backup.job', 'eq', 'homelab-pbs-daily-cloud')
|
||
| map(attribute='vmid') | sort | join(',') }}
|
||
storage: pbs
|
||
schedule: "02:10"
|
||
mode: snapshot
|
||
exclude_path: /var/lib/docker/fuse-overlayfs/*/merged
|
||
- id: homelab-pbs-daily-mini
|
||
comment: Daily PBS backup for mini-pc service containers
|
||
node: mini-pc
|
||
vmid: >-
|
||
{{ homelab_services.values() | selectattr('backup.job', 'defined')
|
||
| selectattr('backup.job', 'eq', 'homelab-pbs-daily-mini')
|
||
| map(attribute='vmid') | sort | join(',') }}
|
||
storage: pbs
|
||
schedule: "02:40"
|
||
mode: snapshot
|
||
exclude_path: /var/lib/docker/fuse-overlayfs/*/merged
|
||
- id: homelab-local-weekly-pbs
|
||
comment: Weekly local backup for PBS container rootfs/config
|
||
node: cloud-pc
|
||
vmid: >-
|
||
{{ homelab_services.values() | selectattr('backup.job', 'defined')
|
||
| selectattr('backup.job', 'eq', 'homelab-local-weekly-pbs')
|
||
| map(attribute='vmid') | sort | join(',') }}
|
||
storage: backup
|
||
schedule: "Sun 03:30"
|
||
mode: snapshot
|
||
prune_backups: keep-last=2
|
||
|
||
tasks:
|
||
- name: Get existing Proxmox backup jobs
|
||
ansible.builtin.command: pvesh get /cluster/backup --output-format json
|
||
register: pve_backup_jobs_existing_raw
|
||
changed_when: false
|
||
|
||
- name: Parse existing Proxmox backup jobs
|
||
ansible.builtin.set_fact:
|
||
pve_backup_jobs_existing: "{{ pve_backup_jobs_existing_raw.stdout | from_json }}"
|
||
|
||
- name: Create missing Proxmox backup jobs
|
||
ansible.builtin.command: >-
|
||
pvesh create /cluster/backup
|
||
--id {{ item.id }}
|
||
--enabled 1
|
||
--node {{ item.node }}
|
||
--vmid {{ item.vmid }}
|
||
--storage {{ item.storage }}
|
||
--schedule {{ item.schedule | quote }}
|
||
--mode {{ item.mode }}
|
||
{% if item.prune_backups is defined %}
|
||
--prune-backups {{ item.prune_backups }} --remove 1
|
||
{% else %}
|
||
--remove 0
|
||
{% endif %}
|
||
{% if item.exclude_path is defined %}
|
||
--exclude-path {{ item.exclude_path | quote }}
|
||
{% endif %}
|
||
--notes-template {{ '{{' }}guestname{{ '}}' }}
|
||
--comment {{ item.comment | quote }}
|
||
loop: "{{ pve_backup_jobs }}"
|
||
when: item.id not in (pve_backup_jobs_existing | map(attribute='id') | list)
|
||
|
||
- name: Update existing Proxmox backup jobs
|
||
ansible.builtin.command: >-
|
||
pvesh set /cluster/backup/{{ item.id }}
|
||
--enabled 1
|
||
--node {{ item.node }}
|
||
--vmid {{ item.vmid }}
|
||
--storage {{ item.storage }}
|
||
--schedule {{ item.schedule | quote }}
|
||
--mode {{ item.mode }}
|
||
{% if item.prune_backups is defined %}
|
||
--prune-backups {{ item.prune_backups }} --remove 1
|
||
{% else %}
|
||
--delete prune-backups --remove 0
|
||
{% endif %}
|
||
{% if item.exclude_path is defined %}
|
||
--exclude-path {{ item.exclude_path | quote }}
|
||
{% else %}
|
||
--delete exclude-path
|
||
{% endif %}
|
||
--notes-template {{ '{{' }}guestname{{ '}}' }}
|
||
--comment {{ item.comment | quote }}
|
||
loop: "{{ pve_backup_jobs }}"
|
||
vars:
|
||
current_job: >-
|
||
{{ pve_backup_jobs_existing | selectattr('id', 'equalto', item.id) | first }}
|
||
when:
|
||
- item.id in (pve_backup_jobs_existing | map(attribute='id') | list)
|
||
- >-
|
||
current_job.enabled | int != 1 or
|
||
current_job.node != item.node or
|
||
current_job.vmid | string != item.vmid | string or
|
||
current_job.storage != item.storage or
|
||
current_job.schedule != item.schedule or
|
||
current_job.mode != item.mode or
|
||
current_job.comment != item.comment or
|
||
current_job.remove | int != (1 if item.prune_backups is defined else 0) or
|
||
current_job['exclude-path'] | default([]) != ([item.exclude_path] if item.exclude_path is defined else []) or
|
||
(item.prune_backups is not defined and current_job['prune-backups'] is defined) or
|
||
(item.prune_backups is defined and
|
||
(current_job['prune-backups'] | default({})).get('keep-last', 0) | int !=
|
||
item.prune_backups | regex_replace('^keep-last=', '') | int)
|
||
changed_when: true
|
||
|
||
- name: Show configured Proxmox backup jobs
|
||
ansible.builtin.command: pvesh get /cluster/backup --output-format yaml
|
||
register: pve_backup_jobs_configured
|
||
changed_when: false
|
||
|
||
- name: Print configured Proxmox backup jobs
|
||
ansible.builtin.debug:
|
||
var: pve_backup_jobs_configured.stdout_lines
|