--- - name: Reboot Linux hosts if required (or forced) hosts: linux_hosts gather_facts: true vars: force_reboot: false # override with -e force_reboot=true to reboot all hosts tasks: - name: Get running kernel version ansible.builtin.command: uname -r register: running_kernel changed_when: false - name: Detect if running inside an LXC container ansible.builtin.shell: | grep -q 'lxc' /proc/1/environ 2>/dev/null \ || systemd-detect-virt --quiet --container 2>/dev/null \ || [ -f /run/.containerenv ] \ && echo "lxc" || echo "not-lxc" register: virt_detect changed_when: false failed_when: false - name: Set is_lxc fact ansible.builtin.set_fact: is_lxc: "{{ 'lxc' in virt_detect.stdout }}" # ── Debian/Ubuntu ────────────────────────────────────────────────────────── - name: Get installed kernel version (Debian/Ubuntu) ansible.builtin.shell: | dpkg -l 'linux-image-*' 2>/dev/null \ | awk '/^ii/ {print $3}' \ | sort -V | tail -1 register: installed_kernel_deb changed_when: false when: ansible_os_family == "Debian" - name: Normalize installed kernel version (Debian/Ubuntu) # dpkg reports e.g. "6.12.74-2", uname -r reports "6.12.74+deb13+1-amd64" # Extract just the base X.Y.Z for comparison ansible.builtin.set_fact: installed_kernel_version: "{{ installed_kernel_deb.stdout | trim }}" installed_kernel_base: "{{ installed_kernel_deb.stdout | trim | regex_replace('^(\\d+\\.\\d+\\.\\d+).*', '\\1') }}" running_kernel_base: "{{ running_kernel.stdout | trim | regex_replace('^(\\d+\\.\\d+\\.\\d+).*', '\\1') }}" when: ansible_os_family == "Debian" # ── Alpine ───────────────────────────────────────────────────────────────── - name: Get installed kernel version (Alpine) ansible.builtin.shell: | apk info --installed 2>/dev/null \ | grep '^linux-' | sort -V | tail -1 | awk '{print $1}' register: installed_kernel_alpine changed_when: false when: ansible_os_family == "Alpine" - name: Normalize installed kernel version (Alpine) ansible.builtin.set_fact: installed_kernel_version: "{{ installed_kernel_alpine.stdout | trim }}" installed_kernel_base: "{{ installed_kernel_alpine.stdout | trim | regex_replace('^(\\d+\\.\\d+\\.\\d+).*', '\\1') }}" running_kernel_base: "{{ running_kernel.stdout | trim | regex_replace('^(\\d+\\.\\d+\\.\\d+).*', '\\1') }}" when: ansible_os_family == "Alpine" # ── RHEL/CentOS ──────────────────────────────────────────────────────────── - name: Get installed kernel version (RHEL/CentOS) ansible.builtin.shell: | rpm -q --last kernel 2>/dev/null \ | head -1 | awk '{print $1}' | sed 's/kernel-//' register: installed_kernel_rhel changed_when: false when: ansible_os_family == "RedHat" - name: Normalize installed kernel version (RHEL/CentOS) ansible.builtin.set_fact: installed_kernel_version: "{{ installed_kernel_rhel.stdout | trim }}" installed_kernel_base: "{{ installed_kernel_rhel.stdout | trim | regex_replace('^(\\d+\\.\\d+\\.\\d+).*', '\\1') }}" running_kernel_base: "{{ running_kernel.stdout | trim | regex_replace('^(\\d+\\.\\d+\\.\\d+).*', '\\1') }}" when: ansible_os_family == "RedHat" # ── Fallbacks ────────────────────────────────────────────────────────────── - name: Set fallback for unknown/LXC hosts ansible.builtin.set_fact: installed_kernel_version: "unknown" installed_kernel_base: "unknown" running_kernel_base: "unknown" when: installed_kernel_version is not defined # ── Determine reboot need ────────────────────────────────────────────────── - name: Determine if reboot is needed ansible.builtin.set_fact: reboot_needed: >- {{ not is_lxc | bool and installed_kernel_version != 'unknown' and installed_kernel_base != '' and installed_kernel_base != running_kernel_base }} - name: Report reboot status ansible.builtin.debug: msg: >- {{ inventory_hostname }}: running={{ running_kernel.stdout | trim }} (base={{ running_kernel_base }}), installed={{ installed_kernel_version }} (base={{ installed_kernel_base }}), is_lxc={{ is_lxc }}, reboot_needed={{ reboot_needed }}, force_reboot={{ force_reboot }} — {{ 'WILL reboot' if (reboot_needed | bool or force_reboot | bool) else 'Skipping reboot' }} - name: Reboot host ansible.builtin.reboot: reboot_timeout: 300 pre_reboot_delay: 10 post_reboot_delay: 30 msg: "Scheduled reboot — initiated by Ansible" when: reboot_needed | bool or force_reboot | bool - name: Verify kernel version after reboot ansible.builtin.command: uname -r register: post_reboot_kernel changed_when: false when: reboot_needed | bool or force_reboot | bool - name: Report post-reboot kernel ansible.builtin.debug: msg: "{{ inventory_hostname }} rebooted — now running kernel {{ post_reboot_kernel.stdout | trim }}" when: reboot_needed | bool or force_reboot | bool