Files
mrcharles 3672227f94 feat(preflight): add host-class detection role
Port PreflightCheck.sh and pi-detect.sh to the preflight role.

Detects at runtime:
- Physical Dell host (dmidecode, NOT Proxmox, NOT Pi)
- Proxmox VE host (dpkg-query proxmox-ve)
- Virtual guest (virt-what: hyperv/kvm)
- KVM guest specifically
- Raspberry Pi (device-tree model file)
- Distro: Debian, Ubuntu, Kali
- User existence: localuser, subodev

Derives from inventory groups:
- NTP server, DHCP server, DNS server
- LibreNMS server, Wazuh server
- Dev workstation

All facts are cacheable so downstream roles can branch on them without
re-running detection commands.

🤖 Generated with [Crush](https://github.com/charmassociates/crush)

Assisted-by: GLM-5 via Crush <crush@charm.land>
2026-07-30 12:15:50 -05:00

130 lines
5.5 KiB
YAML

---
# Preflight checks — equivalent of Project-Includes/PreflightCheck.sh +
# pi-detect.sh. In Ansible, `become: true` replaces the root-user check.
# Here we additionally assert the target is a supported host type and detect
# the full set of host-class booleans that downstream roles branch on.
# ---------------------------------------------------------------------------
# Assert supported OS
# ---------------------------------------------------------------------------
- name: Assert host runs a supported Debian-family distribution
ansible.builtin.assert:
that:
- ansible_facts.os_family == "Debian"
fail_msg: >-
Refusing to provision {{ inventory_hostname }}: only Debian-family hosts
are supported (got OS family "{{ ansible_facts.os_family }}").
success_msg: >-
Preflight OK — {{ inventory_hostname }} is
{{ ansible_facts.distribution }} {{ ansible_facts.distribution_version }}.
# ---------------------------------------------------------------------------
# Dynamic detections (run on the target host)
# ---------------------------------------------------------------------------
- name: Detect physical Dell host (dmidecode) # noqa var-naming[no-role-prefix]
ansible.builtin.shell: |
set -o pipefail
/usr/sbin/dmidecode -t system 2>/dev/null | grep -c Dell || true
register: _dell_check
changed_when: false
failed_when: false
- name: Detect Proxmox VE host # noqa var-naming[no-role-prefix]
ansible.builtin.shell: |
# Proxmox VE is installed if the proxmox-ve package is present OR the
# /etc/pve cluster FS is mounted.
dpkg-query -W -f='${Status}' proxmox-ve 2>/dev/null | grep -c 'install ok installed' || true
register: _proxmox_check
changed_when: false
failed_when: false
- name: Detect virtualization type (virt-what) # noqa var-naming[no-role-prefix]
ansible.builtin.command: virt-what
register: _virt_what
changed_when: false
failed_when: false
- name: Detect Raspberry Pi (device-tree model file) # noqa var-naming[no-role-prefix]
ansible.builtin.stat:
path: /sys/firmware/devicetree/base/model
register: _raspi_model
- name: Detect existing system users
ansible.builtin.getent:
database: passwd
# ---------------------------------------------------------------------------
# Compute host-class facts
# ---------------------------------------------------------------------------
- name: Set dynamic host-class facts # noqa var-naming[no-role-prefix]
ansible.builtin.set_fact:
_dell_count: "{{ _dell_check.stdout | default('0') | int }}"
_pve_count: "{{ _proxmox_check.stdout | default('0') | int }}"
_virt_output: "{{ _virt_what.stdout | default('') }}"
cacheable: true
- name: Record host-class facts for downstream roles # noqa var-naming[no-role-prefix]
ansible.builtin.set_fact:
# Physical hardware (bare-metal Dell server, NOT a VM and NOT Proxmox PVE)
is_physical_host: >-
{{
(_dell_check.stdout | default('0') | int) > 0
and (_proxmox_check.stdout | default('0') | int) == 0
and not (_raspi_model.stat.exists)
}}
# Proxmox VE hypervisor (bare-metal Dell running PVE)
is_proxmox_host: >-
{{
(_proxmox_check.stdout | default('0') | int) > 0
}}
# Raspberry Pi / ARM SBC
is_raspi: "{{ _raspi_model.stat.exists }}"
# Virtualization details from virt-what
virt_type: "{{ _virt_what.stdout | default('') }}"
is_virt_guest: >-
{{
(_virt_what.stdout | default('')) is search('hyperv|kvm')
}}
is_kvm_guest: >-
{{
(_virt_what.stdout | default('')) is search('kvm')
}}
# Distro flags
is_ubuntu: "{{ ansible_facts.distribution == 'Ubuntu' }}"
is_kali: "{{ ansible_facts.distribution == 'Kali' }}"
is_debian: "{{ ansible_facts.distribution == 'Debian' }}"
is_proxmox_ve: "{{ ansible_facts.distribution == 'Debian' and (_proxmox_check.stdout | default('0') | int) > 0 }}"
# User existence flags (for SSH + shell config roles)
localuser_exists: "{{ 'localuser' in getent_passwd }}"
subodev_exists: "{{ 'subodev' in getent_passwd }}"
cacheable: true
# ---------------------------------------------------------------------------
# Inventory-group-derived facts
# These let roles skip config for infrastructure servers (NTP, DHCP, DNS)
# based on inventory group membership rather than hostname pattern matching.
# ---------------------------------------------------------------------------
- name: Record inventory-group facts for downstream roles # noqa var-naming[no-role-prefix]
ansible.builtin.set_fact:
is_ntp_server: "{{ 'ntp_servers' in group_names }}"
is_dhcp_server: "{{ 'dhcp_servers' in group_names }}"
is_dns_server: "{{ 'dns_servers' in group_names }}"
is_librenms_server: "{{ 'librenms_server' in group_names }}"
is_wazuh_server: "{{ 'wazuh_server' in group_names }}"
is_dev_workstation: "{{ 'dev_workstations' in group_names }}"
cacheable: true
# ---------------------------------------------------------------------------
# Report
# ---------------------------------------------------------------------------
- name: Report detected host class
ansible.builtin.debug:
msg: >-
host-class: physical={{ is_physical_host }} proxmox={{ is_proxmox_host }}
raspi={{ is_raspi }} virt='{{ virt_type | default('none') }}'
ubuntu={{ is_ubuntu }} kali={{ is_kali }}
ntp_server={{ is_ntp_server }} dhcp_server={{ is_dhcp_server }}
librenms={{ is_librenms_server }} wazuh={{ is_wazuh_server }}
dev_ws={{ is_dev_workstation }}