Skip to content

Commit afc69b4

Browse files
committed
Convert install-config generation from bash heredoc to Ansible
Replace the generate_ocp_install_config() bash heredoc in ocp_install_env.sh with an Ansible role and Jinja2 template. 05_create_install_config.sh now sources the existing config chain for environment variable resolution, generates YAML extra-vars via a config bridge script, and calls ansible-playbook to render install-config.yaml. This is the first step toward migrating dev-scripts from imperative bash to declarative Ansible roles. The extra worker manifest generation remains in bash for now. Assisted-By: Claude Opus 4.6
1 parent e741cb5 commit afc69b4

12 files changed

Lines changed: 806 additions & 8 deletions

File tree

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ scratch-test/
1818
assets/templates/99_master-chronyd-redhat.yaml
1919
assets/templates/99_worker-chronyd-redhat.yaml
2020

21-
pull_secret.json
21+
pull_secret.json
22+
23+
# Auto-generated ansible vars (may contain secrets)
24+
ansible/.config_vars.yml
25+
ansible/.runtime_vars.yml

05_create_install_config.sh

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,79 @@ early_deploy_validation
1414
set_api_and_ingress_vip
1515

1616
if [ ! -f "${OCP_DIR}/install-config.yaml" ]; then
17-
# Validate there are enough nodes to avoid confusing errors later..
1817
NODES_LEN=$(jq '.nodes | length' "${NODES_FILE}")
1918
if (( NODES_LEN < NUM_MASTERS + NUM_WORKERS )); then
2019
echo "ERROR: ${NODES_FILE} contains ${NODES_LEN} nodes, but ${NUM_MASTERS} masters and ${NUM_WORKERS} workers requested"
2120
exit 1
2221
fi
2322

24-
# Create a nodes.json file
25-
mkdir -p "${OCP_DIR}"
26-
jq '{nodes: .}' "${NODES_FILE}" | tee "${BAREMETALHOSTS_FILE}"
23+
# Generate base config vars from the resolved bash environment
24+
SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25+
"${SCRIPTDIR}/ansible/vars_bridge.sh"
2726

