Files
Ansible-Bootstrap/roles/global_defaults/tasks/validation.yml

488 lines
16 KiB
YAML

---
- name: Validate core variables
ansible.builtin.assert:
that:
- system_cfg is defined
- system_cfg is mapping
- system_cfg.type is defined
- system_cfg.type in ["virtual", "physical"]
- hypervisor_cfg is defined
- hypervisor_cfg is mapping
- hypervisor_type is defined
- hypervisor_type 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 hypervisor relationship
ansible.builtin.assert:
that:
- system_cfg.type == "physical" or hypervisor_type in ["libvirt", "proxmox", "vmware", "xen"]
fail_msg: "hypervisor.type must be one of: libvirt, proxmox, vmware, xen when system.type=virtual."
quiet: true
- name: Validate hypervisor schema
vars:
hypervisor_allowed_keys:
- type
- url
- username
- password
- host
- storage
- datacenter
- cluster
- validate_certs
- ssh
hypervisor_keys: "{{ (hypervisor | default({})) | dict2items | map(attribute='key') | list }}"
hypervisor_unknown_keys: "{{ hypervisor_keys | difference(hypervisor_allowed_keys) }}"
ansible.builtin.assert:
that:
- hypervisor_unknown_keys | length == 0
fail_msg: "Unsupported hypervisor keys: {{ hypervisor_unknown_keys | join(', ') }}"
quiet: true
- name: Validate system schema
vars:
system_allowed_keys:
- type
- os
- os_version
- name
- id
- cpus
- memory
- balloon
- network
- vlan
- ip
- prefix
- gateway
- dns
- path
- packages
- disks
- user
- root
- luks
- features
system_keys: "{{ (system | default({})) | dict2items | map(attribute='key') | list }}"
system_unknown_keys: "{{ system_keys | difference(system_allowed_keys) }}"
ansible.builtin.assert:
that:
- system_unknown_keys | length == 0
fail_msg: "Unsupported system keys: {{ system_unknown_keys | join(', ') }}"
quiet: true
- name: Reject deprecated top-level input keys
vars:
deprecated_input_keys:
- install_type
- vm_ip
- vm_id
- vm_name
- vm_cpus
- memory_mb
- balloon_mb
- dns_servers
- dns_search
- extra_packages
- user_name
- user_password
- user_public_key
- root_password
- luks_enabled
- luks_passphrase
- luks_mapper_name
- luks_auto_decrypt
- luks_auto_decrypt_method
- luks_tpm2_device
- luks_tpm2_pcrs
- luks_keyfile_size
- firewall_enabled
- firewall_backend
- firewall_toolkit
- ssh_enabled
- cis
- selinux_enabled
- zstd_enabled
- swap_enabled
- motd_enabled
- sudo_banner_enabled
- chroot_tool
- hypervisor_url
- hypervisor_username
- hypervisor_password
- hypervisor_node
- hypervisor_storage
- hypervisor_datacenter
- hypervisor_cluster
- hypervisor_validate_certs
- hypervisor_ssh
- hypervisor_path
top_level_input_keys: "{{ (hostvars[inventory_hostname] | dict2items | map(attribute='key') | list) }}"
deprecated_input_keys_present: "{{ top_level_input_keys | intersect(deprecated_input_keys) }}"
ansible.builtin.assert:
that:
- deprecated_input_keys_present | length == 0
fail_msg: >-
Unsupported top-level keys found: {{ deprecated_input_keys_present | join(', ') }}.
Use only the `system` and `hypervisor` dictionaries for runtime configuration.
quiet: true
- name: Validate nested system schema
vars:
dns_allowed_keys: [servers, search]
user_allowed_keys: [name, password, public_key]
root_allowed_keys: [password]
luks_allowed_keys:
- enabled
- passphrase
- mapper_name
- auto_decrypt
- auto_decrypt_method
- tpm2_device
- tpm2_pcrs
- keyfile_size
- options
- type
- cipher
- hash
- iter_time
- key_size
- pbkdf
- use_urandom
- verify_passphrase
features_allowed_keys:
- cis
- selinux
- firewall
- ssh
- zstd
- swap
- banner
- chroot
feature_leaf_allowed:
cis: [enabled]
selinux: [enabled]
firewall: [enabled, backend, toolkit]
ssh: [enabled]
zstd: [enabled]
swap: [enabled]
banner: [motd, sudo]
chroot: [tool]
dns_keys: "{{ (system.dns | default({})) | dict2items | map(attribute='key') | list }}"
user_keys: "{{ (system.user | default({})) | dict2items | map(attribute='key') | list }}"
root_keys: "{{ (system.root | default({})) | dict2items | map(attribute='key') | list }}"
luks_keys: "{{ (system.luks | default({})) | dict2items | map(attribute='key') | list }}"
features_keys: "{{ (system.features | default({})) | dict2items | map(attribute='key') | list }}"
dns_unknown: "{{ dns_keys | difference(dns_allowed_keys) }}"
user_unknown: "{{ user_keys | difference(user_allowed_keys) }}"
root_unknown: "{{ root_keys | difference(root_allowed_keys) }}"
luks_unknown: "{{ luks_keys | difference(luks_allowed_keys) }}"
features_unknown: "{{ features_keys | difference(features_allowed_keys) }}"
ansible.builtin.assert:
that:
- system.dns is not defined or system.dns is mapping
- system.user is not defined or system.user is mapping
- system.root is not defined or system.root is mapping
- system.luks is not defined or system.luks is mapping
- system.features is not defined or system.features is mapping
- dns_unknown | length == 0
- user_unknown | length == 0
- root_unknown | length == 0
- luks_unknown | length == 0
- features_unknown | length == 0
fail_msg: >-
Invalid nested system schema.
dns_unknown={{ dns_unknown | join(',') }},
user_unknown={{ user_unknown | join(',') }},
root_unknown={{ root_unknown | join(',') }},
luks_unknown={{ luks_unknown | join(',') }},
features_unknown={{ features_unknown | join(',') }}
quiet: true
- name: Validate feature leaf schemas
vars:
system_features: "{{ system.features | default({}) }}"
feature_keys: "{{ system_features | dict2items | map(attribute='key') | list }}"
feature_leaf_allowed:
cis: [enabled]
selinux: [enabled]
firewall: [enabled, backend, toolkit]
ssh: [enabled]
zstd: [enabled]
swap: [enabled]
banner: [motd, sudo]
chroot: [tool]
ansible.builtin.assert:
that:
- >-
(
feature_keys
| map('extract', system_features)
| select('mapping')
| list
| length
)
== (feature_keys | length)
- >-
(
(
system_features.cis | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.cis)
) | length == 0
- >-
(
(
system_features.selinux | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.selinux)
) | length == 0
- >-
(
(
system_features.firewall | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.firewall)
) | length == 0
- >-
(
(
system_features.ssh | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.ssh)
) | length == 0
- >-
(
(
system_features.zstd | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.zstd)
) | length == 0
- >-
(
(
system_features.swap | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.swap)
) | length == 0
- >-
(
(
system_features.banner | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.banner)
) | length == 0
- >-
(
(
system_features.chroot | default({})
) | dict2items | map(attribute='key') | list | difference(feature_leaf_allowed.chroot)
) | length == 0
fail_msg: "Invalid system.features schema detected."
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:
- system_cfg.type == "virtual"
- hypervisor_type == "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.host | 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,host,storage) and system.(id,network)."
quiet: true
- name: Validate VMware hypervisor inputs
when:
- system_cfg.type == "virtual"
- hypervisor_type == "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:
- system_cfg.type == "virtual"
- hypervisor_type == "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:
- system_cfg.type == "physical" or (boot_iso is defined and (boot_iso | string | length) > 0)
fail_msg: "boot_iso is required when system.type=virtual."
quiet: true
- name: Validate firewall and feature flags
ansible.builtin.assert:
that:
- system_cfg.features.firewall.backend is defined
- system_cfg.features.firewall.backend in ["firewalld", "ufw"]
- system_cfg.features.firewall.toolkit is defined
- system_cfg.features.firewall.toolkit in ["iptables", "nftables"]
- system_cfg.features.firewall.enabled is defined
- system_cfg.features.banner.motd is defined
- system_cfg.features.banner.sudo is defined
- system_cfg.luks.enabled is defined
- system_cfg.features.chroot.tool is defined
- system_cfg.features.chroot.tool in ["arch-chroot", "chroot", "systemd-nspawn"]
fail_msg: Invalid feature flags were specified, please check your inventory/vars.
quiet: true
- name: Validate virtual system sizing
when: system_cfg.type == "virtual"
ansible.builtin.assert:
that:
- system_cfg.cpus is defined and (system_cfg.cpus | int) > 0
- system_cfg.memory is defined and (system_cfg.memory | 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 | float / 1024 >= 16.0)
| ternary(
(system_cfg.memory | float / 2048),
[system_cfg.memory | float / 1024, 4.0] | max
)
)
+ 5.5
)
)
fail_msg: "Invalid system sizing. Check system.cpus, system.memory, and system.disks[0].size."
quiet: true
- name: Validate all virtual disks have a positive size
when: system_cfg.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 system.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.path | default('') | string | trim) == ''
fail_msg: "system.disks[0].mount.path 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(attribute='path')
| 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.path | default('') | string) | trim }}"
disk_fstype: "{{ (item.mount.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 system_cfg.type == "virtual" or (disk_device | length) > 0
- disk_mount == "" or system_cfg.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