/
/
/
Ansible role that can run restic backups and push it to a remote storage server.
1---
2# Publish the run report to Home Assistant as MQTT discovery sensors.
3#
4# Everything goes through HA's own mqtt.publish service (REST API), so this
5# needs only the HA token - no MQTT client or credentials on the host, and no
6# HA configuration changes. All messages are retained: the sensors keep their
7# values across HA restarts until the next run overwrites them.
8#
9# Entities (device "Homelab Backup"):
10# sensor.homelab_backup_status ok|failed; attributes = the full report
11# sensor.homelab_backup_last_run timestamp of the last run
12# sensor.homelab_backup_repo_size repository size in GB
13
14- name: Prepare the report topics and device
15 ansible.builtin.set_fact:
16 _report_base: "homelab/{{ backup_report_object_id }}"
17 _report_device:
18 identifiers: ["{{ backup_report_object_id }}"]
19 name: "Homelab Backup"
20 manufacturer: "server_automation"
21 model: "restic {{ backup_restic_version }}"
22
23- name: Publish the sensor discovery configs
24 ansible.builtin.uri:
25 url: "{{ backup_report_ha_url | regex_replace('/$', '') }}/api/services/mqtt/publish"
26 method: POST
27 headers:
28 Authorization: "Bearer {{ backup_report_ha_token }}"
29 body_format: json
30 body:
31 topic: "homeassistant/sensor/{{ backup_report_object_id }}/{{ item.key }}/config"
32 retain: true
33 payload: >-
34 {{ ({
35 'name': item.name,
36 'unique_id': backup_report_object_id ~ '_' ~ item.key,
37 'object_id': backup_report_object_id ~ '_' ~ item.key,
38 'state_topic': _report_base ~ '/state',
39 'value_template': item.value,
40 'device': _report_device
41 } | combine({'icon': item.icon} if item.icon is defined else {})
42 | combine({'device_class': item.device_class} if item.device_class is defined else {})
43 | combine({'unit_of_measurement': item.unit} if item.unit is defined else {})
44 | combine({'state_class': item.state_class} if item.state_class is defined else {})
45 | combine({'json_attributes_topic': _report_base ~ '/attributes'} if item.attributes | default(false) else {})
46 ) | to_json }}
47 validate_certs: "{{ backup_report_validate_certs }}"
48 status_code: [200]
49 loop: "{{ backup_report_sensors }}"
50 loop_control:
51 label: "{{ item.key }}"
52 register: _report_discovery
53 failed_when: false
54 no_log: true # the request carries the HA token
55
56- name: Publish the report state and attributes
57 ansible.builtin.uri:
58 url: "{{ backup_report_ha_url | regex_replace('/$', '') }}/api/services/mqtt/publish"
59 method: POST
60 headers:
61 Authorization: "Bearer {{ backup_report_ha_token }}"
62 body_format: json
63 body:
64 topic: "{{ _report_base }}/{{ item.topic }}"
65 retain: true
66 payload: "{{ item.payload | to_json }}"
67 validate_certs: "{{ backup_report_validate_certs }}"
68 status_code: [200]
69 loop:
70 - topic: state
71 payload:
72 status: "{{ _report.status }}"
73 finished: "{{ _report.finished }}"
74 repo_size_gb: "{{ _report.repo_size_gb }}"
75 - topic: attributes
76 payload: "{{ _report }}"
77 loop_control:
78 label: "{{ item.topic }}"
79 register: _report_state
80 failed_when: false
81 no_log: true
82
83# Reporting must never fail the backup itself, but it must be visible
84- name: Warn if Home Assistant did not accept the report
85 ansible.builtin.debug:
86 msg: >-
87 WARNING: publishing to Home Assistant failed
88 (HTTP {{ (_report_discovery.results + _report_state.results) | map(attribute='status') | reject('equalto', 200) | list | join(', ') }}).
89 Check homeassistant_url/homeassistant_token in vault_runner and that the MQTT integration is set up.
90 when: >-
91 (_report_discovery.results + _report_state.results)
92 | map(attribute='status') | reject('equalto', 200) | list | length > 0
93