Skip to content

Commit 7bf008e

Browse files
OSPRH-38067: Record platform-state after RHOSO in monolithic jobs
Add a record_platform_state stage that probes live RHOSO (OpenStackVersion.deployedVersion, OCP API, OpenStackControlPlane Ready) from the shiftstackclient pod and writes platform-state.yaml on the hypervisor in the same schema as the phased recorder. Enable the stage only on monolithic osp_verification* nightlies so warm Pipeline B can reuse Saturday platforms. Phased Pipeline A already records via ci-framework-jobs; Pipeline B stage overrides must not list this stage (redundant). Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent d5d1283 commit 7bf008e

11 files changed

Lines changed: 278 additions & 0 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
# Write the phased platform-state contract after RHOSO is already up.
3+
# Intended only for monolithic osp_verification* jobs; phased pipelines already
4+
# record state via ci-framework-jobs record-platform-state and must not list
5+
# this stage (Pipeline B overrides stages without it).
6+
#
7+
# Path and schema must stay aligned with
8+
# ci-framework-jobs/scenarios/baremetal/shiftstack/phased-platform-state-vars.yml
9+
record_platform_state_path: /home/zuul/ci-framework-data/platform-state.yaml
10+
record_platform_state_schema_version: 1
11+
record_platform_state_expected_rhoso_version: "18.0"
12+
record_platform_state_remote_user: root
13+
# rhoso_kubeconfig is passed by cifmw (-e rhoso_kubeconfig=...); no default.
14+
record_platform_state_openstack_namespace: openstack
15+
record_platform_state_openstackversion_name: controlplane
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
collections: []
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
# Probe live RHOSO from the shiftstackclient pod (incluster kubeconfig) and
3+
# write platform-state.yaml on the hypervisor for warm Pipeline B reuse.
4+
# Fail closed: do not write ready state when probes fail.
5+
6+
- name: Require hypervisor and rhoso_kubeconfig for platform-state recording
7+
ansible.builtin.assert:
8+
that:
9+
- hypervisor is defined
10+
- (hypervisor | string | length) > 0
11+
- rhoso_kubeconfig is defined
12+
- (rhoso_kubeconfig | string | length) > 0
13+
fail_msg: >-
14+
record_platform_state requires -e hypervisor=<fqdn> and
15+
-e rhoso_kubeconfig=<path> (cifmw passes both for monolithic jobs).
16+
17+
- name: Check rhoso_kubeconfig exists on the client pod
18+
ansible.builtin.stat:
19+
path: "{{ rhoso_kubeconfig }}"
20+
register: record_platform_state_kubeconfig_stat
21+
22+
- name: Fail when rhoso_kubeconfig is missing
23+
ansible.builtin.assert:
24+
that:
25+
- record_platform_state_kubeconfig_stat.stat.exists | default(false)
26+
fail_msg: >-
27+
record_platform_state: rhoso_kubeconfig not found at
28+
{{ rhoso_kubeconfig }}.
29+
30+
- name: Probe live OpenStackVersion.deployedVersion via rhoso_kubeconfig
31+
environment:
32+
KUBECONFIG: "{{ rhoso_kubeconfig }}"
33+
ansible.builtin.shell: |
34+
set -euo pipefail
35+
oc -n {{ record_platform_state_openstack_namespace }} \
36+
get openstackversion {{ record_platform_state_openstackversion_name }} \
37+
-o json | python3 -c "
38+
import json, re, sys
39+
data = json.load(sys.stdin)
40+
deployed = str((data.get('status') or {}).get('deployedVersion') or '').strip()
41+
if not deployed:
42+
sys.stderr.write('OpenStackVersion status.deployedVersion missing\n')
43+
sys.exit(1)
44+
match = re.match(r'^(\\d+\\.\\d+)', deployed)
45+
major_minor = match.group(1) if match else ''
46+
if not major_minor:
47+
sys.stderr.write(
48+
'unable to parse major.minor from deployedVersion=' + deployed + '\n'
49+
)
50+
sys.exit(1)
51+
print(deployed)
52+
print(major_minor)
53+
"
54+
args:
55+
executable: /bin/bash
56+
register: record_platform_state_version_probe
57+
changed_when: false
58+
failed_when: false
59+
60+
- name: Set live RHOSO version facts from probe
61+
vars:
62+
_stdout_lines: >-
63+
{{
64+
(record_platform_state_version_probe.stdout | default('') | trim).splitlines()
65+
}}
66+
ansible.builtin.set_fact:
67+
record_platform_state_rhoso_version_ok: >-
68+
{{
69+
(record_platform_state_version_probe.rc | default(1) == 0)
70+
and (_stdout_lines | length >= 2)
71+
}}
72+
record_platform_state_rhoso_version_full: >-
73+
{{
74+
_stdout_lines[0] | default('')
75+
if (
76+
(record_platform_state_version_probe.rc | default(1) == 0)
77+
and (_stdout_lines | length >= 2)
78+
)
79+
else ''
80+
}}
81+
record_platform_state_rhoso_version: >-
82+
{{
83+
_stdout_lines[1] | default('')
84+
if (
85+
(record_platform_state_version_probe.rc | default(1) == 0)
86+
and (_stdout_lines | length >= 2)
87+
)
88+
else ''
89+
}}
90+
91+
- name: Compare live RHOSO version against expected prefix
92+
vars:
93+
_expected: "{{ record_platform_state_expected_rhoso_version | string }}"
94+
_live: "{{ record_platform_state_rhoso_version_full | default('') | string }}"
95+
ansible.builtin.set_fact:
96+
record_platform_state_rhoso_version_matches_expected: >-
97+
{{
98+
(record_platform_state_rhoso_version_ok | default(false) | bool)
99+
and (_live | length > 0)
100+
and (
101+
(_live == _expected)
102+
or _live.startswith(_expected ~ '.')
103+
or _live.startswith(_expected ~ '-')
104+
)
105+
}}
106+
107+
- name: Probe RHOSO OCP API reachability via rhoso_kubeconfig
108+
environment:
109+
KUBECONFIG: "{{ rhoso_kubeconfig }}"
110+
ansible.builtin.command:
111+
cmd: oc get --raw=/readyz
112+
register: record_platform_state_ocp_probe
113+
changed_when: false
114+
failed_when: false
115+
116+
- name: Probe OpenStackControlPlane Ready condition via rhoso_kubeconfig
117+
environment:
118+
KUBECONFIG: "{{ rhoso_kubeconfig }}"
119+
ansible.builtin.shell: |
120+
set -euo pipefail
121+
oc -n {{ record_platform_state_openstack_namespace }} \
122+
get openstackcontrolplane controlplane -o json | python3 -c "
123+
import json, sys
124+
data = json.load(sys.stdin)
125+
ready = ''
126+
for cond in data.get('status', {}).get('conditions', []):
127+
if cond.get('type') == 'Ready':
128+
ready = str(cond.get('status', ''))
129+
break
130+
print(ready if ready else 'Unknown')
131+
sys.exit(0 if ready == 'True' else 1)
132+
"
133+
args:
134+
executable: /bin/bash
135+
register: record_platform_state_osp_probe
136+
changed_when: false
137+
failed_when: false
138+
139+
- name: Set platform health facts from probes
140+
ansible.builtin.set_fact:
141+
record_platform_state_ocp_api_reachable: >-
142+
{{ record_platform_state_ocp_probe.rc | default(1) == 0 }}
143+
record_platform_state_openstack_ready: >-
144+
{{ record_platform_state_osp_probe.rc | default(1) == 0 }}
145+
# Client-pod oc against the RHOSO API implies the control plane is reachable
146+
# for the purpose of this contract (phased also probes controller-0 SSH).
147+
record_platform_state_controller_reachable: >-
148+
{{
149+
(record_platform_state_ocp_probe.rc | default(1) == 0)
150+
and (record_platform_state_version_probe.rc | default(1) == 0)
151+
}}
152+
153+
- name: Fail without writing state when any probe fails
154+
ansible.builtin.fail:
155+
msg: >-
156+
Platform probes failed; refusing to write platform-state.yaml.
157+
controller_reachable={{ record_platform_state_controller_reachable | default(false) }};
158+
ocp_api_reachable={{ record_platform_state_ocp_api_reachable | default(false) }};
159+
openstack_ready={{ record_platform_state_openstack_ready | default(false) }}
160+
({{ record_platform_state_osp_probe.stdout | default('') | trim
161+
or record_platform_state_osp_probe.stderr | default('osp probe failed') | trim }});
162+
rhoso_version_ok={{ record_platform_state_rhoso_version_ok | default(false) }}
163+
rhoso_version_matches_expected={{ record_platform_state_rhoso_version_matches_expected | default(false) }}
164+
(live={{ record_platform_state_rhoso_version_full | default('missing') }}
165+
expected={{ record_platform_state_expected_rhoso_version }}).
166+
when: >-
167+
not (
168+
(record_platform_state_controller_reachable | default(false) | bool)
169+
and (record_platform_state_ocp_api_reachable | default(false) | bool)
170+
and (record_platform_state_openstack_ready | default(false) | bool)
171+
and (record_platform_state_rhoso_version_ok | default(false) | bool)
172+
and (record_platform_state_rhoso_version_matches_expected | default(false) | bool)
173+
)
174+
175+
- name: Ensure platform-state parent directory exists on hypervisor
176+
ansible.builtin.file:
177+
path: "{{ record_platform_state_path | dirname }}"
178+
state: directory
179+
mode: "0755"
180+
delegate_to: "{{ hypervisor }}"
181+
remote_user: "{{ record_platform_state_remote_user }}"
182+
vars:
183+
ansible_ssh_common_args: >-
184+
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
185+
186+
- name: Build platform-state payload
187+
ansible.builtin.set_fact:
188+
record_platform_state_payload:
189+
schema_version: "{{ record_platform_state_schema_version | int }}"
190+
hypervisor: "{{ hypervisor }}"
191+
rhoso_major: "{{ record_platform_state_rhoso_version.split('.')[0] }}"
192+
rhoso_version: "{{ record_platform_state_rhoso_version }}"
193+
rhoso_version_full: "{{ record_platform_state_rhoso_version_full }}"
194+
platform_status: ready
195+
phases_complete: [1, 2]
196+
controller_reachable: "{{ record_platform_state_controller_reachable | bool }}"
197+
ocp_api_reachable: "{{ record_platform_state_ocp_api_reachable | bool }}"
198+
openstack_ready: "{{ record_platform_state_openstack_ready | bool }}"
199+
recorded_at: "{{ lookup('pipe', 'date -u +%Y-%m-%dT%H:%M:%SZ') }}"
200+
last_zuul_job: >-
201+
{{
202+
lookup('env', 'ZUUL_JOB_NAME')
203+
| default(lookup('env', 'ZUUL_JOB'), true)
204+
| default('monolithic-osp-verification', true)
205+
}}
206+
207+
- name: Write platform-state.yaml on hypervisor
208+
ansible.builtin.copy:
209+
content: "{{ record_platform_state_payload | to_nice_yaml }}"
210+
dest: "{{ record_platform_state_path }}"
211+
mode: "0644"
212+
delegate_to: "{{ hypervisor }}"
213+
remote_user: "{{ record_platform_state_remote_user }}"
214+
vars:
215+
ansible_ssh_common_args: >-
216+
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
217+
218+
- name: Report recorded platform-state location
219+
ansible.builtin.debug:
220+
msg: >-
221+
Wrote platform-state.yaml to {{ hypervisor }}:{{ record_platform_state_path }}
222+
(rhoso_version_full={{ record_platform_state_rhoso_version_full }},
223+
rhoso_version={{ record_platform_state_rhoso_version }}).

