/
/
/
Ansible role that can run restic backups and push it to a remote storage server.
1---
2# Back up one data path outside /docker, hot (tags: <name>, data).
3# Only the path's own excludes apply - never the global /docker patterns.
4
5- name: "{{ dp.name }} - Reset per-path state"
6 ansible.builtin.set_fact:
7 _dp_summary: {}
8 _dp_error: ""
9
10- name: "{{ dp.name }} - Check the path"
11 ansible.builtin.stat:
12 path: "{{ dp.path }}"
13 register: _dp_stat
14
15- name: "{{ dp.name }} - restic backup of {{ dp.path }}"
16 when: _dp_stat.stat.exists and _dp_stat.stat.isdir
17 block:
18 - name: "{{ dp.name }} - restic backup"
19 ansible.builtin.command:
20 argv: >-
21 {{ [backup_restic_bin, 'backup', '--json', '--quiet',
22 '--host', inventory_hostname, '--tag', dp.name, '--tag', 'data',
23 '--one-file-system', '--exclude-caches', '--exclude-if-present', '.nobackup']
24 + ((dp.excludes | default([])) | map('regex_replace', '^', '--exclude=') | list)
25 + restic_backup_args
26 + [dp.path] }}
27 environment:
28 RESTIC_REPOSITORY: "{{ restic_repo }}"
29 RESTIC_PASSWORD: "{{ restic_password }}"
30 register: _dp_backup
31 changed_when: true
32
33 - name: "{{ dp.name }} - Record the result"
34 ansible.builtin.set_fact:
35 _dp_summary: >-
36 {{ (_dp_backup.stdout_lines | select('search', '"message_type":"summary"') | list | last | default('{}')) | from_json }}
37 _dp_error: ""
38
39 rescue:
40 - name: "{{ dp.name }} - Record the failure"
41 ansible.builtin.set_fact:
42 _dp_summary: {}
43 _dp_error: >-
44 {{ (ansible_failed_result.stderr | default(ansible_failed_result.msg | default('unknown error')))
45 | regex_replace('rest:[^ ]*', 'rest:<repo>') | truncate(300) }}
46
47- name: "{{ dp.name }} - Add to the results"
48 ansible.builtin.set_fact:
49 backup_results: >-
50 {{ backup_results + [{
51 'name': dp.name, 'kind': 'data',
52 'status': 'failed' if (_dp_error | default('') or not (_dp_stat.stat.exists | default(false))) else 'ok',
53 'error': _dp_error | default('') if _dp_stat.stat.exists | default(false) else ('path missing: ' ~ dp.path),
54 'snapshot': (_dp_summary | default({})).snapshot_id | default(''),
55 'added_bytes': (_dp_summary | default({})).data_added | default(0),
56 'processed_bytes': (_dp_summary | default({})).total_bytes_processed | default(0),
57 'files': (_dp_summary | default({})).total_files_processed | default(0),
58 'seconds': ((_dp_summary | default({})).total_duration | default(0)) | round(1),
59 'stopped': [] }] }}
60