--- - name: Check change freeze ansible.builtin.assert: that: - not (change_freeze | bool) fail_msg: "FROZEN: {{ inventory_hostname }} is under a change freeze. Aborting." success_msg: "Change freeze check passed" - name: Check disk space on critical mountpoints ansible.builtin.shell: | df -P {{ item }} | tail -1 | awk '{print $5}' | tr -d '%' # POSIX flag — compatible with BusyBox (Alpine) and GNU coreutils register: disk_usage changed_when: false loop: - / - /var - /boot ignore_errors: true - name: Assert disk space is sufficient ansible.builtin.assert: that: - item.stdout | int < (100 - min_free_disk_percent) fail_msg: "DISK: {{ item.item }} is {{ item.stdout }}% full — minimum {{ min_free_disk_percent }}% free required" success_msg: "Disk OK: {{ item.item }} is {{ item.stdout }}% used" loop: "{{ disk_usage.results }}" when: item.rc == 0 and item.stdout | int(-1) >= 0 # skip if stdout is not numeric (e.g. /boot missing on Alpine LXC) - name: Get load average ansible.builtin.shell: | cat /proc/loadavg | awk '{print $3}' register: load_avg changed_when: false - name: Get CPU count ansible.builtin.shell: | nproc register: cpu_count changed_when: false - name: Assert load average is acceptable ansible.builtin.assert: that: - load_avg.stdout | float < (cpu_count.stdout | int * max_load_multiplier) fail_msg: "LOAD: 15min load average {{ load_avg.stdout }} exceeds threshold ({{ cpu_count.stdout }} CPUs x {{ max_load_multiplier }})" success_msg: "Load average OK: {{ load_avg.stdout }} ({{ cpu_count.stdout }} CPUs)" - name: Check for pending reboot (Debian/Ubuntu) ansible.builtin.stat: path: /var/run/reboot-required register: reboot_required when: ansible_os_family == "Debian" - name: Warn if reboot is pending ansible.builtin.debug: msg: "WARNING: {{ inventory_hostname }} has a pending reboot. Patching will proceed but reboot required afterwards." when: - ansible_os_family == "Debian" - reboot_required.stat.exists - name: Set reboot_required fact (Debian) ansible.builtin.set_fact: host_reboot_required: "{{ reboot_required.stat.exists | default(false) }}" when: ansible_os_family == "Debian" - name: Check for pending reboot (RHEL/CentOS) ansible.builtin.shell: | needs-restarting -r register: rhel_reboot_check changed_when: false failed_when: false when: ansible_os_family == "RedHat" - name: Set reboot_required fact (RHEL) ansible.builtin.set_fact: host_reboot_required: "{{ rhel_reboot_check.rc == 1 }}" when: ansible_os_family == "RedHat" - name: Preflight complete ansible.builtin.debug: msg: "Preflight passed for {{ inventory_hostname }} — proceeding with maintenance" - name: Check for Proxmox helper script marker ansible.builtin.stat: path: /usr/bin/update register: helper_script_marker - name: Log helper script detection ansible.builtin.debug: msg: "INFO: This LXC was deployed via Proxmox helper script — built-in update script detected at /usr/bin/update. Ansible will manage updates instead." when: helper_script_marker.stat.exists # ============================================================================= # OPNsense Specific Preflight Checks # ============================================================================= - name: OPN | Verify Required Variables for OPNsense ansible.builtin.assert: that: - ansible_host is defined - firewall_api_port is defined fail_msg: "Required OPNsense variables (ansible_host or firewall_api_port) are missing." when: "'opnsense' in group_names" tags: [preflight, vars] - name: OPN | Verify SSH Connectivity (Port {{ ansible_port | default(22) }}) ansible.builtin.wait_for: host: "{{ ansible_host }}" port: "{{ ansible_port | default(22) }}" timeout: 5 msg: "SSH port is not reachable. Check firewall whitelisting." when: "'opnsense' in group_names" tags: [preflight, connection] - name: OPN | Verify API Connectivity (Port {{ firewall_api_port }}) ansible.builtin.wait_for: host: "{{ ansible_host }}" port: "{{ firewall_api_port }}" timeout: 5 msg: "Web GUI/API port is not reachable. Check OPNsense settings." when: "'opnsense' in group_names" tags: [preflight, connection, api] - name: OPN | Verify httpx Library on Control Node ansible.builtin.command: python3 -c "import httpx" delegate_to: localhost run_once: true register: httpx_check failed_when: httpx_check.rc != 0 changed_when: false when: "'opnsense' in group_names" tags: [preflight, dependencies]