‎jobs_definitions/osp_verification.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ openshift_release: 4-stable # It can be "4.10", "4.11", etc. or "4-stable"
1515
openshift_build_name: "" # It can be an empty value "" (latest nightly), a given build
1616
installation_type: ipi
1717
stages:
18+
# Monolithic-only: record platform-state after RHOSO for warm Pipeline B.
19+
# Do not add this stage to phased Pipeline B stage overrides.
20+
- record_platform_state
1821
- prepare
1922
- install
2023
- post

‎jobs_definitions/osp_verification_4.21_nightly.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ openshift_build_name: "" # Empty resolves to latest nightly via 4.21.0-0.nightly
1515
installation_type: ipi
1616
lb_tests_guest_cleanup: true
1717
stages:
18+
# Monolithic-only: record platform-state after RHOSO for warm Pipeline B.
19+
# Do not add this stage to phased Pipeline B stage overrides.
20+
- record_platform_state
1821
- prepare
1922
- install
2023
- post

‎jobs_definitions/osp_verification_4.22_nightly.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ openshift_build_name: "" # Empty resolves to latest nightly via 4.22.0-0.nightly
1515
installation_type: ipi
1616
lb_tests_guest_cleanup: true
1717
stages:
18+
# Monolithic-only: record platform-state after RHOSO for warm Pipeline B.
19+
# Do not add this stage to phased Pipeline B stage overrides.
20+
- record_platform_state
1821
- prepare
1922
- install
2023
- post

