GuestOps/deploy/ansible/guestops-operations.yml

228 lines
7.8 KiB
YAML

---
- name: Install GuestOps backup and monitoring operations
hosts: guestops
become: true
gather_facts: true
vars:
guestops_root: /srv/guestops
guestops_current: "{{ guestops_root }}/current"
guestops_config_root: /etc/guestops
guestops_backup_root: /var/backups/guestops
guestops_gnupg_root: /var/lib/guestops-backup/gnupg
guestops_zabbix_service: zabbix-agent2
guestops_zabbix_include_directory: /etc/zabbix/zabbix_agent2.d
guestops_enable_backup_schedule: false
guestops_durable_logs_configured: false
pre_tasks:
- name: Validate required operational variables
ansible.builtin.assert:
that:
- guestops_release_commit is match('^[0-9a-f]{40}$')
- guestops_backup_recipient is match('^[A-Fa-f0-9]{40}$')
- guestops_recovery_public_key | length > 0
- guestops_evidence_directory | length > 0
- guestops_durable_logs_configured | bool
fail_msg: Release commit, recovery public key/fingerprint, evidence directory and reviewed durable logging are required.
- name: Confirm selected release link
ansible.builtin.stat:
path: "{{ guestops_current }}"
follow: false
register: guestops_selected_release
- name: Require deployed release before operations installation
ansible.builtin.assert:
that:
- guestops_selected_release.stat.exists
- guestops_selected_release.stat.islnk
- guestops_selected_release.stat.lnk_source == guestops_root + '/releases/' + guestops_release_commit
fail_msg: /srv/guestops/current must select the exact approved release commit.
- name: Confirm selected release target exists
ansible.builtin.stat:
path: "{{ guestops_root }}/releases/{{ guestops_release_commit }}"
follow: true
register: guestops_release_target
- name: Require selected release directory
ansible.builtin.assert:
that:
- guestops_release_target.stat.exists
- guestops_release_target.stat.isdir
fail_msg: The selected release target must be an installed directory.
- name: Inspect pre-provisioned private operational files
ansible.builtin.stat:
path: "{{ item.path }}"
follow: false
loop:
- { path: /etc/guestops/backup.env, mode: "0600" }
- { path: /etc/guestops/backup-transfer.env, mode: "0600" }
- { path: /etc/guestops/backup-transfer.key, mode: "0600" }
- { path: /etc/guestops/backup-known-hosts, mode: "0644" }
register: guestops_operational_files
- name: Require private backup and transfer configuration
ansible.builtin.assert:
that:
- item.stat.exists
- item.stat.isreg
- not item.stat.islnk
- item.stat.mode == item.item.mode
fail_msg: "{{ item.item.path }} must be a regular non-symlink file with mode {{ item.item.mode }}."
loop: "{{ guestops_operational_files.results }}"
no_log: true
tasks:
- name: Create private backup directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0700"
loop:
- "{{ guestops_backup_root }}"
- "{{ guestops_gnupg_root }}"
- name: Stage recovery public key
ansible.builtin.copy:
src: "{{ guestops_recovery_public_key }}"
dest: /etc/guestops/recovery-public.asc
owner: root
group: root
mode: "0600"
- name: Import recovery public key only
ansible.builtin.command:
argv:
- gpg
- --batch
- --homedir
- "{{ guestops_gnupg_root }}"
- --import
- /etc/guestops/recovery-public.asc
no_log: true
changed_when: true
- name: Verify configured recovery fingerprint
ansible.builtin.command:
argv:
- gpg
- --batch
- --homedir
- "{{ guestops_gnupg_root }}"
- --list-keys
- "{{ guestops_backup_recipient }}"
changed_when: false
no_log: true
- name: Remove staged recovery public key
ansible.builtin.file:
path: /etc/guestops/recovery-public.asc
state: absent
- name: Install GuestOps systemd units
ansible.builtin.copy:
src: "{{ playbook_dir }}/../systemd/{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
owner: root
group: root
mode: "0644"
loop:
- guestops-backup.service
- guestops-backup.timer
- guestops-backup-transfer.service
- guestops-backup-transfer.timer
- guestops-monitor-status.service
- guestops-monitor-status.timer
register: guestops_systemd_units
- name: Verify installed systemd units
ansible.builtin.command:
argv:
- systemd-analyze
- verify
- /etc/systemd/system/guestops-backup.service
- /etc/systemd/system/guestops-backup.timer
- /etc/systemd/system/guestops-backup-transfer.service
- /etc/systemd/system/guestops-backup-transfer.timer
- /etc/systemd/system/guestops-monitor-status.service
- /etc/systemd/system/guestops-monitor-status.timer
changed_when: false
- name: Reload systemd unit definitions
ansible.builtin.systemd_service:
daemon_reload: true
when: guestops_systemd_units.changed
- name: Enable transfer retry and monitoring timers
ansible.builtin.systemd_service:
name: "{{ item }}"
enabled: true
state: started
loop:
- guestops-backup-transfer.timer
- guestops-monitor-status.timer
- name: Select backup schedule state
ansible.builtin.systemd_service:
name: guestops-backup.timer
enabled: "{{ guestops_enable_backup_schedule | bool }}"
state: "{{ 'started' if guestops_enable_backup_schedule | bool else 'stopped' }}"
- name: Require Zabbix include directory
ansible.builtin.stat:
path: "{{ guestops_zabbix_include_directory }}"
register: guestops_zabbix_directory
- name: Confirm Zabbix agent configuration path
ansible.builtin.assert:
that:
- guestops_zabbix_directory.stat.exists
- guestops_zabbix_directory.stat.isdir
fail_msg: Override guestops_zabbix_include_directory for the installed Zabbix agent.
- name: Install restricted Zabbix status item
ansible.builtin.copy:
src: "{{ playbook_dir }}/../systemd/zabbix-agent-guestops.conf.example"
dest: "{{ guestops_zabbix_include_directory }}/guestops.conf"
owner: root
group: root
mode: "0644"
register: guestops_zabbix_configuration
- name: Restart Zabbix agent after configuration change
ansible.builtin.service:
name: "{{ guestops_zabbix_service }}"
enabled: true
state: restarted
when: guestops_zabbix_configuration.changed
- name: Generate initial non-sensitive monitoring status
ansible.builtin.command:
argv: [systemctl, start, guestops-monitor-status.service]
changed_when: true
- name: Read installed timer schedule
ansible.builtin.command:
argv: [systemctl, list-timers, --all, --no-pager, guestops-backup.timer, guestops-backup-transfer.timer, guestops-monitor-status.timer]
register: guestops_timer_status
changed_when: false
- name: Retain monitoring status on the Ansible controller
ansible.builtin.fetch:
src: /run/guestops-monitor/status.json
dest: "{{ guestops_evidence_directory }}/monitor-status.json"
flat: true
- name: Retain timer status on the Ansible controller
ansible.builtin.copy:
content: "{{ guestops_timer_status.stdout }}\n"
dest: "{{ guestops_evidence_directory }}/systemd-timers.txt"
mode: "0600"
delegate_to: localhost
become: false