28-
# Create install config for openshift-installer
29-
generate_ocp_install_config "${OCP_DIR}"
27+
EXTRA_VARS_FILE="${SCRIPTDIR}/ansible/.runtime_vars.yml"
28+
29+
# Append runtime-computed values that vars_bridge.sh does not cover
30+
cat > "${EXTRA_VARS_FILE}" << RUNTIME_EOF
31+
# Runtime vars generated by 05_create_install_config.sh
32+
devscripts_machine_os_bootstrap_image_name: "${MACHINE_OS_BOOTSTRAP_IMAGE_NAME}"
33+
devscripts_machine_os_bootstrap_image_sha256: "${MACHINE_OS_BOOTSTRAP_IMAGE_UNCOMPRESSED_SHA256}"
34+
devscripts_nodes_file: "${NODES_FILE}"
35+
devscripts_ocp_dir: "${OCP_DIR}"
36+
devscripts_scriptdir: "${SCRIPTDIR}"
37+
RUNTIME_EOF
38+
39+
# BMC verify CA certificate (OCP >= 4.22)
40+
BMC_CA_FILE="${WORKING_DIR}/virtualbmc/sushy-tools/cert.pem"
41+
if [[ -f "${BMC_CA_FILE}" ]] && ! is_lower_version "$(openshift_version "${OCP_DIR}")" "4.22"; then
42+
{
43+
echo 'devscripts_bmc_verify_ca: |'
44+
sed 's/^/ /' "${BMC_CA_FILE}"
45+
} >> "${EXTRA_VARS_FILE}"
46+
fi
47+
48+
# Image mirror content sources and trust bundle
49+
MIRROR_CONTENT=$(image_mirror_config || true)
50+
if [[ -n "${MIRROR_CONTENT}" ]]; then
51+
# Extract imageContentSources as JSON array for the template
52+
ICS_YAML=$(echo "${MIRROR_CONTENT}" | sed -n '/^imageContentSources:/,/^[a-zA-Z]/p' | sed '$d')
53+
if [[ -n "${ICS_YAML}" ]]; then
54+
echo "${ICS_YAML}" >> "${EXTRA_VARS_FILE}"
55+
fi
56+
57+
# Extract additionalTrustBundle content
58+
TRUST_BUNDLE=$(echo "${MIRROR_CONTENT}" | sed -n '/^additionalTrustBundle:/,$ p')
59+
if [[ -n "${TRUST_BUNDLE}" ]]; then
60+
{
61+
echo "devscripts_additional_trust_bundle_content: |"
62+
echo "${TRUST_BUNDLE}" | tail -n +2
63+
} >> "${EXTRA_VARS_FILE}"
64+
fi
65+
elif [[ -n "${ADDITIONAL_TRUST_BUNDLE:-}" ]]; then
66+
{
67+
echo "devscripts_additional_trust_bundle_content: |"
68+
awk '{ print " ", $0 }' "${ADDITIONAL_TRUST_BUNDLE}"
69+
} >> "${EXTRA_VARS_FILE}"
70+
fi
71+
72+
# Proxy variables (when INSTALLER_PROXY is set)
73+
if [[ -n "${INSTALLER_PROXY:-}" ]]; then
74+
cat >> "${EXTRA_VARS_FILE}" << PROXY_EOF
75+
devscripts_http_proxy: "${HTTP_PROXY}"
76+
devscripts_https_proxy: "${HTTPS_PROXY}"
77+
devscripts_no_proxy: "${NO_PROXY}"
78+
PROXY_EOF
79+
fi
80+
81+
ANSIBLE_FORCE_COLOR=true ansible-playbook \
82+
-i "${SCRIPTDIR}/ansible/inventory/hosts" \
83+
--extra-vars "@${SCRIPTDIR}/ansible/.config_vars.yml" \
84+
--extra-vars "@${EXTRA_VARS_FILE}" \
85+
-v \
86+
"${SCRIPTDIR}/ansible/playbooks/install_config.yml"
3087
fi
3188

32-
# Generate the assets for extra worker VMs
89+
# Generate the assets for extra worker VMs (not yet migrated to Ansible)
3390
if [ -f "${EXTRA_NODES_FILE}" ]; then
3491
jq '.nodes' "${EXTRA_NODES_FILE}" | tee "${EXTRA_BAREMETALHOSTS_FILE}"
3592
generate_ocp_host_manifest "${OCP_DIR}" "${EXTRA_BAREMETALHOSTS_FILE}" extra_host_manifests.yaml "${EXTRA_WORKERS_NAMESPACE}"

ansible/group_vars/all/main.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Cluster identity
2+
devscripts_cluster_name: ostest
3+
devscripts_base_domain: test.metalkube.org
4+
devscripts_working_dir: /opt/dev-scripts
5+
devscripts_ssh_pub_key: "{{ lookup('file', ansible_env.HOME + '/.ssh/id_rsa.pub') }}"
6+
7+
# Release image
8+
# These mirror the bash defaults from common.sh.
9+
# Set devscripts_release_image to pin an explicit image and skip resolution.
10+
devscripts_release_stream: "4.22"
11+
devscripts_release_type: nightly
12+
devscripts_release_image: ""
13+
devscripts_openshift_version: ""
14+
15+
# CI mode — when true, skips pull-secret merge, uses mixed BMC driver, etc.
16+
devscripts_openshift_ci: false
17+
18+
# Security and features
19+
devscripts_fips_mode: false
20+
devscripts_feature_set: ""
21+
devscripts_feature_gates: ""
22+
devscripts_os_image_stream: ""
23+
24+
# Capabilities
25+
devscripts_baseline_capability_set: ""
26+
devscripts_additional_capabilities: ""
27+
28+
# Workload partitioning (set to true to add cpuPartitioningMode: AllNodes)
29+
devscripts_enable_workload_partitioning: false
30+
31+
# Node counts
32+
devscripts_num_masters: 3
33+
devscripts_num_workers: 2
34+
devscripts_num_arbiters: 0
35+
devscripts_num_extra_workers: 0
36+
37+
# BMC driver: redfish, redfish-virtualmedia, ipmi, mixed
38+
devscripts_bmc_driver: redfish
39+
40+
# Misc
41+
devscripts_disable_multicast: false
42+
devscripts_enable_bootstrap_static_ip: false
43+
devscripts_external_loadbalancer: false

