/
/
/
This repo is destined for my server automations and setup.
1---
2# Backup Services Playbook
3# Comprehensive backup and restore system for all services across hosts
4# Supports selective backup/restore by service and host
5
6- name: Service Discovery Phase
7 hosts: docker_servers
8 gather_facts: yes
9 become: yes
10
11 vars:
12 # Service selection (empty = all services, comma-separated string or list)
13 backup_services: []
14
15 # Host selection (empty = all hosts)
16 backup_hosts: []
17
18 tasks:
19 - name: Quick service discovery - find Docker directories
20 find:
21 paths:
22 - "/docker"
23 file_type: directory
24 recurse: no
25 hidden: false
26 register: docker_directories
27 tags: [discovery, always]
28
29 - name: Build host service map (excluding restic-server)
30 set_fact:
31 host_service_map: "{{ host_service_map | default({}) | combine({ inventory_hostname: docker_directories.files | map(attribute='path') | map('basename') | reject('equalto', 'restic-server') | list }) }}"
32 cacheable: true
33 tags: [discovery, always]
34
35- name: Backup Operations Phase
36 hosts: localhost
37 # environment:
38 # LANG: en_US.UTF-8
39 # LC_ALL: en_US.UTF-8
40 # PYTHONIOENCODING: utf-8
41 gather_facts: false
42
43 vars:
44 # Default backup mode (backup|restore)
45 backup_mode: "backup"
46
47 # Service selection (empty = all services, comma-separated string or list)
48 backup_services: []
49
50 # Host selection (empty = all hosts)
51 backup_hosts: []
52
53 # Restic configuration
54 restic_repo: "{{ restic_backup_repo | default('rest:http://storage.home:8000/backup') }}"
55 restic_password: "{{ restic_backup_password | default('changeme') }}"
56
57 # Backup settings
58 backup_health_check: true
59 backup_health_timeout_sec: 300
60 backup_health_interval_sec: 10
61
62 tasks:
63 - name: Debug backup_services variable
64 debug:
65 var: backup_services
66 tags: [discovery, always]
67
68 - name: Normalize backup_services to list format
69 set_fact:
70 _backup_services_list: "{{ backup_services | split(',') if backup_services is string else backup_services }}"
71 tags: [discovery, always]
72
73 - name: Debug host service maps
74 debug:
75 msg: |
76 Host service maps:
77 {% for host in groups['docker_servers'] %}
78 - {{ host }}: {{ hostvars[host].host_service_map | default('NOT_DEFINED') }}
79 {% endfor %}
80 tags: [discovery, always]
81
82 - name: Build aggregated host service map
83 set_fact:
84 host_service_map: "{{ host_service_map | default({}) }}"
85 tags: [discovery, always]
86
87 - name: Aggregate host service maps
88 set_fact:
89 host_service_map: "{{ host_service_map | combine(hostvars[item].host_service_map) }}"
90 loop: "{{ groups['docker_servers'] }}"
91 when: hostvars[item].host_service_map is defined
92 tags: [discovery, always]
93
94 - name: Debug aggregated host service map
95 debug:
96 msg: "Aggregated host_service_map: {{ host_service_map }}"
97 tags: [discovery, always]
98
99 - name: Convert host_service_map to items
100 set_fact:
101 host_service_items: "{{ host_service_map | dict2items }}"
102 tags: [discovery, always]
103
104 - name: Build target host-service pairs
105 set_fact:
106 target_host_services: "{{ [] }}"
107 tags: [discovery, always]
108
109 - name: Add host-service pairs for all services
110 include_tasks: roles/backup/tasks/build-target-pairs.yml
111 loop: "{{ host_service_items }}"
112 loop_control:
113 loop_var: host_item
114 when:
115 - backup_services | length == 0
116 tags: [discovery, always]
117
118 - name: Add host-service pairs for specific services
119 set_fact:
120 target_host_services: "{{ target_host_services + [{'host': item.0.key, 'service': item.1}] }}"
121 with_nested:
122 - "{{ host_service_items }}"
123 - "{{ _backup_services_list }}"
124 when:
125 - item.1 in item.0.value
126 - _backup_services_list | length > 0
127 tags: [discovery, always]
128
129 - name: Validate backup mode
130 assert:
131 that:
132 - backup_mode in ['backup', 'restore']
133 fail_msg: "backup_mode must be 'backup' or 'restore'"
134 tags: [always, validation]
135
136 - name: Display service discovery results
137 debug:
138 msg: |
139 ð Service Discovery Results
140
141 ð Host-Service Map:
142 {% for host, services in host_service_map.items() %}
143 - {{ host }}: {{ services | join(', ') }}
144 {% endfor %}
145
146 ð¯ Target Host-Service Pairs ({{ target_host_services | length }}):
147 {% for pair in target_host_services %}
148 - {{ pair.host }} â {{ pair.service }}
149 {% else %}
150 - No matching services found
151 {% endfor %}
152
153 ð Operation Details:
154 - Mode: {{ backup_mode }}
155 - Requested Services: {{ _backup_services_list | default('ALL SERVICES') }}
156 - Requested Hosts: {{ backup_hosts | default('ALL HOSTS') }}
157 tags: [always, discovery]
158
159 - name: Fail if no services found
160 fail:
161 msg: "No services found matching the criteria. Requested services: {{ _backup_services_list | default('ALL') }}, Requested hosts: {{ backup_hosts | default('ALL') }}"
162 when: target_host_services | length == 0
163 tags: [always, validation]
164
165 - name: Group services by host
166 set_fact:
167 host_services_map: "{{ host_services_map | default({}) | combine({ item.host: host_services_map[item.host] | default([]) + [item.service] }) }}"
168 loop: "{{ target_host_services }}"
169 loop_control:
170 label: "{{ item.host }}:{{ item.service }}"
171 tags: [discovery, always]
172
173 - name: Create dynamic host groups for backup operations
174 add_host:
175 name: "{{ item.key }}"
176 groups: backup_targets
177 backup_operation_mode: "{{ backup_mode }}"
178 backup_services_list: "{{ item.value }}"
179 restic_repo: "{{ restic_repo }}"
180 restic_password: "{{ restic_password }}"
181 backup_health_check: "{{ backup_health_check }}"
182 backup_health_timeout_sec: "{{ backup_health_timeout_sec }}"
183 backup_health_interval_sec: "{{ backup_health_interval_sec }}"
184 loop: "{{ host_services_map | dict2items }}"
185 loop_control:
186 label: "{{ item.key }}:{{ item.value | join(',') }}"
187 when: target_host_services | length > 0 and "discovery" not in ansible_run_tags
188 tags: [backup, operations]
189
190- name: Backup Operations Execution
191 hosts: backup_targets
192 gather_facts: false
193 become: yes
194
195 tasks:
196 - name: Run backup role on target host
197 include_role:
198 name: backup
199 tags: [backup, operations]
200
201- name: Final Summary
202 hosts: localhost
203 gather_facts: false
204
205 tasks:
206 - name: Display operation summary
207 debug:
208 msg: |
209 {% if "discovery" in ansible_run_tags %}
210 ð Service Discovery Complete!
211
212 ð Discovery Results:
213 - Available Services: {{ target_host_services | length }}
214 - Hosts Involved: {{ target_host_services | map(attribute='host') | unique | list | join(', ') }}
215
216 ð Next Steps:
217 - Run backup: ansible-playbook backup.yml -e "backup_services=jellyfin"
218 - Run restore: ansible-playbook backup.yml -e "backup_mode=restore backup_services=jellyfin"
219 {% else %}
220 â
Backup Operation Complete!
221
222 ð Summary:
223 - Services Processed: {{ target_host_services | length }}
224 - Hosts Involved: {{ target_host_services | map(attribute='host') | unique | list | join(', ') }}
225
226 ð§ Next Steps:
227 - Verify operation completed successfully
228 - Check service health and functionality
229
230 ð Management Commands:
231 - View snapshots: restic snapshots
232 - Restore specific snapshot: restic restore <snapshot-id> --target /path
233 - Check repository: restic check
234 - Prune old backups: restic forget --keep-last 10 --prune
235 {% endif %}
236 tags: [always, summary]
237