feat: uniform system.content source schema across installers and repositories

This commit is contained in:
2026-05-27 05:15:32 +02:00
parent 939c5c741f
commit d922efd2e4
25 changed files with 419 additions and 50 deletions

View File

@@ -172,7 +172,7 @@ Top-level variables outside `system`/`hypervisor`.
| `memory` | int | `0` | Memory in MiB (required for virtual) | | `memory` | int | `0` | Memory in MiB (required for virtual) |
| `balloon` | int | `0` | Balloon memory in MiB (Proxmox) | | `balloon` | int | `0` | Balloon memory in MiB (Proxmox) |
| `path` | string | -- | Hypervisor folder/path (falls back to `hypervisor.folder`) | | `path` | string | -- | Hypervisor folder/path (falls back to `hypervisor.folder`) |
| `mirror` | string | per-distro default | Override package mirror (Debian/Ubuntu) | | `content` | dict | see below | Package content source (mirror/DVD/Satellite, family-resolved) |
| `packages` | list | `[]` | Additional packages installed post-reboot | | `packages` | list | `[]` | Additional packages installed post-reboot |
| `network` | dict | see below | Network configuration | | `network` | dict | see below | Network configuration |
| `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#46-multi-disk-schema)) | | `disks` | list | `[]` | Disk layout (see [Multi-Disk Schema](#46-multi-disk-schema)) |
@@ -181,6 +181,25 @@ Top-level variables outside `system`/`hypervisor`.
| `luks` | dict | see below | Encryption settings | | `luks` | dict | see below | Encryption settings |
| `features` | dict | see below | Feature toggles | | `features` | dict | see below | Feature toggles |
#### `system.content`
Uniform package content source, family-resolved. `source: ''` defaults to `dvd` on EL and `mirror` on Debian/Ubuntu/Arch. Satellite values come from inventory/vault only, never committed code.
| Key | Type | Default | Description |
| -------------------------- | ------ | -------------- | ----------------------------------------------------------------- |
| `source` | string | family default | `dvd`, `mirror`, `satellite`, or `none` |
| `url` | string | family default | Mirror URL / EL `.repo` baseurl |
| `proxy` | string | -- | `http://host:port` content proxy (dnf/apt/pacman) |
| `gpgcheck` | bool | `true` | Repository GPG checking |
| `satellite.host` | string | -- | EL Katello/Satellite hostname |
| `satellite.ip` | string | -- | Optional `/etc/hosts` entry when DNS does not resolve the host |
| `satellite.org` | string | -- | Organization label |
| `satellite.activation_key` | string | -- | Activation key |
| `satellite.ca_url` | string | derived | Katello CA RPM URL (default `https://<host>/pub/katello-ca-consumer-latest.noarch.rpm`) |
| `satellite.service_level` | string | -- | syspurpose service level |
| `satellite.environment` | string | -- | Lifecycle environment |
| `satellite.install` | bool | `false` | `false`: base from DVD/mirror then register; `true`: install from Satellite |
#### `system.network` #### `system.network`
| Key | Type | Default | Description | | Key | Type | Default | Description |
@@ -281,8 +300,6 @@ The bootstrap auto-switches to dracut when `method: tpm2` is set. Override via `
| `banner.sudo` | bool | `true` | Sudo banner | | `banner.sudo` | bool | `true` | Sudo banner |
| `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, or `systemd-nspawn` | | `chroot.tool` | string | `arch-chroot` | `arch-chroot`, `chroot`, or `systemd-nspawn` |
| `initramfs.generator` | string | auto-detected | Override initramfs generator (see below) | | `initramfs.generator` | string | auto-detected | Override initramfs generator (see below) |
| `rhel_repo.source` | string | `iso` | RHEL post-install repo source: `iso`, `satellite`, or `none` |
| `rhel_repo.url` | string | -- | Satellite/custom repo URL when `source: satellite` |
| `secure_boot.enabled` | bool | `false` | Enable Secure Boot (Arch via sbctl, others via shim) | | `secure_boot.enabled` | bool | `false` | Enable Secure Boot (Arch via sbctl, others via shim) |
| `secure_boot.method` | string | -- | Arch only: `sbctl` (default) or `uki` | | `secure_boot.method` | string | -- | Arch only: `sbctl` (default) or `uki` |
| `desktop.*` | dict | see below | Desktop environment settings (see [4.2.5](#425-systemfeaturesdesktop)) | | `desktop.*` | dict | see below | Desktop environment settings (see [4.2.5](#425-systemfeaturesdesktop)) |

View File

@@ -1,9 +1,4 @@
--- ---
- name: Refresh Arch keyring in the live environment
ansible.builtin.command: pacman -Sy --noconfirm archlinux-keyring
register: bootstrap_arch_keyring
changed_when: bootstrap_arch_keyring.rc == 0
- name: Bootstrap ArchLinux - name: Bootstrap ArchLinux
vars: vars:
_config: "{{ lookup('vars', bootstrap_var_key) }}" _config: "{{ lookup('vars', bootstrap_var_key) }}"
@@ -13,7 +8,44 @@
| reject('equalto', '') | reject('equalto', '')
| list | list
}} }}
block:
- name: Notify that mirror mode falls back to the public mirrorlist
when:
- system_cfg.content.source == 'mirror'
- system_cfg.content.url | length == 0
ansible.builtin.debug:
msg: >-
content.source is 'mirror' but content.url is empty: keeping the live
ISO public mirrorlist (refreshed by reflector). Set content.url to pin
a specific mirror.
- name: Point pacman at the content mirror
when: system_cfg.content.url | length > 0
ansible.builtin.copy:
dest: /etc/pacman.d/mirrorlist
content: "Server = {{ system_cfg.content.url }}/$repo/os/$arch\n"
mode: "0644"
- name: Refresh Arch keyring in the live environment
ansible.builtin.command: pacman -Sy --noconfirm archlinux-keyring
environment:
http_proxy: "{{ system_cfg.content.proxy }}"
https_proxy: "{{ system_cfg.content.proxy }}"
register: bootstrap_arch_keyring
changed_when: bootstrap_arch_keyring.rc == 0
- name: Install Arch base system
ansible.builtin.command: >- ansible.builtin.command: >-
pacstrap /mnt {{ bootstrap_archlinux_packages | join(' ') }} pacstrap /mnt {{ bootstrap_archlinux_packages | join(' ') }}
environment:
http_proxy: "{{ system_cfg.content.proxy }}"
https_proxy: "{{ system_cfg.content.proxy }}"
register: bootstrap_result register: bootstrap_result
changed_when: bootstrap_result.rc == 0 changed_when: bootstrap_result.rc == 0
- name: Persist the content mirror in the installed system
when: system_cfg.content.url | length > 0
ansible.builtin.copy:
dest: /mnt/etc/pacman.d/mirrorlist
content: "Server = {{ system_cfg.content.url }}/$repo/os/$arch\n"
mode: "0644"

