--- - 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: 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