refactor(vars): add system/hypervisor dict inputs

This commit is contained in:
2026-02-11 05:37:18 +01:00
parent c4c96dbfb5
commit fc05708466
62 changed files with 2422 additions and 871 deletions

View File

@@ -0,0 +1,42 @@
---
- name: Validate hypervisor dict input
when: hypervisor is mapping
ansible.builtin.assert:
that:
- hypervisor.type is defined
- hypervisor.type | string | length > 0
fail_msg: "hypervisor.type is required when hypervisor is a dictionary"
quiet: true
- name: Normalize hypervisor configuration
vars:
hypervisor_input: "{{ hypervisor if hypervisor is mapping else {} }}"
hypervisor_type_legacy: "{{ (hypervisor | default('none')) if hypervisor is string else '' }}"
hypervisor_legacy_cfg:
type: "{{ hypervisor_type_legacy }}"
url: "{{ hypervisor_url | default('') }}"
username: "{{ hypervisor_username | default('') }}"
password: "{{ hypervisor_password | default('') }}"
node: "{{ hypervisor_node | default('') }}"
storage: "{{ hypervisor_storage | default('') }}"
datacenter: "{{ hypervisor_datacenter | default('') }}"
cluster: "{{ hypervisor_cluster | default('') }}"
validate_certs: "{{ hypervisor_validate_certs | default(false) | bool }}"
hypervisor_cfg_effective: >-
{{
hypervisor_defaults
| combine(hypervisor_legacy_cfg, recursive=True)
| combine(hypervisor_input, recursive=True)
}}
ansible.builtin.set_fact:
hypervisor_cfg: "{{ hypervisor_cfg_effective }}"
hypervisor: "{{ hypervisor_cfg_effective.type | string | lower }}"
hypervisor_url: "{{ hypervisor_cfg_effective.url }}"
hypervisor_username: "{{ hypervisor_cfg_effective.username }}"
hypervisor_password: "{{ hypervisor_cfg_effective.password }}"
hypervisor_node: "{{ hypervisor_cfg_effective.node }}"
hypervisor_storage: "{{ hypervisor_cfg_effective.storage }}"
hypervisor_datacenter: "{{ hypervisor_cfg_effective.datacenter }}"
hypervisor_cluster: "{{ hypervisor_cfg_effective.cluster }}"
hypervisor_validate_certs: "{{ hypervisor_cfg_effective.validate_certs | bool }}"
changed_when: false

View File