View File

@@ -43,7 +43,10 @@
debootstrap --keyring=/usr/share/keyrings/debian-archive-keyring.gpg debootstrap --keyring=/usr/share/keyrings/debian-archive-keyring.gpg
--include={{ bootstrap_debian_base_csv }} --include={{ bootstrap_debian_base_csv }}
{{ bootstrap_debian_release }} /mnt {{ bootstrap_debian_release }} /mnt
{{ system_cfg.mirror | default('http://deb.debian.org/debian', true) }} {{ system_cfg.content.url }}
environment:
http_proxy: "{{ system_cfg.content.proxy }}"
https_proxy: "{{ system_cfg.content.proxy }}"
register: bootstrap_debian_base_result register: bootstrap_debian_base_result
changed_when: bootstrap_debian_base_result.rc == 0 changed_when: bootstrap_debian_base_result.rc == 0
@@ -60,6 +63,10 @@
Acquire::Retries "3"; Acquire::Retries "3";
Acquire::http::Pipeline-Depth "10"; Acquire::http::Pipeline-Depth "10";
APT::Install-Recommends "false"; APT::Install-Recommends "false";
{% if system_cfg.content.proxy | length > 0 %}
Acquire::http::Proxy "{{ system_cfg.content.proxy }}";
Acquire::https::Proxy "{{ system_cfg.content.proxy }}";
{% endif %}
mode: "0644" mode: "0644"
- name: Update package lists - name: Update package lists

View File

