refactor(global_defaults): split system.yml into composable normalization stages
This commit is contained in:
100
roles/global_defaults/tasks/_normalize_disks.yml
Normal file
100
roles/global_defaults/tasks/_normalize_disks.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
- name: Normalize system disks input
|
||||
vars:
|
||||
system_disks: "{{ system_cfg.disks | default([]) }}"
|
||||
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||
system_disk_device_prefix: >-
|
||||
{{
|
||||
{'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.get(hypervisor_type, '')
|
||||
if system_cfg.type == 'virtual'
|
||||
else ''
|
||||
}}
|
||||
block:
|
||||
- name: Validate system disks structure
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system_disks is sequence
|
||||
- (system_disks | length) <= 26
|
||||
fail_msg: "system.disks must be a list with at most 26 entries."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system disk entries
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item is mapping
|
||||
- item.mount is not defined or item.mount is mapping
|
||||
fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary."
|
||||
quiet: true
|
||||
loop: "{{ system_disks }}"
|
||||
loop_control:
|
||||
label: "{{ item | to_json }}"
|
||||
|
||||
- name: Initialize normalized disk list
|
||||
ansible.builtin.set_fact:
|
||||
system_disks_cfg: []
|
||||
|
||||
- name: Build normalized system disk configuration
|
||||
vars:
|
||||
disk_idx: "{{ ansible_loop.index0 }}"
|
||||
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
|
||||
disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
|
||||
disk_mount: "{{ system_disk_defaults.mount | combine((disk_cfg_base.mount | default({})), recursive=True) }}"
|
||||
disk_mount_path: "{{ (disk_mount.path | default('') | string) | trim }}"
|
||||
disk_mount_fstype: >-
|
||||
{{
|
||||
disk_mount.fstype
|
||||
if (disk_mount.fstype | default('') | string | length) > 0
|
||||
else ('ext4' if disk_mount_path | length > 0 else '')
|
||||
}}
|
||||
disk_device: >-
|
||||
{{
|
||||
disk_cfg_base.device
|
||||
if (disk_cfg_base.device | string | length) > 0
|
||||
else (
|
||||
(system_disk_device_prefix ~ disk_letter)
|
||||
if system_cfg.type == 'virtual'
|
||||
else ''
|
||||
)
|
||||
}}
|
||||
disk_partition: >-
|
||||
{{
|
||||
disk_device ~ ('p1' if (disk_device | regex_search('\\d$')) else '1')
|
||||
if disk_device | length > 0
|
||||
else ''
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
system_disks_cfg: >-
|
||||
{{
|
||||
system_disks_cfg + [
|
||||
disk_cfg_base
|
||||
| combine(
|
||||
{
|
||||
'device': disk_device,
|
||||
'mount': {
|
||||
'path': disk_mount_path,
|
||||
'fstype': disk_mount_fstype,
|
||||
'label': disk_mount.label | default('') | string,
|
||||
'opts': disk_mount.opts | default('defaults') | string
|
||||
},
|
||||
'partition': disk_partition
|
||||
},
|
||||
recursive=True
|
||||
)
|
||||
]
|
||||
}}
|
||||
loop: "{{ system_disks }}"
|
||||
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}, recursive=True) }}"
|
||||
|
||||
- name: Set install_drive from primary disk
|
||||
when:
|
||||
- system_disks_cfg | length > 0
|
||||
- system_disks_cfg[0].device | string | length > 0
|
||||
ansible.builtin.set_fact:
|
||||
install_drive: "{{ system_disks_cfg[0].device }}"
|
||||
143
roles/global_defaults/tasks/_normalize_system.yml
Normal file
143
roles/global_defaults/tasks/_normalize_system.yml
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
- name: Build normalized system configuration
|
||||
vars:
|
||||
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
|
||||
system_type: "{{ system_raw.type | string | lower }}"
|
||||
system_os_input: "{{ system_raw.os | default('') | string | lower }}"
|
||||
system_name: >-
|
||||
{{
|
||||
system_raw.name | string | trim
|
||||
if (system_raw.name | default('') | string | trim | length) > 0
|
||||
else inventory_hostname
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
system_cfg:
|
||||
type: "{{ system_type }}"
|
||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||
version: "{{ system_raw.version | default('') | string }}"
|
||||
filesystem: "{{ system_raw.filesystem | default('') | string | lower }}"
|
||||
name: "{{ system_name }}"
|
||||
id: "{{ system_raw.id | default('') | string }}"
|
||||
cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}"
|
||||
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
||||
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
||||
network:
|
||||
bridge: "{{ system_raw.network.bridge | default('') | string }}"
|
||||
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
||||
ip: "{{ system_raw.network.ip | default('') | string }}"
|
||||
prefix: >-
|
||||
{{
|
||||
(system_raw.network.prefix | int | string)
|
||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||
else ''
|
||||
}}
|
||||
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
||||
dns:
|
||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
||||
interfaces: >-
|
||||
{{
|
||||
system_raw.network.interfaces
|
||||
if (system_raw.network.interfaces | default([]) | length > 0)
|
||||
else (
|
||||
[{
|
||||
'name': '',
|
||||
'bridge': system_raw.network.bridge | default('') | string,
|
||||
'vlan': system_raw.network.vlan | default('') | string,
|
||||
'ip': system_raw.network.ip | default('') | string,
|
||||
'prefix': (
|
||||
(system_raw.network.prefix | int | string)
|
||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||
else ''
|
||||
),
|
||||
'gateway': system_raw.network.gateway | default('') | string
|
||||
}]
|
||||
if (system_raw.network.bridge | default('') | string | length > 0)
|
||||
else []
|
||||
)
|
||||
}}
|
||||
timezone: "{{ system_raw.timezone | default('Europe/Vienna') | string }}"
|
||||
locale: "{{ system_raw.locale | default('en_US.UTF-8') | string }}"
|
||||
keymap: "{{ system_raw.keymap | default('us') | string }}"
|
||||
path: "{{ system_raw.path | default('') | string }}"
|
||||
packages: >-
|
||||
{{
|
||||
(
|
||||
system_raw.packages
|
||||
if system_raw.packages is iterable and system_raw.packages is not string
|
||||
else (system_raw.packages | string).split(',')
|
||||
)
|
||||
| map('trim')
|
||||
| reject('equalto', '')
|
||||
| list
|
||||
}}
|
||||
disks: "{{ system_raw.disks | default([]) }}"
|
||||
users: "{{ system_raw.users | default([]) }}"
|
||||
root:
|
||||
password: "{{ system_raw.root.password | string }}"
|
||||
luks:
|
||||
enabled: "{{ system_raw.luks.enabled | bool }}"
|
||||
passphrase: "{{ system_raw.luks.passphrase | string }}"
|
||||
mapper: "{{ system_raw.luks.mapper | string }}"
|
||||
auto: "{{ system_raw.luks.auto | bool }}"
|
||||
method: "{{ system_raw.luks.method | string | lower }}"
|
||||
tpm2:
|
||||
device: "{{ system_raw.luks.tpm2.device | string }}"
|
||||
pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}"
|
||||
keysize: "{{ system_raw.luks.keysize | int }}"
|
||||
options: "{{ system_raw.luks.options | string }}"
|
||||
type: "{{ system_raw.luks.type | string }}"
|
||||
cipher: "{{ system_raw.luks.cipher | string }}"
|
||||
hash: "{{ system_raw.luks.hash | string }}"
|
||||
iter: "{{ system_raw.luks.iter | int }}"
|
||||
bits: "{{ system_raw.luks.bits | int }}"
|
||||
pbkdf: "{{ system_raw.luks.pbkdf | string }}"
|
||||
urandom: "{{ system_raw.luks.urandom | bool }}"
|
||||
verify: "{{ system_raw.luks.verify | bool }}"
|
||||
features:
|
||||
cis:
|
||||
enabled: "{{ system_raw.features.cis.enabled | bool }}"
|
||||
selinux:
|
||||
enabled: "{{ system_raw.features.selinux.enabled | bool }}"
|
||||
firewall:
|
||||
enabled: "{{ system_raw.features.firewall.enabled | bool }}"
|
||||
backend: "{{ system_raw.features.firewall.backend | string | lower }}"
|
||||
toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}"
|
||||
ssh:
|
||||
enabled: "{{ system_raw.features.ssh.enabled | bool }}"
|
||||
zstd:
|
||||
enabled: "{{ system_raw.features.zstd.enabled | bool }}"
|
||||
swap:
|
||||
enabled: "{{ system_raw.features.swap.enabled | bool }}"
|
||||
banner:
|
||||
motd: "{{ system_raw.features.banner.motd | bool }}"
|
||||
sudo: "{{ system_raw.features.banner.sudo | bool }}"
|
||||
rhel_repo:
|
||||
source: "{{ system_raw.features.rhel_repo.source | default('iso') | string | lower }}"
|
||||
url: "{{ system_raw.features.rhel_repo.url | default('') | string }}"
|
||||
chroot:
|
||||
tool: "{{ system_raw.features.chroot.tool | string }}"
|
||||
hostname: "{{ system_name }}"
|
||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||
os_version: "{{ system_raw.version | default('') | string }}"
|
||||
no_log: true
|
||||
|
||||
- name: Populate primary network fields from first interface
|
||||
when:
|
||||
- system_cfg.network.interfaces | length > 0
|
||||
- system_cfg.network.bridge | default('') | string | length == 0
|
||||
vars:
|
||||
_primary: "{{ system_cfg.network.interfaces[0] }}"
|
||||
ansible.builtin.set_fact:
|
||||
system_cfg: >-
|
||||
{{
|
||||
system_cfg | combine({
|
||||
'network': system_cfg.network | combine({
|
||||
'bridge': _primary.bridge | default(''),
|
||||
'vlan': _primary.vlan | default(''),
|
||||
'ip': _primary.ip | default(''),
|
||||
'prefix': _primary.prefix | default(''),
|
||||
'gateway': _primary.gateway | default('')
|
||||
})
|
||||
}, recursive=True)
|
||||
}}
|
||||
57
roles/global_defaults/tasks/_validate_input.yml
Normal file
57
roles/global_defaults/tasks/_validate_input.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
- name: Ensure system input is a dictionary
|
||||
ansible.builtin.set_fact:
|
||||
system: "{{ system | default({}) }}"
|
||||
|
||||
- name: Validate system input types
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system is mapping
|
||||
- system.network is not defined or system.network is mapping
|
||||
- system.users is not defined or (system.users is iterable and system.users is not string and system.users is not 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
|
||||
fail_msg: "system and its nested keys (network, root, luks, features) must be dictionaries; system.users must be a list."
|
||||
quiet: true
|
||||
|
||||
- name: Validate DNS lists (not strings)
|
||||
when: system.network is defined and system.network.dns is defined
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string)
|
||||
- system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string)
|
||||
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system.users entries
|
||||
when: system.users is defined and system.users | length > 0
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item is mapping
|
||||
- item.name is defined and (item.name | string | length) > 0
|
||||
- item['keys'] is not defined or (item['keys'] is iterable and item['keys'] is not string)
|
||||
fail_msg: "Each system.users[] entry must be a dict with 'name'; 'keys' must be a list."
|
||||
quiet: true
|
||||
loop: "{{ system.users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name | default('(unnamed)') }}"
|
||||
|
||||
- name: Validate system features input types
|
||||
when: system.features is defined
|
||||
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
||||
loop_control:
|
||||
label: "system.features.{{ item }}"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (system.features[item] | default({})) is mapping
|
||||
fail_msg: "system.features.{{ item }} must be a dictionary."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system LUKS TPM2 input type
|
||||
when: system.luks is defined and system.luks is mapping
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
|
||||
fail_msg: "system.luks.tpm2 must be a dictionary."
|
||||
quiet: true
|
||||
@@ -1,300 +1,9 @@
|
||||
---
|
||||
- name: Ensure system input is a dictionary
|
||||
ansible.builtin.set_fact:
|
||||
system: "{{ system | default({}) }}"
|
||||
- name: Validate raw system input types
|
||||
ansible.builtin.include_tasks: _validate_input.yml
|
||||
|
||||
- name: Validate system input types
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system is mapping
|
||||
- system.network is not defined or system.network is mapping
|
||||
- system.users is not defined or (system.users is iterable and system.users is not string and system.users is not 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
|
||||
fail_msg: "system and its nested keys (network, root, luks, features) must be dictionaries; system.users must be a list."
|
||||
quiet: true
|
||||
- name: Normalize system configuration
|
||||
ansible.builtin.include_tasks: _normalize_system.yml
|
||||
|
||||
- name: Validate DNS lists (not strings)
|
||||
when: system.network is defined and system.network.dns is defined
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system.network.dns.servers is not defined or (system.network.dns.servers is iterable and system.network.dns.servers is not string)
|
||||
- system.network.dns.search is not defined or (system.network.dns.search is iterable and system.network.dns.search is not string)
|
||||
fail_msg: "system.network.dns.servers and system.network.dns.search must be lists, not strings."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system.users entries
|
||||
when: system.users is defined and system.users | length > 0
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item is mapping
|
||||
- item.name is defined and (item.name | string | length) > 0
|
||||
- item['keys'] is not defined or (item['keys'] is iterable and item['keys'] is not string)
|
||||
fail_msg: "Each system.users[] entry must be a dict with 'name'; 'keys' must be a list."
|
||||
quiet: true
|
||||
loop: "{{ system.users }}"
|
||||
loop_control:
|
||||
label: "{{ item.name | default('(unnamed)') }}"
|
||||
|
||||
- name: Validate system features input types
|
||||
when: system.features is defined
|
||||
loop: "{{ system_defaults.features | dict2items | map(attribute='key') | list }}"
|
||||
loop_control:
|
||||
label: "system.features.{{ item }}"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (system.features[item] | default({})) is mapping
|
||||
fail_msg: "system.features.{{ item }} must be a dictionary."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system LUKS TPM2 input type
|
||||
when: system.luks is defined and system.luks is mapping
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system.luks.tpm2 is not defined or system.luks.tpm2 is mapping
|
||||
fail_msg: "system.luks.tpm2 must be a dictionary."
|
||||
quiet: true
|
||||
|
||||
- name: Build normalized system configuration
|
||||
vars:
|
||||
system_raw: "{{ system_defaults | combine(system, recursive=True) }}"
|
||||
system_type: "{{ system_raw.type | string | lower }}"
|
||||
system_os_input: "{{ system_raw.os | default('') | string | lower }}"
|
||||
system_name: >-
|
||||
{{
|
||||
system_raw.name | string | trim
|
||||
if (system_raw.name | default('') | string | trim | length) > 0
|
||||
else inventory_hostname
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
system_cfg:
|
||||
type: "{{ system_type }}"
|
||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||
version: "{{ system_raw.version | default('') | string }}"
|
||||
filesystem: "{{ system_raw.filesystem | default('') | string | lower }}"
|
||||
name: "{{ system_name }}"
|
||||
id: "{{ system_raw.id | default('') | string }}"
|
||||
cpus: "{{ [system_raw.cpus | default(0) | int, 0] | max }}"
|
||||
memory: "{{ [system_raw.memory | default(0) | int, 0] | max }}"
|
||||
balloon: "{{ [system_raw.balloon | default(0) | int, 0] | max }}"
|
||||
network:
|
||||
bridge: "{{ system_raw.network.bridge | default('') | string }}"
|
||||
vlan: "{{ system_raw.network.vlan | default('') | string }}"
|
||||
ip: "{{ system_raw.network.ip | default('') | string }}"
|
||||
prefix: >-
|
||||
{{
|
||||
(system_raw.network.prefix | int | string)
|
||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||
else ''
|
||||
}}
|
||||
gateway: "{{ system_raw.network.gateway | default('') | string }}"
|
||||
dns:
|
||||
servers: "{{ system_raw.network.dns.servers | default([]) }}"
|
||||
search: "{{ system_raw.network.dns.search | default([]) }}"
|
||||
interfaces: >-
|
||||
{{
|
||||
system_raw.network.interfaces
|
||||
if (system_raw.network.interfaces | default([]) | length > 0)
|
||||
else (
|
||||
[{
|
||||
'name': '',
|
||||
'bridge': system_raw.network.bridge | default('') | string,
|
||||
'vlan': system_raw.network.vlan | default('') | string,
|
||||
'ip': system_raw.network.ip | default('') | string,
|
||||
'prefix': (
|
||||
(system_raw.network.prefix | int | string)
|
||||
if (system_raw.network.prefix | default('') | string | length) > 0
|
||||
else ''
|
||||
),
|
||||
'gateway': system_raw.network.gateway | default('') | string
|
||||
}]
|
||||
if (system_raw.network.bridge | default('') | string | length > 0)
|
||||
else []
|
||||
)
|
||||
}}
|
||||
timezone: "{{ system_raw.timezone | default('Europe/Vienna') | string }}"
|
||||
locale: "{{ system_raw.locale | default('en_US.UTF-8') | string }}"
|
||||
keymap: "{{ system_raw.keymap | default('us') | string }}"
|
||||
path: "{{ system_raw.path | default('') | string }}"
|
||||
packages: >-
|
||||
{{
|
||||
(
|
||||
system_raw.packages
|
||||
if system_raw.packages is iterable and system_raw.packages is not string
|
||||
else (system_raw.packages | string).split(',')
|
||||
)
|
||||
| map('trim')
|
||||
| reject('equalto', '')
|
||||
| list
|
||||
}}
|
||||
disks: "{{ system_raw.disks | default([]) }}"
|
||||
users: "{{ system_raw.users | default([]) }}"
|
||||
root:
|
||||
password: "{{ system_raw.root.password | string }}"
|
||||
luks:
|
||||
enabled: "{{ system_raw.luks.enabled | bool }}"
|
||||
passphrase: "{{ system_raw.luks.passphrase | string }}"
|
||||
mapper: "{{ system_raw.luks.mapper | string }}"
|
||||
auto: "{{ system_raw.luks.auto | bool }}"
|
||||
method: "{{ system_raw.luks.method | string | lower }}"
|
||||
tpm2:
|
||||
device: "{{ system_raw.luks.tpm2.device | string }}"
|
||||
pcrs: "{{ system_raw.luks.tpm2.pcrs | string }}"
|
||||
keysize: "{{ system_raw.luks.keysize | int }}"
|
||||
options: "{{ system_raw.luks.options | string }}"
|
||||
type: "{{ system_raw.luks.type | string }}"
|
||||
cipher: "{{ system_raw.luks.cipher | string }}"
|
||||
hash: "{{ system_raw.luks.hash | string }}"
|
||||
iter: "{{ system_raw.luks.iter | int }}"
|
||||
bits: "{{ system_raw.luks.bits | int }}"
|
||||
pbkdf: "{{ system_raw.luks.pbkdf | string }}"
|
||||
urandom: "{{ system_raw.luks.urandom | bool }}"
|
||||
verify: "{{ system_raw.luks.verify | bool }}"
|
||||
features:
|
||||
cis:
|
||||
enabled: "{{ system_raw.features.cis.enabled | bool }}"
|
||||
selinux:
|
||||
enabled: "{{ system_raw.features.selinux.enabled | bool }}"
|
||||
firewall:
|
||||
enabled: "{{ system_raw.features.firewall.enabled | bool }}"
|
||||
backend: "{{ system_raw.features.firewall.backend | string | lower }}"
|
||||
toolkit: "{{ system_raw.features.firewall.toolkit | string | lower }}"
|
||||
ssh:
|
||||
enabled: "{{ system_raw.features.ssh.enabled | bool }}"
|
||||
zstd:
|
||||
enabled: "{{ system_raw.features.zstd.enabled | bool }}"
|
||||
swap:
|
||||
enabled: "{{ system_raw.features.swap.enabled | bool }}"
|
||||
banner:
|
||||
motd: "{{ system_raw.features.banner.motd | bool }}"
|
||||
sudo: "{{ system_raw.features.banner.sudo | bool }}"
|
||||
rhel_repo:
|
||||
source: "{{ system_raw.features.rhel_repo.source | default('iso') | string | lower }}"
|
||||
url: "{{ system_raw.features.rhel_repo.url | default('') | string }}"
|
||||
chroot:
|
||||
tool: "{{ system_raw.features.chroot.tool | string }}"
|
||||
hostname: "{{ system_name }}"
|
||||
os: "{{ system_os_input if system_os_input | length > 0 else ('archlinux' if system_type == 'physical' else '') }}"
|
||||
os_version: "{{ system_raw.version | default('') | string }}"
|
||||
no_log: true
|
||||
|
||||
- name: Populate primary network fields from first interface
|
||||
when:
|
||||
- system_cfg.network.interfaces | length > 0
|
||||
- system_cfg.network.bridge | default('') | string | length == 0
|
||||
vars:
|
||||
_primary: "{{ system_cfg.network.interfaces[0] }}"
|
||||
ansible.builtin.set_fact:
|
||||
system_cfg: >-
|
||||
{{
|
||||
system_cfg | combine({
|
||||
'network': system_cfg.network | combine({
|
||||
'bridge': _primary.bridge | default(''),
|
||||
'vlan': _primary.vlan | default(''),
|
||||
'ip': _primary.ip | default(''),
|
||||
'prefix': _primary.prefix | default(''),
|
||||
'gateway': _primary.gateway | default('')
|
||||
})
|
||||
}, recursive=True)
|
||||
}}
|
||||
|
||||
- name: Normalize system disks input
|
||||
vars:
|
||||
system_disks: "{{ system_cfg.disks | default([]) }}"
|
||||
system_disk_letter_map: "abcdefghijklmnopqrstuvwxyz"
|
||||
system_disk_device_prefix: >-
|
||||
{{
|
||||
{'libvirt': '/dev/vd', 'xen': '/dev/xvd', 'proxmox': '/dev/sd', 'vmware': '/dev/sd'}.get(hypervisor_type, '')
|
||||
if system_cfg.type == 'virtual'
|
||||
else ''
|
||||
}}
|
||||
block:
|
||||
- name: Validate system disks structure
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- system_disks is sequence
|
||||
- (system_disks | length) <= 26
|
||||
fail_msg: "system.disks must be a list with at most 26 entries."
|
||||
quiet: true
|
||||
|
||||
- name: Validate system disk entries
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- item is mapping
|
||||
- item.mount is not defined or item.mount is mapping
|
||||
fail_msg: "Each disk entry must be a dictionary, and disk.mount (if set) must be a dictionary."
|
||||
quiet: true
|
||||
loop: "{{ system_disks }}"
|
||||
loop_control:
|
||||
label: "{{ item | to_json }}"
|
||||
|
||||
- name: Initialize normalized disk list
|
||||
ansible.builtin.set_fact:
|
||||
system_disks_cfg: []
|
||||
|
||||
- name: Build normalized system disk configuration
|
||||
vars:
|
||||
disk_idx: "{{ ansible_loop.index0 }}"
|
||||
disk_letter: "{{ system_disk_letter_map[disk_idx] }}"
|
||||
disk_cfg_base: "{{ system_disk_defaults | combine(item, recursive=True) }}"
|
||||
disk_mount: "{{ system_disk_defaults.mount | combine((disk_cfg_base.mount | default({})), recursive=True) }}"
|
||||
disk_mount_path: "{{ (disk_mount.path | default('') | string) | trim }}"
|
||||
disk_mount_fstype: >-
|
||||
{{
|
||||
disk_mount.fstype
|
||||
if (disk_mount.fstype | default('') | string | length) > 0
|
||||
else ('ext4' if disk_mount_path | length > 0 else '')
|
||||
}}
|
||||
disk_device: >-
|
||||
{{
|
||||
disk_cfg_base.device
|
||||
if (disk_cfg_base.device | string | length) > 0
|
||||
else (
|
||||
(system_disk_device_prefix ~ disk_letter)
|
||||
if system_cfg.type == 'virtual'
|
||||
else ''
|
||||
)
|
||||
}}
|
||||
disk_partition: >-
|
||||
{{
|
||||
disk_device ~ ('p1' if (disk_device | regex_search('\\d$')) else '1')
|
||||
if disk_device | length > 0
|
||||
else ''
|
||||
}}
|
||||
ansible.builtin.set_fact:
|
||||
system_disks_cfg: >-
|
||||
{{
|
||||
system_disks_cfg + [
|
||||
disk_cfg_base
|
||||
| combine(
|
||||
{
|
||||
'device': disk_device,
|
||||
'mount': {
|
||||
'path': disk_mount_path,
|
||||
'fstype': disk_mount_fstype,
|
||||
'label': disk_mount.label | default('') | string,
|
||||
'opts': disk_mount.opts | default('defaults') | string
|
||||
},
|
||||
'partition': disk_partition
|
||||
},
|
||||
recursive=True
|
||||
)
|
||||
]
|
||||
}}
|
||||
loop: "{{ system_disks }}"
|
||||
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}, recursive=True) }}"
|
||||
|
||||
- name: Set install_drive from primary disk
|
||||
when:
|
||||
- system_disks_cfg | length > 0
|
||||
- system_disks_cfg[0].device | string | length > 0
|
||||
ansible.builtin.set_fact:
|
||||
install_drive: "{{ system_disks_cfg[0].device }}"
|
||||
- name: Normalize disk configuration
|
||||
ansible.builtin.include_tasks: _normalize_disks.yml
|
||||
|
||||
Reference in New Issue
Block a user