‎jobs_definitions/osp_verification_4.23_nightly.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ openshift_build_name: "" # Empty resolves to latest nightly via 4.23.0-0.nightly
1515
installation_type: ipi
1616
lb_tests_guest_cleanup: true
1717
stages:
18+
# Monolithic-only: record platform-state after RHOSO for warm Pipeline B.
19+
# Do not add this stage to phased Pipeline B stage overrides.
20+
- record_platform_state
1821
- prepare
1922
- install
2023
- post

‎jobs_definitions/osp_verification_5.0_nightly.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ openshift_build_name: "" # Empty resolves to latest nightly via 5.0.0-0.nightly/
1818
installation_type: ipi
1919
lb_tests_guest_cleanup: true
2020
stages:
21+
# Monolithic-only: record platform-state after RHOSO for warm Pipeline B.
22+
# Do not add this stage to phased Pipeline B stage overrides.
23+
- record_platform_state
2124
- prepare
2225
- install
2326
- post

‎jobs_definitions/osp_verification_5.1_nightly.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ openshift_build_name: "" # Empty resolves to latest nightly via 5.1.0-0.nightly/
1616
installation_type: ipi
1717
lb_tests_guest_cleanup: true
1818
stages:
19+
# Monolithic-only: record platform-state after RHOSO for warm Pipeline B.
20+
# Do not add this stage to phased Pipeline B stage overrides.
21+
- record_platform_state
1922
- prepare
2023
- install
2124
- post

‎playbooks/ocp_testing.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
---
2+
# Monolithic osp_verification* only: write platform-state.yaml after RHOSO is up
3+
# so warm Pipeline B can reuse the platform. Phased jobs must not list this stage
4+
# (Pipeline B overrides stages without it; Pipeline A already records via jobs).
5+
- name: Record platform-state for monolithic warm reuse
6+
ansible.builtin.import_playbook: plays/record_platform_state.yaml
7+
when: "'record_platform_state' in stages"
8+
29
- name: Cleanup the OCP setup and prepare the environment for testing OCP on OpenStack 18
310
hosts: localhost
411
gather_facts: yes

0 commit comments

Comments
 (0)