@@ -24,12 +24,14 @@
- "'grub2-common' not in (bootstrap_result.stderr | default(''))" - "'grub2-common' not in (bootstrap_result.stderr | default(''))"
- name: Ensure chroot RHEL DVD directory exists - name: Ensure chroot RHEL DVD directory exists
when: system_cfg.content.source != 'mirror'
ansible.builtin.file: ansible.builtin.file:
path: /mnt/usr/local/install/redhat/dvd path: /mnt/usr/local/install/redhat/dvd
state: directory state: directory
mode: "0755" mode: "0755"
- name: Bind mount RHEL DVD into chroot - name: Bind mount RHEL DVD into chroot
when: system_cfg.content.source != 'mirror'
ansible.posix.mount: ansible.posix.mount:
src: /usr/local/install/redhat/dvd src: /usr/local/install/redhat/dvd
path: /mnt/usr/local/install/redhat/dvd path: /mnt/usr/local/install/redhat/dvd

View File

@@ -42,7 +42,10 @@
--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg --keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg
--include={{ bootstrap_ubuntu_base_csv }} --include={{ bootstrap_ubuntu_base_csv }}
{{ bootstrap_ubuntu_release }} /mnt {{ bootstrap_ubuntu_release }} /mnt
{{ system_cfg.mirror | default('http://archive.ubuntu.com/ubuntu', true) }} {{ system_cfg.content.url }}
environment:
http_proxy: "{{ system_cfg.content.proxy }}"
https_proxy: "{{ system_cfg.content.proxy }}"
register: bootstrap_ubuntu_base_result register: bootstrap_ubuntu_base_result
changed_when: bootstrap_ubuntu_base_result.rc == 0 changed_when: bootstrap_ubuntu_base_result.rc == 0
@@ -59,6 +62,10 @@
Acquire::Retries "3"; Acquire::Retries "3";
Acquire::http::Pipeline-Depth "10"; Acquire::http::Pipeline-Depth "10";
APT::Install-Recommends "false"; APT::Install-Recommends "false";
{% if system_cfg.content.proxy | length > 0 %}
Acquire::http::Proxy "{{ system_cfg.content.proxy }}";
Acquire::https::Proxy "{{ system_cfg.content.proxy }}";
{% endif %}
mode: "0644" mode: "0644"
- name: Update package lists - name: Update package lists

View File

@@ -1,6 +1,6 @@
# Managed by Ansible. # Managed by Ansible.
{% set release = bootstrap_debian_release %} {% set release = bootstrap_debian_release %}
{% set mirror = system_cfg.mirror | default('http://deb.debian.org/debian', true) %} {% set mirror = system_cfg.content.url | default('http://deb.debian.org/debian', true) %}
{% set components = 'main contrib non-free non-free-firmware' %} {% set components = 'main contrib non-free non-free-firmware' %}
deb {{ mirror }} {{ release }} {{ components }} deb {{ mirror }} {{ release }} {{ components }}

View File

@@ -1,6 +1,6 @@
# Managed by Ansible. # Managed by Ansible.
{% set release = bootstrap_ubuntu_release %} {% set release = bootstrap_ubuntu_release %}
{% set mirror = system_cfg.mirror %} {% set mirror = system_cfg.content.url %}
{% set components = 'main restricted universe multiverse' %} {% set components = 'main restricted universe multiverse' %}
deb {{ mirror }} {{ release }} {{ components }} deb {{ mirror }} {{ release }} {{ components }}

View File

@@ -314,7 +314,6 @@ bootstrap_archlinux:
- nfs-utils - nfs-utils
- ppp - ppp
- python - python
- reflector
- rsync - rsync
- sudo - sudo
- tldr - tldr
@@ -327,5 +326,6 @@ bootstrap_archlinux:
(['openssh'] if system_cfg.features.ssh.enabled | bool else []) (['openssh'] if system_cfg.features.ssh.enabled | bool else [])
+ (['iptables-nft'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else []) + (['iptables-nft'] if system_cfg.features.firewall.toolkit == 'nftables' and system_cfg.features.firewall.enabled | bool else [])
+ (['sbctl'] if system_cfg.features.secure_boot.enabled | bool else []) + (['sbctl'] if system_cfg.features.secure_boot.enabled | bool else [])
+ (['reflector'] if system_cfg.content.url | length == 0 else [])
+ (bootstrap_common_conditional | reject('equalto', 'nftables') | list) + (bootstrap_common_conditional | reject('equalto', 'nftables') | list)
}} }}

View File

@@ -16,7 +16,7 @@
loop: >- loop: >-
{{ {{
['ide0', 'ide2'] ['ide0', 'ide2']
+ (['ide1'] if not (os == 'rhel' and system_cfg.features.rhel_repo.source == 'iso') else []) + (['ide1'] if not (os == 'rhel' and system_cfg.content.source == 'dvd') else [])
}} }}
failed_when: false failed_when: false
no_log: true no_log: true