ansible/group_vars/all/network.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# IP stack configuration
2+
# Choices: v4, v6, v4v6, v6v4
3+
devscripts_ip_stack: v6
4+
devscripts_host_ip_stack: "{{ devscripts_ip_stack }}"
5+
6+
# Provisioning network profile: Managed or Disabled
7+
devscripts_provisioning_network_profile: Managed
8+
9+
# Network names (derived from cluster name by default)
10+
devscripts_provisioning_network_name: "{{ devscripts_cluster_name }}pr"
11+
devscripts_baremetal_network_name: "{{ devscripts_cluster_name }}bm"
12+
13+
# Provisioning network CIDR — defaults depend on host_ip_stack.
14+
# Override explicitly or leave empty to use the ip-stack-based default.
15+
devscripts_provisioning_network: >-
16+
{%- if devscripts_host_ip_stack in ['v4', 'v4v6'] -%}
17+
172.22.0.0/24
18+
{%- else -%}
19+
fd00:1101::0/64
20+
{%- endif -%}
21+
22+
# External subnets — defaults depend on host_ip_stack.
23+
# For single-stack, the unused family is left empty.
24+
devscripts_external_subnet_v4: >-
25+
{%- if devscripts_host_ip_stack in ['v4', 'v4v6', 'v6v4'] -%}
26+
192.168.111.0/24
27+
{%- else -%}
28+
{%- endif -%}
29+
30+
devscripts_external_subnet_v6: >-
31+
{%- if devscripts_host_ip_stack in ['v6', 'v4v6', 'v6v4'] -%}
32+
fd2e:6f44:5dd8:c956::/120
33+
{%- else -%}
34+
{%- endif -%}
35+
36+
# Cluster networking — defaults depend on ip_stack.
37+
devscripts_cluster_subnet_v4: >-
38+
{%- if devscripts_ip_stack in ['v4', 'v4v6', 'v6v4'] -%}
39+
10.128.0.0/14
40+
{%- else -%}
41+
{%- endif -%}
42+
43+
devscripts_cluster_subnet_v6: >-
44+
{%- if devscripts_ip_stack in ['v6', 'v4v6', 'v6v4'] -%}
45+
fd01::/48
46+
{%- else -%}
47+
{%- endif -%}
48+
49+
devscripts_cluster_host_prefix_v4: >-
50+
{%- if devscripts_ip_stack in ['v4', 'v4v6', 'v6v4'] -%}
51+
23
52+
{%- else -%}
53+
{%- endif -%}
54+
55+
devscripts_cluster_host_prefix_v6: >-
56+
{%- if devscripts_ip_stack in ['v6', 'v4v6', 'v6v4'] -%}
57+
64
58+
{%- else -%}
59+
{%- endif -%}
60+
61+
devscripts_service_subnet_v4: >-
62+
{%- if devscripts_ip_stack in ['v4', 'v4v6', 'v6v4'] -%}
63+
172.30.0.0/16
64+
{%- else -%}
65+
{%- endif -%}
66+
67+
devscripts_service_subnet_v6: >-
68+
{%- if devscripts_ip_stack in ['v6', 'v4v6', 'v6v4'] -%}
69+
fd02::/112
70+
{%- else -%}
71+
{%- endif -%}
72+
73+
# Network type — OVNKubernetes is required for IPv6 and dual-stack.
74+
# For IPv4-only on OCP < 4.15, OpenShiftSDN was the default.
75+
devscripts_network_type: OVNKubernetes
76+
77+
# Provisioning interface inside the cluster nodes
78+
devscripts_cluster_provisioning_interface: enp1s0
79+
80+
# Proxy settings (set devscripts_installer_proxy to true to enable)
81+
devscripts_installer_proxy: false
82+
devscripts_installer_proxy_port: 8215
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Local registry — set devscripts_enable_local_registry to true to start one
2+
devscripts_enable_local_registry: false
3+
4+
# Registry backend: podman or quay
5+
devscripts_registry_backend: podman
6+
devscripts_registry_port: "5000"
7+
devscripts_registry_user: ocp-user
8+
devscripts_registry_pass: ocp-pass
9+
devscripts_registry_dir: "{{ devscripts_working_dir }}/registry"
10+
devscripts_registry_dns_name: "virthost.{{ devscripts_cluster_name }}.{{ devscripts_base_domain }}"
11+
devscripts_registry_crt: registry.2.crt
12+
13+
# Image mirroring — required for IPv6, optional for IPv4
14+
# When left empty, defaults to true for v6 ip_stack.
15+
devscripts_mirror_images: >-
16+
{%- if devscripts_ip_stack == 'v6' -%}
17+
true
18+
{%- else -%}
19+
false
20+
{%- endif -%}
21+
22+
# Mirror command: oc-adm or oc-mirror
23+
devscripts_mirror_command: oc-adm
24+
25+
# OLM operators to mirror (comma-separated)
26+
devscripts_mirror_olm: ""
27+
28+
# Custom images to mirror (comma-separated)
29+
devscripts_mirror_custom_images: ""
30+
31+
# Additional trust bundle PEM file path (leave empty to skip)
32+
devscripts_additional_trust_bundle: ""