@@ -4,96 +4,59 @@
msg: Global defaults loaded.
changed_when: false
- name: Normalize hypervisor inputs
ansible.builtin.include_tasks: hypervisor.yml
- name: Normalize system inputs
ansible.builtin.include_tasks: system.yml
- name: Validate variables
ansible.builtin.assert:
that:
- install_type is defined and install_type in ["virtual", "physical"]
- hypervisor in ["libvirt", "proxmox", "vmware", "none"]
- >-
install_type is defined and (
install_type == "physical"
or hypervisor in ["libvirt", "proxmox", "vmware"]
)
- filesystem is defined and filesystem in ["btrfs", "ext4", "xfs"]
- install_drive is defined and install_drive | length > 0
- hostname is defined and hostname | length > 0
- >-
os is defined and os in [
"archlinux", "almalinux", "debian11", "debian12", "debian13", "fedora",
"rhel8", "rhel9", "rhel10", "rocky", "ubuntu", "ubuntu-lts"
]
- >-
os is defined and (
os not in ["rhel8", "rhel9", "rhel10"]
or (rhel_iso is defined and rhel_iso | length > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (boot_iso is defined and boot_iso | length > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (vm_cpus is defined and (vm_cpus | int) > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (vm_size is defined and (vm_size | float) > 0)
)
- >-
install_type is defined and (
install_type == "physical"
or (vm_memory is defined and (vm_memory | float) > 0)
)
- >-
install_type is defined and filesystem is defined and (
install_type == "physical"
or (
vm_size is defined
and (vm_size | int) >= 20
)
)
- >-
install_type is defined and (
install_type == "physical"
or (
vm_size is defined
and vm_memory is defined
and filesystem is defined
and (
filesystem != "btrfs"
or (
(vm_size | float)
>= (
(vm_memory | float / 1024 >= 16.0)
| ternary(
(vm_memory | float / 2048),
[vm_memory | float / 1024, 4.0] | max
)
+ 5.5
)
)
)
)
)
- >-
vm_ip is not defined
or vm_ip | length == 0
or (vm_nms is defined and (vm_nms | int) > 0)
fail_msg: Invalid input specified, please try again.
ansible.builtin.include_tasks: validation.yml
- name: Set OS family flags
ansible.builtin.set_fact:
is_rhel: "{{ os | lower in ['almalinux', 'fedora', 'rhel8', 'rhel9', 'rhel10', 'rocky'] }}"
is_debian: "{{ os | lower in ['debian11', 'debian12', 'debian13', 'ubuntu', 'ubuntu-lts'] }}"
is_rhel: "{{ os | lower in ['almalinux', 'fedora', 'rhel', 'rocky'] }}"
is_debian: "{{ os | lower in ['debian', 'ubuntu', 'ubuntu-lts'] }}"
changed_when: false
- name: Normalize OS version for keying
when:
- os_version is defined
- (os_version | string | length) > 0
ansible.builtin.set_fact:
os_version_major: "{{ (os_version | string).split('.')[0] }}"
changed_when: false
- name: Resolve final OS key with version
when:
- os_version is defined
- (os_version | string | length) > 0
ansible.builtin.set_fact:
os_resolved: >-
{{
'debian' + os_version | string if os == 'debian'
else 'fedora' + os_version | string if os == 'fedora'
else 'rocky' + os_version_major if os == 'rocky'
else 'almalinux' + os_version_major if os == 'almalinux'
else 'rhel' + os_version_major if os == 'rhel'
else os
}}
changed_when: false
- name: Set chroot command wrapper
ansible.builtin.set_fact:
chroot_command: >-
{{
'systemd-nspawn -D /mnt'
if chroot_tool == 'systemd-nspawn'
else chroot_tool ~ ' /mnt'
}}
changed_when: false
- name: Set Python interpreter for RHEL-based installers
when:
- ansible_python_interpreter is not defined
- os | lower in ["almalinux", "rhel8", "rhel9", "rhel10", "rocky"]
- is_rhel | bool
ansible.builtin.set_fact:
ansible_python_interpreter: /usr/bin/python3
changed_when: false
@@ -107,8 +70,10 @@
ansible_password: "{{ user_password }}"
ansible_become_password: "{{ user_password }}"
ansible_ssh_extra_args: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
changed_when: false
- name: Set connection for VMware
when: hypervisor == "vmware"
ansible.builtin.set_fact:
ansible_connection: vmware_tools
changed_when: false

View File

@@ -0,0 +1,291 @@
---
- name: Ensure system input is a dictionary
ansible.builtin.set_fact:
system: "{{ system | default({}) }}"
changed_when: false
- name: Validate system input type
ansible.builtin.assert:
that:
- system is mapping
fail_msg: "system must be a dictionary"
quiet: true
- name: Normalize base system fields
vars:
system_name_effective: >-
{{
system.name
if system.name is defined and (system.name | string | length) > 0
else (
hostname
if hostname is defined and (hostname | string | length) > 0
else inventory_hostname
)
}}
system_id_effective: >-
{{
system.id
if system.id is defined and (system.id | string | length) > 0
else (vm_id | default(''))
}}
system_cpus_effective: >-
{{
system.cpus
if system.cpus is defined and (system.cpus | int) > 0
else (vm_cpus | default(0))
}}
system_memory_mb_effective: >-
{{
system.memory_mb
if system.memory_mb is defined and (system.memory_mb | int) > 0
else (vm_memory | default(0))
}}
system_balloon_mb_effective: >-
{{
system.balloon_mb
if system.balloon_mb is defined and (system.balloon_mb | int) > 0
else (vm_ballo | default(''))
}}
system_network_effective: >-
{{
system.network
if system.network is defined and (system.network | string | length) > 0
else (vm_nif | default(''))
}}
system_vlan_effective: >-
{{
system.vlan
if system.vlan is defined and (system.vlan | string | length) > 0
else (vlan_name | default(''))
}}
system_ip_effective: >-
{{
system.ip
if system.ip is defined and (system.ip | string | length) > 0
else (vm_ip | default(''))
}}
system_prefix_effective: >-
{{
system.prefix
if system.prefix is defined and (system.prefix | int) > 0
else (vm_nms | default(''))
}}
system_gateway_effective: >-
{{
system.gateway
if system.gateway is defined and (system.gateway | string | length) > 0
else (vm_gw | default(''))
}}
system_dns_servers_effective: >-
{{
system.dns_servers
if system.dns_servers is defined
else (vm_dns | default([]))
}}
system_dns_search_effective: >-
{{
system.dns_search
if system.dns_search is defined
else (vm_dns_search | default([]))
}}
system_path_effective: >-
{{
system.path
if system.path is defined and (system.path | string | length) > 0
else (
system.hypervisor_path
if system.hypervisor_path is defined and (system.hypervisor_path | string | length) > 0
else (vm_path | default(''))
)
}}
ansible.builtin.set_fact:
hostname: "{{ system_name_effective }}"
system_cfg: >-
{{
system_defaults
| combine(system, recursive=True)
| combine(
{
'name': system_name_effective,
'id': system_id_effective,
'cpus': system_cpus_effective,
'memory_mb': system_memory_mb_effective,
'balloon_mb': system_balloon_mb_effective,
'network': system_network_effective,
'vlan': system_vlan_effective,
'ip': system_ip_effective,
'prefix': system_prefix_effective,
'gateway': system_gateway_effective,
'dns_servers': system_dns_servers_effective,
'dns_search': system_dns_search_effective,
'path': system_path_effective
},
recursive=True
)
}}
changed_when: false
- name: Normalize system disks input
vars:
system_disk_defaults:
size: 0
device: ""
mount: ""
fstype: ""
label: ""
opts: "defaults"
system_disks_raw: >-
{{
system_cfg.disks
if system_cfg.disks is defined
else []
}}
system_disks_legacy: >-
{{
[ {'size': vm_size} ]
if (system_disks_raw | length) == 0 and (vm_size is defined and (vm_size | float) > 0)
else []
}}
system_disks_effective: >-
{{
system_disks_raw
if (system_disks_raw | length) > 0
else system_disks_legacy
}}
system_disk_device_prefix: >-
{{
'/dev/vd'
if (install_type | default('')) == 'virtual' and (hypervisor | default('')) == 'libvirt'
else (
'/dev/xvd'
if (install_type | default('')) == 'virtual' and (hypervisor | default('')) == 'xen'
else (
'/dev/sd'
if (install_type | default('')) == 'virtual'
and (hypervisor | default('')) in ['proxmox', 'vmware']
else ''
)
)
}}
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
block:
- name: Validate system disks type
ansible.builtin.assert:
that:
- system_disks_effective is sequence
fail_msg: "system.disks must be a list"
quiet: true
- name: Validate system disk items
ansible.builtin.assert:
that:
- item is mapping
fail_msg: "Each system disk entry must be a dictionary"
quiet: true
loop: "{{ system_disks_effective }}"
loop_control:
label: "{{ item | to_json }}"
- name: Validate system disk count
ansible.builtin.assert:
that:
- (system_disks_effective | length) <= 26
fail_msg: "system.disks supports at most 26 entries."
quiet: true
- name: Build normalized system disk configuration
ansible.builtin.set_fact:
system_disks_cfg: "{{ system_disks_cfg | default([]) + [system_disk_cfg] }}"
vars:
disk_idx: "{{ ansible_loop.index0 }}"
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
disk_device_default: >-
{{
(
install_drive
if disk_idx == 0 and install_drive is defined and (install_drive | string | length) > 0
else (system_disk_device_prefix ~ disk_letter)
)
if (install_type | default('')) == 'virtual'
else (
install_drive
if disk_idx == 0 and install_drive is defined and (install_drive | string | length) > 0
else ''
)
}}
system_disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
system_disk_cfg_tmp: >-
{{
system_disk_cfg_base
| combine(
{
'device': (
system_disk_cfg_base.device
if system_disk_cfg_base.device | string | length > 0
else disk_device_default
),
'fstype': (
system_disk_cfg_base.fstype
if system_disk_cfg_base.fstype | string | length > 0
else (
'ext4'
if system_disk_cfg_base.mount | string | length > 0
else ''
)
)
},
recursive=True
)
}}
system_disk_partition_device: >-
{{
system_disk_cfg_tmp.device
~ ('p1' if (system_disk_cfg_tmp.device | regex_search('\\d$')) else '1')
}}
system_disk_cfg: >-
{{
system_disk_cfg_tmp
| combine(
{
'partition': system_disk_partition_device
},
recursive=True
)
}}
loop: "{{ system_disks_effective }}"
loop_control:
loop_var: item
extended: true
label: "{{ item | to_json }}"
- name: Update system configuration with normalized disks
ansible.builtin.set_fact:
system_cfg: "{{ system_cfg | combine({'disks': system_disks_cfg | default([])}, recursive=True) }}"
changed_when: false
- name: Set install_drive from system disk definition (if needed)
when:
- (install_drive is not defined) or (install_drive | string | length) == 0
- system_disks_cfg | length > 0
- system_disks_cfg[0].device | string | length > 0
ansible.builtin.set_fact:
install_drive: "{{ system_disks_cfg[0].device }}"
changed_when: false
- name: Set legacy vm_* aliases (compat)
ansible.builtin.set_fact:
vm_id: "{{ system_cfg.id }}"
vm_cpus: "{{ system_cfg.cpus }}"
vm_memory: "{{ system_cfg.memory_mb }}"
vm_ballo: "{{ system_cfg.balloon_mb }}"
vm_nif: "{{ system_cfg.network }}"
vlan_name: "{{ system_cfg.vlan }}"
vm_ip: "{{ system_cfg.ip }}"
vm_nms: "{{ system_cfg.prefix }}"
vm_gw: "{{ system_cfg.gateway }}"
vm_dns: "{{ system_cfg.dns_servers }}"
vm_dns_search: "{{ system_cfg.dns_search }}"
vm_path: "{{ system_cfg.path }}"
vm_size: "{{ (system_cfg.disks | default([]) | first | default({})).size | default(0) }}"
changed_when: false

View File

@@ -0,0 +1,234 @@
---
- name: Validate core variables
ansible.builtin.assert:
that:
- install_type is defined
- install_type in ["virtual", "physical"]
- hypervisor is defined
- hypervisor in ["libvirt", "proxmox", "vmware", "xen", "none"]
- filesystem is defined
- filesystem in ["btrfs", "ext4", "xfs"]
- install_drive is defined
- install_drive | string | length > 0
- hostname is defined
- hostname | string | length > 0
fail_msg: Invalid core variables were specified, please check your inventory/vars.
quiet: true
- name: Validate install_type/hypervisor relationship
ansible.builtin.assert:
that:
- install_type == "physical" or hypervisor in ["libvirt", "proxmox", "vmware", "xen"]
fail_msg: "hypervisor must be one of: libvirt, proxmox, vmware, xen when install_type=virtual."
quiet: true
- name: Validate OS and version inputs
ansible.builtin.assert:
that:
- os is defined
- os in ["almalinux", "alpine", "archlinux", "debian", "fedora", "opensuse", "rhel", "rocky", "ubuntu", "ubuntu-lts", "void"]
- >-
os not in ["debian", "fedora", "rocky", "almalinux", "rhel"]
or (os_version is defined and (os_version | string | length) > 0)
- >-
os_version is not defined or (os_version | string | length) == 0
or (
os == "debian" and (os_version | string) in ["10", "11", "12", "13", "unstable"]
) or (
os == "fedora" and (os_version | string) in ["40", "41", "42", "43"]
) or (
os in ["rocky", "almalinux"]
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
) or (
os == "rhel"
and (os_version | string) is match("^(8|9|10)(\\.\\d+)?$")
) or (
os in ["alpine", "archlinux", "opensuse", "ubuntu", "ubuntu-lts", "void"]
)
fail_msg: "Invalid os/os_version specified. Please check README.md for supported values."
quiet: true
- name: Validate RHEL ISO requirement
ansible.builtin.assert:
that:
- os != "rhel" or (rhel_iso is defined and (rhel_iso | string | length) > 0)
fail_msg: "rhel_iso is required when os=rhel."
quiet: true
- name: Validate Proxmox hypervisor inputs
when:
- install_type == "virtual"
- hypervisor == "proxmox"
ansible.builtin.assert:
that:
- hypervisor_cfg.url | string | length > 0
- hypervisor_cfg.username | string | length > 0
- hypervisor_cfg.password | string | length > 0
- hypervisor_cfg.node | string | length > 0
- hypervisor_cfg.storage | string | length > 0
- system_cfg.id | string | length > 0
- system_cfg.network | string | length > 0
fail_msg: "Missing required Proxmox inputs. Define hypervisor.(url,username,password,node,storage) and system.(id,network)."
quiet: true
- name: Validate VMware hypervisor inputs
when:
- install_type == "virtual"
- hypervisor == "vmware"
ansible.builtin.assert:
that:
- hypervisor_cfg.url | string | length > 0
- hypervisor_cfg.username | string | length > 0
- hypervisor_cfg.password | string | length > 0
- hypervisor_cfg.datacenter | string | length > 0
- hypervisor_cfg.cluster | string | length > 0
- hypervisor_cfg.storage | string | length > 0
- system_cfg.network | string | length > 0
fail_msg: "Missing required VMware inputs. Define hypervisor.(url,username,password,datacenter,cluster,storage) and system.network."
quiet: true
- name: Validate Xen hypervisor inputs
when:
- install_type == "virtual"
- hypervisor == "xen"
ansible.builtin.assert:
that:
- system_cfg.network | string | length > 0
fail_msg: "Missing required Xen inputs. Define system.network."
quiet: true
- name: Validate virtual installer ISO requirement
ansible.builtin.assert:
that:
- install_type == "physical" or (boot_iso is defined and (boot_iso | string | length) > 0)
fail_msg: "boot_iso is required when install_type=virtual."
quiet: true
- name: Validate firewall and feature flags
ansible.builtin.assert:
that:
- firewall_backend is defined
- firewall_backend in ["firewalld", "ufw"]
- firewall_toolkit is defined
- firewall_toolkit in ["iptables", "nftables"]
- firewall_enabled is defined
- motd_enabled is defined
- sudo_banner_enabled is defined
- luks_enabled is defined
- chroot_tool is defined
- chroot_tool in ["arch-chroot", "chroot", "systemd-nspawn"]
fail_msg: Invalid feature flags were specified, please check your inventory/vars.
quiet: true
- name: Validate system configuration exists
ansible.builtin.assert:
that:
- system_cfg is defined
- system_cfg is mapping
fail_msg: "system configuration is missing. Define system: {...} or legacy vm_* variables."
quiet: true
- name: Validate virtual system sizing
when: install_type == "virtual"
ansible.builtin.assert:
that:
- system_cfg.cpus is defined and (system_cfg.cpus | int) > 0
- system_cfg.memory_mb is defined and (system_cfg.memory_mb | int) > 0
- system_cfg.disks is defined and (system_cfg.disks | length) > 0
- (system_cfg.disks[0].size | float) > 0
- (system_cfg.disks[0].size | float) >= 20
- >-
filesystem != "btrfs"
or (
(system_cfg.disks[0].size | float)
>= (
(
(system_cfg.memory_mb | float / 1024 >= 16.0)
| ternary(
(system_cfg.memory_mb | float / 2048),
[system_cfg.memory_mb | float / 1024, 4.0] | max
)
)
+ 5.5
)
)
fail_msg: "Invalid system sizing. Check system.cpus, system.memory_mb, and system.disks[0].size."
quiet: true
- name: Validate all virtual disks have a positive size
when: install_type == "virtual"
ansible.builtin.assert:
that:
- item.size is defined
- (item.size | float) > 0
fail_msg: "Each system disk must have a positive size when install_type=virtual: {{ item | to_json }}"
quiet: true
loop: "{{ system_cfg.disks | default([]) }}"
loop_control:
label: "{{ item | to_json }}"
- name: Validate primary disk mount is not used
when:
- system_cfg.disks is defined
- system_cfg.disks | length > 0
ansible.builtin.assert:
that:
- (system_cfg.disks[0].mount | default('') | string | trim) == ''
fail_msg: "system.disks[0].mount must be empty; use system.disks[1:] for additional mounts."
quiet: true
- name: Validate disk mountpoint inputs
vars:
system_disk_mounts: >-
{{
(system_cfg.disks | default([]))
| map(attribute='mount')
| map('string')
| map('trim')
| reject('equalto', '')
| list
}}
ansible.builtin.assert:
that:
- system_disk_mounts | length == (system_disk_mounts | unique | list | length)
fail_msg: "Duplicate disk mountpoints found in system.disks."
quiet: true
- name: Validate disk mount definitions
when: system_cfg.disks is defined
vars:
reserved_mounts:
- /boot
- /boot/efi
- /home
- /swap
- /var
- /var/cache/pacman/pkg
- /var/log
- /var/log/audit
disk_mount: "{{ (item.mount | default('') | string) | trim }}"
disk_fstype: "{{ (item.fstype | default('') | string) | trim }}"
disk_device: "{{ (item.device | default('') | string) | trim }}"
disk_size: "{{ item.size | default(0) }}"
ansible.builtin.assert:
that:
- disk_mount == "" or disk_mount.startswith("/")
- disk_mount == "" or disk_mount != "/"
- disk_mount == "" or disk_mount not in reserved_mounts
- disk_mount == "" or disk_fstype in ["btrfs", "ext4", "xfs"]
- disk_mount == "" or install_type == "virtual" or (disk_device | length) > 0
- disk_mount == "" or install_type != "virtual" or (disk_size | float) > 0
fail_msg: "Invalid system disk entry: {{ item | to_json }}"
quiet: true
loop: "{{ system_cfg.disks }}"
loop_control:
label: "{{ item | to_json }}"
- name: Validate static IP requirements
when: system_cfg.ip is defined and (system_cfg.ip | string | length) > 0
ansible.builtin.assert:
that:
- system_cfg.prefix is defined
- (system_cfg.prefix | int) > 0
fail_msg: "system.prefix is required when system.ip is set."
quiet: true