View File

@@ -35,7 +35,7 @@
} }
] ]
if (rhel_iso is defined and rhel_iso | length > 0 if (rhel_iso is defined and rhel_iso | length > 0
and not (os == 'rhel' and system_cfg.features.rhel_repo.source == 'iso')) and not (os == 'rhel' and system_cfg.content.source == 'dvd'))
else [] else []
) )
}} }}

View File

@@ -26,7 +26,7 @@
- name: Remove RHEL ISO fstab entry when not using local repo - name: Remove RHEL ISO fstab entry when not using local repo
when: when:
- os == "rhel" - os == "rhel"
- system_cfg.features.rhel_repo.source != "iso" - system_cfg.content.source != "dvd"
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /mnt/etc/fstab path: /mnt/etc/fstab
regexp: "^.*\\/dvd.*$" regexp: "^.*\\/dvd.*$"
@@ -35,7 +35,7 @@
- name: Replace ISO UUID entry with /dev/sr0 in fstab - name: Replace ISO UUID entry with /dev/sr0 in fstab
when: when:
- os == "rhel" - os == "rhel"
- system_cfg.features.rhel_repo.source == "iso" - system_cfg.content.source == "dvd"
vars: vars:
configuration_fstab_dvd_line: >- configuration_fstab_dvd_line: >-
{{ {{
@@ -53,7 +53,7 @@
when: when:
- os == "rhel" - os == "rhel"
- hypervisor_type == "vmware" - hypervisor_type == "vmware"
- system_cfg.features.rhel_repo.source == "iso" - system_cfg.content.source == "dvd"
ansible.builtin.command: ansible.builtin.command:
argv: argv:
- dd - dd

View File

@@ -7,7 +7,6 @@
ansible.builtin.include_tasks: "{{ configuration_task.file }}" ansible.builtin.include_tasks: "{{ configuration_task.file }}"
loop: loop:
- file: repositories.yml - file: repositories.yml
when: "{{ os_family == 'Debian' }}"
- file: banner.yml - file: banner.yml
- file: fstab.yml - file: fstab.yml
- file: locales.yml - file: locales.yml

View File

@@ -1,5 +1,8 @@
--- ---
- name: Write final sources.list # Config runs against the chroot, so these write /mnt directly via templates
# rather than apt_repository/yum_repository, which would touch the live host.
- name: Write the apt sources.list
when: os_family == 'Debian'
vars: vars:
_debian_release_map: _debian_release_map:
"12": bookworm "12": bookworm
@@ -13,11 +16,69 @@
dest: /mnt/etc/apt/sources.list dest: /mnt/etc/apt/sources.list
mode: "0644" mode: "0644"
- name: Ensure apt performance configuration persists - name: Ensure apt performance and content-proxy configuration
when: os_family == 'Debian'
ansible.builtin.copy: ansible.builtin.copy:
dest: /mnt/etc/apt/apt.conf.d/99performance dest: /mnt/etc/apt/apt.conf.d/99performance
content: | content: |
Acquire::Retries "3"; Acquire::Retries "3";
Acquire::http::Pipeline-Depth "10"; Acquire::http::Pipeline-Depth "10";
APT::Install-Recommends "false"; APT::Install-Recommends "false";
{% if system_cfg.content.proxy | length > 0 %}
Acquire::http::Proxy "{{ system_cfg.content.proxy }}";
Acquire::https::Proxy "{{ system_cfg.content.proxy }}";
{% endif %}
mode: "0644" mode: "0644"
- name: Drop the install-time DVD repo from the target on non-dvd sources
when:
- os_family == 'RedHat'
- system_cfg.content.source != 'dvd'
ansible.builtin.file:
path: /mnt/etc/yum.repos.d/redhat.repo
state: absent
- name: Write the EL mirror repo on the target
when:
- os_family == 'RedHat'
- system_cfg.content.source == 'mirror'
- system_cfg.content.url | length > 0
ansible.builtin.template:
src: el_mirror.repo.j2
dest: "/mnt/etc/yum.repos.d/{{ os }}.repo"
mode: "0644"
- name: Find the stock vendor repos shipped by the release package
when:
- os_family == 'RedHat'
- system_cfg.content.source == 'mirror'
- system_cfg.content.url | length > 0
ansible.builtin.find:
paths: /mnt/etc/yum.repos.d
patterns: "*.repo"
excludes: "{{ os }}.repo"
register: el_stock_repos
- name: Remove the stock vendor repos so only the custom mirror is reachable
when:
- os_family == 'RedHat'
- system_cfg.content.source == 'mirror'
- system_cfg.content.url | length > 0
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ el_stock_repos.files | default([]) }}"
loop_control:
label: "{{ item.path }}"
- name: Configure the dnf content proxy on the target
when:
- os_family == 'RedHat'
- system_cfg.content.proxy | length > 0
ansible.builtin.lineinfile:
path: /mnt/etc/dnf/dnf.conf
line: "proxy={{ system_cfg.content.proxy }}"
regexp: "^proxy="
create: true
mode: "0644"
state: present

View File

@@ -0,0 +1,46 @@
---
# Invoked post-reboot on the booted host, not in the chroot: subscription-manager
# needs a running systemd and the live network.
- name: Add the Satellite host to /etc/hosts
when: system_cfg.content.satellite.ip | length > 0
ansible.builtin.lineinfile:
path: /etc/hosts
line: "{{ system_cfg.content.satellite.ip }} {{ system_cfg.content.satellite.host }}"
regexp: "[[:space:]]{{ system_cfg.content.satellite.host | regex_escape }}([[:space:]]|$)"
state: present
- name: Fetch the Katello CA consumer RPM
ansible.builtin.get_url:
url: >-
{{ system_cfg.content.satellite.ca_url
if (system_cfg.content.satellite.ca_url | length > 0)
else 'https://' ~ system_cfg.content.satellite.host ~ '/pub/katello-ca-consumer-latest.noarch.rpm' }}
dest: /tmp/katello-ca-consumer-latest.noarch.rpm
validate_certs: false
mode: "0644"
- name: Install the Katello CA consumer RPM
ansible.builtin.dnf:
name: /tmp/katello-ca-consumer-latest.noarch.rpm
state: present
disable_gpg_check: true
- name: Clean any stale subscription identity
ansible.builtin.command: subscription-manager clean
changed_when: true
- name: Register with Satellite via activation key
no_log: true
community.general.redhat_subscription:
state: present
server_hostname: "{{ system_cfg.content.satellite.host }}"
org_id: "{{ system_cfg.content.satellite.org }}"
activationkey: "{{ system_cfg.content.satellite.activation_key }}"
environment: "{{ system_cfg.content.satellite.environment | default(omit, true) }}"
auto_attach: true
force_register: true
server_proxy_hostname: "{{ (system_cfg.content.proxy | urlsplit('hostname')) | default(omit, true) }}"
server_proxy_port: "{{ (system_cfg.content.proxy | urlsplit('port')) | default(omit, true) }}"
syspurpose:
service_level_agreement: "{{ system_cfg.content.satellite.service_level | default(omit, true) }}"
sync: true

View File

@@ -1,6 +1,6 @@
# Managed by Ansible. # Managed by Ansible.
{% set release = _debian_release_map[os_version | string] | default('trixie') %} {% set release = _debian_release_map[os_version | string] | default('trixie') %}
{% set mirror = system_cfg.mirror | default('http://deb.debian.org/debian', true) %} {% set mirror = system_cfg.content.url | default('http://deb.debian.org/debian', true) %}
{% set components = 'main contrib non-free non-free-firmware' %} {% set components = 'main contrib non-free non-free-firmware' %}
deb {{ mirror }} {{ release }} {{ components }} deb {{ mirror }} {{ release }} {{ components }}

View File

@@ -0,0 +1,17 @@
[{{ os }}{{ os_version_major }}-baseos]
name={{ os }} {{ os_version_major }} BaseOS
baseurl={{ system_cfg.content.url }}/BaseOS
enabled=1
gpgcheck={{ 1 if system_cfg.content.gpgcheck | bool else 0 }}
{% if system_cfg.content.proxy | length > 0 %}
proxy={{ system_cfg.content.proxy }}
{% endif %}
[{{ os }}{{ os_version_major }}-appstream]
name={{ os }} {{ os_version_major }} AppStream
baseurl={{ system_cfg.content.url }}/AppStream
enabled=1
gpgcheck={{ 1 if system_cfg.content.gpgcheck | bool else 0 }}
{% if system_cfg.content.proxy | length > 0 %}
proxy={{ system_cfg.content.proxy }}
{% endif %}

View File

@@ -1,6 +1,6 @@
# Managed by Ansible. # Managed by Ansible.
{% set release = _ubuntu_release_map[os] | default('resolute') %} {% set release = _ubuntu_release_map[os] | default('resolute') %}
{% set mirror = system_cfg.mirror %} {% set mirror = system_cfg.content.url %}
{% set components = 'main restricted universe multiverse' %} {% set components = 'main restricted universe multiverse' %}
deb {{ mirror }} {{ release }} {{ components }} deb {{ mirror }} {{ release }} {{ components }}

View File

@@ -1,13 +1,24 @@
{% set _baseurl = system_cfg.content.url if system_cfg.content.source == 'mirror' else 'file:///usr/local/install/redhat/dvd' %}
[rhel{{ os_version_major }}-baseos] [rhel{{ os_version_major }}-baseos]
name=RHEL {{ os_version_major }} BaseOS name=RHEL {{ os_version_major }} BaseOS
baseurl=file:///usr/local/install/redhat/dvd/BaseOS baseurl={{ _baseurl }}/BaseOS
enabled=1 enabled=1
gpgcheck=0 gpgcheck=0
{% if system_cfg.content.source != 'mirror' %}
gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release
{% endif %}
{% if system_cfg.content.proxy | length > 0 %}
proxy={{ system_cfg.content.proxy }}
{% endif %}
[rhel{{ os_version_major }}-appstream] [rhel{{ os_version_major }}-appstream]
name=RHEL {{ os_version_major }} AppStream name=RHEL {{ os_version_major }} AppStream
baseurl=file:///usr/local/install/redhat/dvd/AppStream baseurl={{ _baseurl }}/AppStream
enabled=1 enabled=1
gpgcheck=0 gpgcheck=0
{% if system_cfg.content.source != 'mirror' %}
gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release gpgkey=file:///usr/local/install/redhat/dvd/RPM-GPG-KEY-redhat-release
{% endif %}
{% if system_cfg.content.proxy | length > 0 %}
proxy={{ system_cfg.content.proxy }}
{% endif %}

View File

@@ -78,7 +78,23 @@ system_defaults:
timezone: "Europe/Vienna" timezone: "Europe/Vienna"
locale: "en_US.UTF-8" locale: "en_US.UTF-8"
keymap: "us" keymap: "us"
mirror: "" # Uniform content source, family-resolved. source: dvd|mirror|satellite|none
# ('' -> family default: EL=dvd, debian/ubuntu/arch=mirror). satellite values
# come from inventory/vault only, never committed code.
content:
source: ""
url: ""
proxy: ""
gpgcheck: true
satellite:
host: ""
ip: "" # optional /etc/hosts entry when DNS does not resolve host
org: ""
activation_key: ""
ca_url: ""
service_level: ""
environment: ""
install: false
packages: [] packages: []
disks: [] disks: []
users: {} users: {}
@@ -127,9 +143,6 @@ system_defaults:
banner: banner:
motd: false motd: false
sudo: true sudo: true
rhel_repo:
source: "iso" # iso|satellite|none - how RHEL systems get packages post-install
url: "" # Satellite/custom repo URL when source=satellite
chroot: chroot:
tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn tool: "arch-chroot" # arch-chroot|chroot|systemd-nspawn
initramfs: initramfs:

View File

@@ -91,12 +91,27 @@
timezone: "{{ system_raw.timezone | string }}" timezone: "{{ system_raw.timezone | string }}"
locale: "{{ system_raw.locale | string }}" locale: "{{ system_raw.locale | string }}"
keymap: "{{ system_raw.keymap | string }}" keymap: "{{ system_raw.keymap | string }}"
mirror: >- content:
{{ source: >-
system_raw.mirror | string | trim {%- set s = system_raw.content.source | default('') | string | lower | trim -%}
if (system_raw.mirror | default('') | string | trim | length) > 0 {%- if s | length > 0 -%}{{ s }}
else _mirror_defaults[system_raw.os | default('') | string | lower] | default('') {%- elif (system_raw.os | default('') | string | lower) == 'rhel' -%}dvd
}} {%- else -%}mirror{%- endif -%}
url: >-
{%- set u = system_raw.content.url | default('') | string | trim -%}
{%- if u | length > 0 -%}{{ u }}
{%- else -%}{{ _mirror_defaults[system_raw.os | default('') | string | lower] | default('') }}{%- endif -%}
proxy: "{{ system_raw.content.proxy | default('') | string | trim }}"
gpgcheck: "{{ system_raw.content.gpgcheck | default(true) | bool }}"
satellite:
host: "{{ system_raw.content.satellite.host | default('') | string | trim }}"
ip: "{{ system_raw.content.satellite.ip | default('') | string | trim }}"
org: "{{ system_raw.content.satellite.org | default('') | string }}"
activation_key: "{{ system_raw.content.satellite.activation_key | default('') | string }}"
ca_url: "{{ system_raw.content.satellite.ca_url | default('') | string | trim }}"
service_level: "{{ system_raw.content.satellite.service_level | default('') | string }}"
environment: "{{ system_raw.content.satellite.environment | default('') | string }}"
install: "{{ system_raw.content.satellite.install | default(false) | bool }}"
path: >- path: >-
{{ {{
(system_raw.path | default('') | string) (system_raw.path | default('') | string)
@@ -161,9 +176,6 @@
banner: banner:
motd: "{{ system_raw.features.banner.motd | bool }}" motd: "{{ system_raw.features.banner.motd | bool }}"
sudo: "{{ system_raw.features.banner.sudo | 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: chroot:
tool: "{{ system_raw.features.chroot.tool | string }}" tool: "{{ system_raw.features.chroot.tool | string }}"
initramfs: initramfs:

View File

@@ -50,23 +50,28 @@
ansible.builtin.set_fact: ansible.builtin.set_fact:
system_cfg: "{{ system_defaults | combine(system | default({}), recursive=True) | combine(system_cfg, recursive=True) }}" system_cfg: "{{ system_defaults | combine(system | default({}), recursive=True) | combine(system_cfg, recursive=True) }}"
- name: Apply mirror default for pre-computed system_cfg - name: Apply content-source family defaults for pre-computed system_cfg
when: when:
- system_cfg is defined - system_cfg is defined
- _bootstrap_needs_enrichment | default(false) | bool - _bootstrap_needs_enrichment | default(false) | bool
- system_cfg.mirror | default('') | string | trim | length == 0
vars: vars:
# Same as _normalize_system.yml - kept in sync manually. # Same family resolution as _normalize_system.yml - kept in sync manually.
_mirror_defaults: _mirror_defaults:
debian: "https://deb.debian.org/debian/" debian: "https://deb.debian.org/debian/"
ubuntu: "http://archive.ubuntu.com/ubuntu/" ubuntu: "http://archive.ubuntu.com/ubuntu/"
ubuntu-lts: "http://archive.ubuntu.com/ubuntu/" ubuntu-lts: "http://archive.ubuntu.com/ubuntu/"
_os: "{{ system_cfg.os | default('') | string | lower }}"
ansible.builtin.set_fact: ansible.builtin.set_fact:
system_cfg: >- system_cfg: >-
{{ {{
system_cfg | combine({ system_cfg | combine({'content': {
'mirror': _mirror_defaults[system_cfg.os | default('') | string | lower] | default('') 'source': system_cfg.content.source
}, recursive=True) if (system_cfg.content.source | default('') | string | trim | length > 0)
else ('dvd' if _os == 'rhel' else 'mirror'),
'url': system_cfg.content.url
if (system_cfg.content.url | default('') | string | trim | length > 0)
else (_mirror_defaults[_os] | default('')),
}}, recursive=True)
}} }}
- name: Populate primary network fields from first interface (pre-computed) - name: Populate primary network fields from first interface (pre-computed)

View File

@@ -148,8 +148,8 @@
- name: Validate RHEL ISO requirement - name: Validate RHEL ISO requirement
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- os != "rhel" or (rhel_iso is defined and (rhel_iso | string | length) > 0) - os != "rhel" or system_cfg.content.source == "mirror" or (rhel_iso is defined and (rhel_iso | string | length) > 0)
fail_msg: "rhel_iso is required when os=rhel." fail_msg: "rhel_iso is required when os=rhel unless content.source is mirror."
quiet: true quiet: true
- name: Validate hypervisor-specific required fields - name: Validate hypervisor-specific required fields

View File

@@ -0,0 +1,40 @@
---
- name: "Seed raw system input (content {{ cf.name }})"
ansible.builtin.set_fact:
system: "{{ cf.system }}"
- name: "Normalize system (content {{ cf.name }})"
ansible.builtin.include_tasks: ../../roles/global_defaults/tasks/_normalize_system.yml
- name: "Apply family defaults (content {{ cf.name }})"
ansible.builtin.include_tasks: ../../roles/global_defaults/tasks/_apply_family_defaults.yml
- name: "Assert computed content source and url (content {{ cf.name }})"
ansible.builtin.assert:
that:
- system_cfg.content.source == cf.expect.source
- system_cfg.content.url == cf.expect.url
fail_msg: "[content {{ cf.name }}] FAIL got {{ system_cfg.content }}"
success_msg: "[content {{ cf.name }}] OK {{ system_cfg.content.source }} {{ system_cfg.content.url }}"
- name: "Assert satellite register inputs (content {{ cf.name }})"
when: cf.expect.satellite is defined
ansible.builtin.assert:
that:
- system_cfg.content.satellite.host == cf.expect.satellite.host
- system_cfg.content.satellite.org == cf.expect.satellite.org
- system_cfg.content.satellite.activation_key == cf.expect.satellite.activation_key
fail_msg: "[content {{ cf.name }}] satellite FAIL got {{ system_cfg.content.satellite }}"
- name: "Assert Arch reflector lock-out (content {{ cf.name }})"
when: cf.expect_reflector is defined
block:
- name: "Load Arch package set (content {{ cf.name }})"
ansible.builtin.include_vars:
file: ../../roles/bootstrap/vars/main.yml
- name: "Assert reflector membership (content {{ cf.name }})"
ansible.builtin.assert:
that:
- ('reflector' in (bootstrap_archlinux.base + bootstrap_archlinux.conditional)) == (cf.expect_reflector == 'present')
fail_msg: "[content {{ cf.name }}] reflector expected {{ cf.expect_reflector }}, url='{{ system_cfg.content.url }}'"

View File

@@ -0,0 +1,78 @@
---
# Raw system.content input -> expected normalized system_cfg.content. Covers family
# source defaults (EL=dvd, else mirror), url pass-through, satellite inputs, and the
# Arch reflector lock-out (dropped only when a custom mirror url is set).
content_fixtures:
- name: arch-mirror-url-drops-reflector
system:
os: archlinux
content: { url: "https://mirror.internal/archlinux" }
expect:
source: mirror
url: "https://mirror.internal/archlinux"
expect_reflector: absent
- name: arch-no-url-keeps-public-and-reflector
system:
os: archlinux
content: {}
expect:
source: mirror
url: ""
expect_reflector: present
- name: el-mirror-url
system:
os: almalinux
version: "10"
content: { source: mirror, url: "https://mirror.internal/almalinux" }
expect:
source: mirror
url: "https://mirror.internal/almalinux"
- name: el-default-dvd
system:
os: rhel
version: "10"
content: {}
expect:
source: dvd
url: ""
- name: el-satellite-register
system:
os: almalinux
version: "10"
content:
source: satellite
satellite: { host: "sat.internal", org: "SG", activation_key: "ak-el10" }
expect:
source: satellite
url: ""
satellite: { host: "sat.internal", org: "SG", activation_key: "ak-el10" }
- name: debian-default-mirror
system:
os: debian
version: "13"
content: {}
expect:
source: mirror
url: "https://deb.debian.org/debian/"
- name: debian-custom-mirror-lands
system:
os: debian
version: "13"
content: { source: mirror, url: "https://mirror.internal/debian" }
expect:
source: mirror
url: "https://mirror.internal/debian"
- name: ubuntu-default-mirror
system:
os: ubuntu
content: {}
expect:
source: mirror
url: "http://archive.ubuntu.com/ubuntu/"

View File

@@ -0,0 +1,22 @@
---
# Run: ansible-playbook tests/content/test_content.yml
- name: Content-source fixture tests
hosts: localhost
gather_facts: false
connection: local
vars:
hypervisor_type: libvirt
hypervisor_cfg: { folder: "" }
vars_files:
- content_fixtures.yml
tasks:
- name: Load global defaults (system_defaults, content_mirror_defaults, os maps)
ansible.builtin.include_vars:
file: ../../roles/global_defaults/defaults/main.yml
- name: Run each content fixture
ansible.builtin.include_tasks: _assert_content.yml
loop: "{{ content_fixtures }}"
loop_control:
loop_var: cf
label: "{{ cf.name }}"