ansible/inventory/hosts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[all]
2+
localhost ansible_connection=local
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
- name: Generate install-config.yaml
3+
hosts: localhost
4+
gather_facts: true
5+
pre_tasks:
6+
- name: Load nodes file
7+
ansible.builtin.set_fact:
8+
devscripts_nodes: "{{ (lookup('file', devscripts_nodes_file) | from_json).nodes }}"
9+
10+
- name: Resolve registry credentials path
11+
ansible.builtin.set_fact:
12+
devscripts_registry_creds: >-
13+
{{ ansible_env.HOME }}/private-mirror-{{ devscripts_cluster_name }}.json
14+
15+
roles:
16+
- role: "{{ playbook_dir }}/../roles/install_config"
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Role defaults — lowest precedence; overridden by group_vars or extra-vars.
2+
# Variables not yet migrated to group_vars fall back to lookup('env').
3+
4+
# Paths derived from group_vars
5+
devscripts_ocp_dir: "{{ devscripts_scriptdir }}/ocp/{{ devscripts_cluster_name }}"
6+
devscripts_scriptdir: "{{ playbook_dir }}/../.."
7+
devscripts_nodes_file: "{{ devscripts_working_dir }}/{{ devscripts_cluster_name }}/ironic_nodes.json"
8+
devscripts_baremetalhosts_file: "{{ devscripts_ocp_dir }}/baremetalhosts.json"
9+
devscripts_pull_secret_file: "{{ devscripts_working_dir }}/pull_secret.json"
10+
devscripts_cluster_domain: "{{ devscripts_cluster_name }}.{{ devscripts_base_domain }}"
11+
12+
# Computed network values
13+
devscripts_provisioning_host_external_ip: >-
14+
{% if devscripts_host_ip_stack in ['v6', 'v6v4'] -%}
15+
{{ devscripts_external_subnet_v6 | ansible.utils.nthhost(1) }}
16+
{%- else -%}
17+
{{ devscripts_external_subnet_v4 | ansible.utils.nthhost(1) }}
18+
{%- endif %}
19+
devscripts_mirror_ip: "{{ devscripts_provisioning_host_external_ip }}"
20+
21+
# DNS VIP
22+
devscripts_dns_vip: >-
23+
{% if devscripts_ip_stack == 'v6' -%}
24+
{{ devscripts_external_subnet_v6 | ansible.utils.nthhost(2) }}
25+
{%- else -%}
26+
{{ devscripts_external_subnet_v4 | ansible.utils.nthhost(2) }}
27+
{%- endif %}
28+
29+
# VIPs — will be resolved in tasks from libvirt DNS or computed from subnets
30+
devscripts_api_vips: []
31+
devscripts_ingress_vips: []
32+
33+
# Bootstrap static IP (.9 on the primary external subnet)
34+
devscripts_bootstrap_static_ip: >-
35+
{% if devscripts_enable_bootstrap_static_ip -%}
36+
{% if devscripts_ip_stack in ['v6', 'v6v4'] -%}
37+
{{ devscripts_external_subnet_v6 | ansible.utils.nthhost(9) }}
38+
{%- else -%}
39+
{{ devscripts_external_subnet_v4 | ansible.utils.nthhost(9) }}
40+
{%- endif %}
41+
{%- endif %}
42+
43+
# Provisioning IPs
44+
devscripts_bootstrap_provisioning_ip: >-
45+
{% if devscripts_provisioning_network_profile == 'Disabled' -%}
46+
{% if devscripts_host_ip_stack == 'v6' -%}
47+
{{ devscripts_external_subnet_v6 | ansible.utils.nthhost(7) }}
48+
{%- else -%}
49+
{{ devscripts_external_subnet_v4 | ansible.utils.nthhost(7) }}
50+
{%- endif %}
51+
{%- else -%}
52+
{{ devscripts_provisioning_network | ansible.utils.nthhost(2) }}
53+
{%- endif %}
54+
55+
devscripts_cluster_provisioning_ip: >-
56+
{% if devscripts_provisioning_network_profile == 'Disabled' -%}
57+
{% if devscripts_host_ip_stack == 'v6' -%}
58+
{{ devscripts_external_subnet_v6 | ansible.utils.nthhost(8) }}
59+
{%- else -%}
60+
{{ devscripts_external_subnet_v4 | ansible.utils.nthhost(8) }}
61+
{%- endif %}
62+
{%- else -%}
63+
{{ devscripts_provisioning_network | ansible.utils.nthhost(3) }}
64+
{%- endif %}
65+
66+
# Not yet migrated — fall back to environment variables
67+
devscripts_root_disk_name: "{{ lookup('env', 'ROOT_DISK_NAME') | default('/dev/sda', true) }}"
68+
devscripts_remote_libvirt: "{{ lookup('env', 'REMOTE_LIBVIRT') | default('0', true) }}"
69+
devscripts_provisioning_host_user: "{{ lookup('env', 'PROVISIONING_HOST_USER') | default(ansible_user_id, true) }}"
70+
devscripts_provisioning_host_ip: "{{ lookup('env', 'PROVISIONING_HOST_IP') | default(devscripts_provisioning_network | ansible.utils.nthhost(1), true) }}"
71+
devscripts_network_config_folder: "{{ lookup('env', 'NETWORK_CONFIG_FOLDER') | default('', true) }}"
72+
devscripts_enable_two_node_fencing: "{{ lookup('env', 'ENABLE_TWO_NODE_FENCING') | default('false', true) | bool }}"
73+
devscripts_master_hostname_format: "{{ lookup('env', 'MASTER_HOSTNAME_FORMAT') | default('master-%d', true) }}"

0 commit comments

Comments
 (0)