From f5619963b83df6bfdfd09e51d77067022c0a6928 Mon Sep 17 00:00:00 2001 From: vinayakray19 Date: Thu, 25 Jun 2026 14:29:44 +0530 Subject: [PATCH 1/6] refactor: make OCP install work directory configurable Add roles/common/tasks/resolve_ocp_install_workdir.yaml, which resolves a single ocp_install_workdir fact (defaulting to abi.ansible_workdir) and asserts it is defined. Use it across the prepare_configs, setup_params, create_agent, and abi_install_complete roles instead of hardcoding ~/ansible_workdir or repeating abi.ansible_workdir, so the install directory can be overridden per inventory and stays consistent. Co-authored-by: Cursor --- roles/abi_install_complete/tasks/main.yaml | 13 ++++++++----- .../tasks/resolve_ocp_install_workdir.yaml | 12 ++++++++++++ roles/create_agent/tasks/main.yml | 19 +++++++++++-------- roles/prepare_configs/tasks/main.yaml | 15 +++++++++------ roles/setup_params/tasks/main.yaml | 11 +++++++---- 5 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 roles/common/tasks/resolve_ocp_install_workdir.yaml diff --git a/roles/abi_install_complete/tasks/main.yaml b/roles/abi_install_complete/tasks/main.yaml index 2985a4e48..41af7e531 100644 --- a/roles/abi_install_complete/tasks/main.yaml +++ b/roles/abi_install_complete/tasks/main.yaml @@ -1,8 +1,11 @@ --- +- name: Resolve OCP install work directory + ansible.builtin.include_tasks: ../../common/tasks/resolve_ocp_install_workdir.yaml + - name: wait-for install-complete block: - name: Start openshift-installer with 'wait-for install-complete' (async task) - ansible.builtin.command: openshift-install agent wait-for install-complete --dir=/root/"{{ abi.ansible_workdir }}" + ansible.builtin.command: openshift-install agent wait-for install-complete --dir=/root/"{{ ocp_install_workdir }}" # Installer will wait up to ~70 min async: 4260 poll: 0 @@ -21,7 +24,7 @@ - name: FIPS wait-for install-complete block: - name: Start openshift-installer with 'wait-for install-complete' (async task) - ansible.builtin.command: openshift-install-fips agent wait-for install-complete --dir=/root/"{{ abi.ansible_workdir }}" + ansible.builtin.command: openshift-install-fips agent wait-for install-complete --dir=/root/"{{ ocp_install_workdir }}" # Installer will wait up to ~71 min async: 4260 poll: 0 @@ -47,7 +50,7 @@ - name: Copy kubeconfig to ~/.kube/config tags: kubeconfig copy: - src: /root/{{ abi.ansible_workdir }}/auth/kubeconfig + src: /root/{{ ocp_install_workdir }}/auth/kubeconfig dest: ~/.kube/config owner: "{{ ansible_user_id }}" group: "{{ ansible_user_gid }}" @@ -71,5 +74,5 @@ msg: - Installation completed! - Access the OpenShift web-console here --> https://console-openshift-console.apps.{{env.cluster.networking.metadata_name}}.{{env.cluster.networking.base_domain}} - - kubeadmin password of cluster is stored in ~/{{ abi.ansible_workdir }}/auth/kubeadmin-password on bastion. - - Login command --> oc login https://api.{{env.cluster.networking.metadata_name}}.{{env.cluster.networking.base_domain}}:6443 -u kubeadmin -p $( cat ~/{{ abi.ansible_workdir }}/auth/kubeadmin-password ) + - kubeadmin password of cluster is stored in ~/{{ ocp_install_workdir }}/auth/kubeadmin-password on bastion. + - Login command --> oc login https://api.{{env.cluster.networking.metadata_name}}.{{env.cluster.networking.base_domain}}:6443 -u kubeadmin -p $( cat ~/{{ ocp_install_workdir }}/auth/kubeadmin-password ) diff --git a/roles/common/tasks/resolve_ocp_install_workdir.yaml b/roles/common/tasks/resolve_ocp_install_workdir.yaml new file mode 100644 index 000000000..2c535c844 --- /dev/null +++ b/roles/common/tasks/resolve_ocp_install_workdir.yaml @@ -0,0 +1,12 @@ +--- +- name: Resolve OCP install work directory + ansible.builtin.set_fact: + ocp_install_workdir: "{{ abi.ansible_workdir }}" + when: ocp_install_workdir is not defined + +- name: Require OCP install work directory + ansible.builtin.assert: + that: + - ocp_install_workdir is defined + - ocp_install_workdir | length > 0 + fail_msg: "Define ocp_install_workdir or abi.ansible_workdir in group_vars/all.yaml." diff --git a/roles/create_agent/tasks/main.yml b/roles/create_agent/tasks/main.yml index c771eaa01..e0aeaa380 100644 --- a/roles/create_agent/tasks/main.yml +++ b/roles/create_agent/tasks/main.yml @@ -1,37 +1,40 @@ --- +- name: Resolve OCP install work directory + ansible.builtin.include_tasks: ../../common/tasks/resolve_ocp_install_workdir.yaml + - name: Create Agent Using PXE Boot (fips = false) ansible.builtin.command: openshift-install agent create pxe-files --log-level=debug args: - chdir: ~/{{ abi.ansible_workdir }} + chdir: ~/{{ ocp_install_workdir }} when: abi.boot_method | lower == "pxe" and not install_config_vars.fips - name: Create Agent Using ISO Image (fips = false) ansible.builtin.command: openshift-install agent create image --log-level=debug args: - chdir: ~/{{ abi.ansible_workdir }} + chdir: ~/{{ ocp_install_workdir }} when: abi.boot_method | lower == "iso" and not install_config_vars.fips - name: Create Agent Using PXE Boot (fips = true) ansible.builtin.command: openshift-install-fips agent create pxe-files --log-level=debug args: - chdir: ~/{{ abi.ansible_workdir }} + chdir: ~/{{ ocp_install_workdir }} when: abi.boot_method | lower == "pxe" and install_config_vars.fips - name: Create Agent Using ISO Image (fips = true) ansible.builtin.command: openshift-install-fips agent create image --log-level=debug args: - chdir: ~/{{ abi.ansible_workdir }} + chdir: ~/{{ ocp_install_workdir }} when: abi.boot_method | lower == "iso" and install_config_vars.fips - name: Check is vmlinuz exists in boot-artifacts stat: - path: "~/{{ abi.ansible_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-vmlinuz" + path: "~/{{ ocp_install_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-vmlinuz" register: vmlinuz - name: Copy and rename vmlinuz as kernel.img for PXE ansible.builtin.copy: - src: "~/{{ abi.ansible_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-vmlinuz" + src: "~/{{ ocp_install_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-vmlinuz" dest: /var/www/html/agent.{{ ansible_architecture }}-kernel.img mode: '644' remote_src: true @@ -39,7 +42,7 @@ - name: Copy initrd.img, kernel.img, and rootfs.img for PXE ansible.builtin.copy: - src: "~/{{ abi.ansible_workdir }}/boot-artifacts/" + src: "~/{{ ocp_install_workdir }}/boot-artifacts/" dest: /var/www/html/ mode: '645' remote_src: true @@ -47,7 +50,7 @@ - name: Copy ISO image to the server ansible.builtin.copy: - src: "~/{{ abi.ansible_workdir }}/" + src: "~/{{ ocp_install_workdir }}/" dest: /var/www/html/ mode: '645' remote_src: true diff --git a/roles/prepare_configs/tasks/main.yaml b/roles/prepare_configs/tasks/main.yaml index cd4eb27c4..c98131e96 100644 --- a/roles/prepare_configs/tasks/main.yaml +++ b/roles/prepare_configs/tasks/main.yaml @@ -1,12 +1,15 @@ --- +- name: Resolve OCP install work directory + ansible.builtin.include_tasks: ../../common/tasks/resolve_ocp_install_workdir.yaml + - name: Delete OCP Work Directory For Idempotency. file: - path: ~/ansible_workdir + path: "~/{{ ocp_install_workdir }}" state: absent - name: Create Work Directory file: - path: ~/ansible_workdir + path: "~/{{ ocp_install_workdir }}" state: directory - name: Prepare Agent Config @@ -18,8 +21,8 @@ mode: "0755" backup: yes loop: - - ~/ansible_workdir/agent-config.yaml - - ~/ansible_workdir/agent-config-backup.yaml + - "~/{{ ocp_install_workdir }}/agent-config.yaml" + - "~/{{ ocp_install_workdir }}/agent-config-backup.yaml" - name: Check if SSH key exists stat: @@ -45,5 +48,5 @@ mode: "0755" backup: yes loop: - - ~/ansible_workdir/install-config.yaml - - ~/ansible_workdir/install-config-backup.yaml + - "~/{{ ocp_install_workdir }}/install-config.yaml" + - "~/{{ ocp_install_workdir }}/install-config-backup.yaml" diff --git a/roles/setup_params/tasks/main.yaml b/roles/setup_params/tasks/main.yaml index e33ac2acd..d97175b8c 100644 --- a/roles/setup_params/tasks/main.yaml +++ b/roles/setup_params/tasks/main.yaml @@ -1,4 +1,7 @@ --- +- name: Resolve OCP install work directory + ansible.builtin.include_tasks: ../../common/tasks/resolve_ocp_install_workdir.yaml + - name: Ensure the PXE boot directory exists ansible.builtin.file: path: /var/lib/libvirt/images/pxeboot @@ -10,7 +13,7 @@ - name: Generate param files ansible.builtin.template: src: param-file.param.j2 - dest: ~/{{ abi.ansible_workdir }}/{{ env.cluster.networking.metadata_name }}-{{ item }}.param + dest: ~/{{ ocp_install_workdir }}/{{ env.cluster.networking.metadata_name }}-{{ item }}.param mode: '644' with_sequence: start=0 end={{ (zvm.nodes | length) - 1 }} stride=1 loop_control: @@ -19,21 +22,21 @@ - name: Copy rootfs.img ansible.builtin.copy: - src: "~/{{ abi.ansible_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-rootfs.img" + src: "~/{{ ocp_install_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-rootfs.img" dest: /var/www/html/rootfs.img mode: '745' remote_src: true - name: Copy initrd.img ansible.builtin.copy: - src: "~/{{ abi.ansible_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-initrd.img" + src: "~/{{ ocp_install_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-initrd.img" dest: /var/lib/libvirt/images/pxeboot/initrd.img mode: '745' remote_src: true - name: Copy kernel.img ansible.builtin.copy: - src: "~/{{ abi.ansible_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-kernel.img" + src: "~/{{ ocp_install_workdir }}/boot-artifacts/agent.{{ ansible_architecture }}-kernel.img" dest: /var/lib/libvirt/images/pxeboot/kernel.img mode: '745' remote_src: true From 54b27beb00793a8f548dd06f0f30b0a8b452f491 Mon Sep 17 00:00:00 2001 From: vinayakray19 Date: Thu, 25 Jun 2026 14:29:44 +0530 Subject: [PATCH 2/6] fix(dns): make bastion nameserver2 optional in named.conf named.conf unconditionally referenced env.bastion.networking.nameserver2 in the forwarders list, raising a Jinja UndefinedError when only a single upstream nameserver is configured. Guard the second forwarder and fall back to the cluster forwarder when nameserver2 is not defined. Co-authored-by: Cursor --- roles/dns/templates/dns-named.conf.j2 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/roles/dns/templates/dns-named.conf.j2 b/roles/dns/templates/dns-named.conf.j2 index 0db669dd3..d755e7659 100644 --- a/roles/dns/templates/dns-named.conf.j2 +++ b/roles/dns/templates/dns-named.conf.j2 @@ -18,7 +18,11 @@ options { secroots-file "/var/named/data/named.secroots"; recursing-file "/var/named/data/named.recursing"; allow-query { any; }; - forwarders { {{ env.bastion.networking.nameserver2}}; {{ env.cluster.networking.forwarder }}; }; +{% if env.bastion.networking.nameserver2 is defined %} + forwarders { {{ env.bastion.networking.nameserver2 }}; {{ env.cluster.networking.forwarder }}; }; +{% else %} + forwarders { {{ env.cluster.networking.forwarder }}; }; +{% endif %} /* - If you are building an AUTHORITATIVE DNS server, do NOT enable recursion. From e5832d42322552bfee6d5d736bc4824d97c28637 Mon Sep 17 00:00:00 2001 From: vinayakray19 Date: Thu, 25 Jun 2026 14:29:44 +0530 Subject: [PATCH 3/6] fix(boot_abi_agents): keep agent VMs running after ISO boot With --wait=-1 the virt-install async tasks left the ISO-booted agent VMs powered off, stalling the agent-based install. Remove --wait=-1 from both the control and compute ISO virt-install commands, then add a task that waits for every VM definition to register and an explicit step to ensure all agent VMs are running before the install proceeds. Co-authored-by: Cursor --- roles/boot_abi_agents/tasks/main.yml | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/roles/boot_abi_agents/tasks/main.yml b/roles/boot_abi_agents/tasks/main.yml index 655a57cd3..2d5e319d8 100644 --- a/roles/boot_abi_agents/tasks/main.yml +++ b/roles/boot_abi_agents/tasks/main.yml @@ -79,8 +79,7 @@ --disk size={{ env.cluster.nodes.control.disk_size }} \ --network network={{ env.vnet_name }}{{ (',mac=' + env.cluster.nodes.control.mac[i]) }} \ --graphics none \ - --noautoconsole \ - --wait=-1 \ + --noautoconsole async: 3600 poll: 0 with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }} stride=1 @@ -149,8 +148,7 @@ --disk size={{ env.cluster.nodes.compute.disk_size }} \ --network network={{ env.vnet_name }}{{ (',mac=' + env.cluster.nodes.compute.mac[i]) }} \ --graphics none \ - --noautoconsole \ - --wait=-1 \ + --noautoconsole async: 3600 poll: 0 with_sequence: start=0 end={{ (env.cluster.nodes.compute.hostname | length) - 1 }} stride=1 @@ -159,3 +157,22 @@ index_var: i pause: 10 when: env.cluster.nodes.compute is defined and abi.boot_method | lower == "iso" + +- name: Wait for all agent VM definitions to exist after ISO virt-install + ansible.builtin.shell: | + set -e + for vm in {{ env.cluster.nodes.control.vm_name | join(' ') }} {{ env.cluster.nodes.compute.vm_name | default([]) | join(' ') }}; do + virsh dominfo "$vm" >/dev/null 2>&1 + done + register: agent_vms_defined + until: agent_vms_defined.rc == 0 + retries: 36 + delay: 10 + when: abi.boot_method | lower == "iso" + +- name: Ensure ABI agent VMs are running after ISO boot + community.libvirt.virt: + name: "{{ item }}" + state: running + loop: "{{ env.cluster.nodes.control.vm_name + (env.cluster.nodes.compute.vm_name | default([])) }}" + when: abi.boot_method | lower == "iso" From b85eddc7642c70300d19a4a8bdd5379662edda52 Mon Sep 17 00:00:00 2001 From: vinayakray19 Date: Thu, 25 Jun 2026 14:29:44 +0530 Subject: [PATCH 4/6] fix(ssh_agent): run ssh_agent play as login user with secrets The Setup SSH agent play ran with the default become and without loading inventory vars, so it escalated to root and could not see secret-backed variables. Set become: false and load group_vars/all.yaml and group_vars/secrets.yaml so the ssh_agent role runs as the bastion login user with the values it expects. Co-authored-by: Cursor --- playbooks/create_abi_cluster.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/playbooks/create_abi_cluster.yaml b/playbooks/create_abi_cluster.yaml index 512b24ea5..0457b3250 100644 --- a/playbooks/create_abi_cluster.yaml +++ b/playbooks/create_abi_cluster.yaml @@ -47,5 +47,9 @@ - name: Setup SSH agent tags: ssh_to_nodes hosts: bastion + become: false + vars_files: + - "{{ inventory_dir }}/group_vars/all.yaml" + - "{{ inventory_dir }}/group_vars/secrets.yaml" roles: - ssh_agent From 396f13ba46277b417d8ecb74e938b179bb9a1e1b Mon Sep 17 00:00:00 2001 From: vinayakray19 Date: Thu, 25 Jun 2026 15:52:37 +0530 Subject: [PATCH 5/6] style(abi_install_complete): satisfy ansible-lint on changed file CI runs ansible-lint on changed files, and touching this role surfaced pre-existing violations. Use FQCN module names (file/copy/stat/debug), replace truthy `yes` with `true`, remove a trailing space, order task keys as name/when/block, and shorten the over-length web-console line. No behavioral change. Co-authored-by: Cursor --- roles/abi_install_complete/tasks/main.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/roles/abi_install_complete/tasks/main.yaml b/roles/abi_install_complete/tasks/main.yaml index 41af7e531..5db732655 100644 --- a/roles/abi_install_complete/tasks/main.yaml +++ b/roles/abi_install_complete/tasks/main.yaml @@ -3,6 +3,7 @@ ansible.builtin.include_tasks: ../../common/tasks/resolve_ocp_install_workdir.yaml - name: wait-for install-complete + when: not install_config_vars.fips block: - name: Start openshift-installer with 'wait-for install-complete' (async task) ansible.builtin.command: openshift-install agent wait-for install-complete --dir=/root/"{{ ocp_install_workdir }}" @@ -19,9 +20,9 @@ # Set wait time to 71 min, because it depends highly on system performance and network speed retries: 141 delay: 30 - when: not install_config_vars.fips - name: FIPS wait-for install-complete + when: install_config_vars.fips block: - name: Start openshift-installer with 'wait-for install-complete' (async task) ansible.builtin.command: openshift-install-fips agent wait-for install-complete --dir=/root/"{{ ocp_install_workdir }}" @@ -38,41 +39,40 @@ # Set wait time to 70 min, because it depends highly on system performance and network speed retries: 140 delay: 30 - when: install_config_vars.fips - name: Create ~/.kube directory if it does not exist tags: kubeconfig - file: + ansible.builtin.file: path: ~/.kube state: directory mode: '0755' - name: Copy kubeconfig to ~/.kube/config tags: kubeconfig - copy: + ansible.builtin.copy: src: /root/{{ ocp_install_workdir }}/auth/kubeconfig dest: ~/.kube/config owner: "{{ ansible_user_id }}" - group: "{{ ansible_user_gid }}" + group: "{{ ansible_user_gid }}" mode: '0600' - remote_src: yes + remote_src: true - name: Check if kubeconfig was copied tags: kubeconfig - stat: + ansible.builtin.stat: path: ~/.kube/config register: kubeconfig_status - name: Display kubeconfig message tags: kubeconfig - debug: + ansible.builtin.debug: msg: "Kubeconfig is set at ~/.kube/config. No need to export KUBECONFIG unless you want to use a different one." when: kubeconfig_status.stat.exists - name: Display cluster details post-install - debug: + ansible.builtin.debug: msg: - Installation completed! - - Access the OpenShift web-console here --> https://console-openshift-console.apps.{{env.cluster.networking.metadata_name}}.{{env.cluster.networking.base_domain}} + - Web console: https://console-openshift-console.apps.{{ env.cluster.networking.metadata_name }}.{{ env.cluster.networking.base_domain }} - kubeadmin password of cluster is stored in ~/{{ ocp_install_workdir }}/auth/kubeadmin-password on bastion. - Login command --> oc login https://api.{{env.cluster.networking.metadata_name}}.{{env.cluster.networking.base_domain}}:6443 -u kubeadmin -p $( cat ~/{{ ocp_install_workdir }}/auth/kubeadmin-password ) From 0c6267b5ea56564ff42b207e40d23774be0df50e Mon Sep 17 00:00:00 2001 From: vinayakray19 Date: Thu, 25 Jun 2026 16:38:07 +0530 Subject: [PATCH 6/6] fix(boot_abi_agents): make ISO --wait=-1 configurable (default on) Address review feedback that removing --wait=-1 could break the ABI flow. Restore upstream behavior by default: --wait=-1 is still passed for the ISO virt-install commands unless the new abi.iso_virt_install_wait toggle is set to false. This lets operators opt out on hosts where the async (poll: 0) virt-install job being reaped at the async timeout powers the agent VMs off mid-install. The follow-up "ensure VMs running" task stays as a safety net. Co-authored-by: Cursor --- inventories/default/group_vars/all.yaml.template | 4 ++++ roles/boot_abi_agents/tasks/main.yml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/inventories/default/group_vars/all.yaml.template b/inventories/default/group_vars/all.yaml.template index d0d5cecfc..2ceb04206 100644 --- a/inventories/default/group_vars/all.yaml.template +++ b/inventories/default/group_vars/all.yaml.template @@ -239,6 +239,10 @@ abi: ocp_installer_base_url: 'https://mirror.openshift.com/pub/openshift-v4/' architecture: boot_method: + # (Optional) Pass --wait=-1 to virt-install for ISO boot. Defaults to true + # (upstream behavior). Set to false on hosts where the async virt-install + # job being reaped at timeout can power the agent VMs off mid-install. + iso_virt_install_wait: true # Openshift Settings # Settings like architecture, hyperthreading, network cidr, etc. diff --git a/roles/boot_abi_agents/tasks/main.yml b/roles/boot_abi_agents/tasks/main.yml index 2d5e319d8..9729e8467 100644 --- a/roles/boot_abi_agents/tasks/main.yml +++ b/roles/boot_abi_agents/tasks/main.yml @@ -79,7 +79,7 @@ --disk size={{ env.cluster.nodes.control.disk_size }} \ --network network={{ env.vnet_name }}{{ (',mac=' + env.cluster.nodes.control.mac[i]) }} \ --graphics none \ - --noautoconsole + --noautoconsole {{ '--wait=-1' if (abi.iso_virt_install_wait | default(true)) else '' }} async: 3600 poll: 0 with_sequence: start=0 end={{ (env.cluster.nodes.control.hostname | length) - 1 }} stride=1 @@ -148,7 +148,7 @@ --disk size={{ env.cluster.nodes.compute.disk_size }} \ --network network={{ env.vnet_name }}{{ (',mac=' + env.cluster.nodes.compute.mac[i]) }} \ --graphics none \ - --noautoconsole + --noautoconsole {{ '--wait=-1' if (abi.iso_virt_install_wait | default(true)) else '' }} async: 3600 poll: 0 with_sequence: start=0 end={{ (env.cluster.nodes.compute.hostname | length) - 1 }} stride=1