Skip to content

Commit ffb5ab7

Browse files
osp_verification: purge bogus Machines and harden LB suite cleanup
Purge bogus-* leftovers before openstack_test, enable nightly LB guest cleanup including lb-sg-*, and fix serial OTE skip exits plus empty late sourceRanges junit. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 1c9e88e commit ffb5ab7

11 files changed

Lines changed: 140 additions & 16 deletions

‎collection/stages/roles/lb_tests/tasks/cleanup_lb_test_leftovers.yml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,23 @@
6767
OS_CLOUD: "{{ user_cloud }}"
6868
changed_when: false
6969
failed_when: false
70+
71+
# Orphan lb-sg-* Neutron SGs left on node ports trip ProviderSpec SG matching
72+
# when lb_tests runs before openstack_test (seen on 4.21 warm re-runs).
73+
- name: Best-effort remove orphan lb-sg-* security group attachments and SGs
74+
ansible.builtin.shell: |
75+
set -o pipefail
76+
openstack security group list -f value -c ID -c Name 2>/dev/null | while read -r sg_id sg_name; do
77+
case "$sg_name" in
78+
lb-sg-*)
79+
for port_id in $(openstack port list --security-group "$sg_id" -f value -c ID 2>/dev/null); do
80+
openstack port unset --security-group "$sg_id" "$port_id" 2>/dev/null || true
81+
done
82+
openstack security group delete "$sg_id" 2>/dev/null || true
83+
;;
84+
esac
85+
done
86+
environment:
87+
OS_CLOUD: "{{ user_cloud }}"
88+
changed_when: false
89+
failed_when: false

‎collection/stages/roles/lb_tests/tasks/main.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,7 @@
101101
openstack_test_ote_run_serial: true
102102
openstack_testsuite_name: openstack_tests_lb_ovn
103103
openstack_reset_result_dir: no # As we want to keep the logs generated in the previous step
104+
105+
- name: Best-effort cleanup after LB tests (before openstack_test)
106+
ansible.builtin.include_tasks: cleanup_lb_test_leftovers.yml
107+
when: lb_tests_guest_cleanup | bool

‎collection/stages/roles/openstack_test/files/ote_resolve_results.py‎

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,47 @@
2323
from typing import Any
2424

2525

26+
def _is_ote_shaped(entry: Any) -> bool:
27+
return isinstance(entry, dict) and "result" in entry
28+
29+
2630
def _load_outer_results(raw: str) -> list[dict[str, Any]]:
2731
raw = raw.strip()
2832
if not raw:
2933
return []
3034

31-
# Skip leading klog / noise before the JSON array/object.
32-
start_candidates = [i for i, ch in enumerate(raw) if ch in "[{"]
33-
for start in start_candidates:
34-
chunk = raw[start:]
35+
# Scan top-level JSON values. Skip empty lists / non-OTE-shaped JSON
36+
# (e.g. bare ``[]`` from ginkgo text like ``map[]``) so we reach the real
37+
# result array(s) at the end of the log. Advance past each decoded value
38+
# to avoid re-parsing nested JSON inside ``output`` fields. Serial
39+
# run-test appends multiple arrays — collect all OTE-shaped entries.
40+
results: list[dict[str, Any]] = []
41+
decoder = json.JSONDecoder()
42+
i = 0
43+
n = len(raw)
44+
while i < n:
45+
while i < n and raw[i] not in "[{":
46+
i += 1
47+
if i >= n:
48+
break
3549
try:
36-
data, _ = json.JSONDecoder().raw_decode(chunk)
50+
data, end = decoder.raw_decode(raw[i:])
3751
except json.JSONDecodeError:
52+
i += 1
3853
continue
54+
i = i + end
3955
if isinstance(data, list):
40-
return [r for r in data if isinstance(r, dict)]
41-
if isinstance(data, dict):
42-
return [data]
56+
ote = [r for r in data if _is_ote_shaped(r)]
57+
if ote:
58+
results.extend(ote)
59+
continue
60+
if _is_ote_shaped(data):
61+
results.append(data)
62+
63+
if results:
64+
return results
4365

4466
# NDJSON fallback
45-
results: list[dict[str, Any]] = []
4667
for line in raw.splitlines():
4768
line = line.strip()
4869
if not line or not line.startswith("{"):
@@ -51,7 +72,7 @@ def _load_outer_results(raw: str) -> list[dict[str, Any]]:
5172
obj = json.loads(line)
5273
except json.JSONDecodeError:
5374
continue
54-
if isinstance(obj, dict):
75+
if _is_ote_shaped(obj):
5576
results.append(obj)
5677
return results
5778

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
# Best-effort purge of leftover bogus-* MachineSets/Machines before openstack_test.
3+
# Pairs with openstack-test bz_2073398 cleanup; suite-level hygiene if that It left
4+
# stuck objects that poison later Machine / MachineSet / ProviderSpec specs.
5+
- name: List bogus MachineSets in openshift-machine-api
6+
ansible.builtin.shell: |
7+
set -o pipefail
8+
oc get machineset -n openshift-machine-api -o name 2>/dev/null \
9+
| grep -E 'bogus-' || true
10+
environment:
11+
KUBECONFIG: "{{ kubeconfig }}"
12+
register: bogus_machinesets_cmd
13+
changed_when: false
14+
failed_when: false
15+
16+
- name: List bogus Machines in openshift-machine-api
17+
ansible.builtin.shell: |
18+
set -o pipefail
19+
oc get machine -n openshift-machine-api -o name 2>/dev/null \
20+
| grep -E 'bogus-' || true
21+
environment:
22+
KUBECONFIG: "{{ kubeconfig }}"
23+
register: bogus_machines_cmd
24+
changed_when: false
25+
failed_when: false
26+
27+
- name: Delete leftover bogus MachineSets
28+
ansible.builtin.command:
29+
argv:
30+
- oc
31+
- delete
32+
- "{{ item }}"
33+
- -n
34+
- openshift-machine-api
35+
- --wait=false
36+
environment:
37+
KUBECONFIG: "{{ kubeconfig }}"
38+
loop: "{{ bogus_machinesets_cmd.stdout_lines | default([]) }}"
39+
loop_control:
40+
label: "{{ item }}"
41+
register: bogus_ms_delete
42+
changed_when: bogus_ms_delete.rc == 0
43+
failed_when: false
44+
when: bogus_machinesets_cmd.stdout_lines | default([]) | length > 0
45+
46+
- name: Delete leftover bogus Machines
47+
ansible.builtin.command:
48+
argv:
49+
- oc
50+
- delete
51+
- "{{ item }}"
52+
- -n
53+
- openshift-machine-api
54+
- --wait=false
55+
environment:
56+
KUBECONFIG: "{{ kubeconfig }}"
57+
loop: "{{ bogus_machines_cmd.stdout_lines | default([]) }}"
58+
loop_control:
59+
label: "{{ item }}"
60+
register: bogus_machine_delete
61+
changed_when: bogus_machine_delete.rc == 0
62+
failed_when: false
63+
when: bogus_machines_cmd.stdout_lines | default([]) | length > 0
64+
65+
- name: Short wait after bogus Machine purge
66+
ansible.builtin.pause:
67+
seconds: 15
68+
when: >-
69+
(bogus_machinesets_cmd.stdout_lines | default([]) | length > 0)
70+
or (bogus_machines_cmd.stdout_lines | default([]) | length > 0)

‎collection/stages/roles/openstack_test/tasks/main.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
delegate_to: "{{ hypervisor }}"
3333
remote_user: root
3434

35+
- name: Best-effort purge leftover bogus Machines before openstack-test
36+
ansible.builtin.include_tasks: cleanup_bogus_machines.yml
37+
3538
- name: Include Openstack-Test tasks
3639
ansible.builtin.include_tasks: run_openstack_test.yml
3740

‎collection/stages/roles/openstack_test/tasks/run_openstack_test.yml‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,19 @@
188188
ansible.builtin.shell: |
189189
set -o pipefail
190190
: > {{ openstack_test_log_path }}
191-
run_exit=0
192191
py_exit=0
193192
while IFS= read -r test || [[ -n "$test" ]]; do
194193
[[ -z "${test// }" ]] && continue
195-
if ! {{ openstack_test_executable }} run-test --output=json "$test" \
196-
>> {{ openstack_test_log_path }} 2>&1; then
197-
run_exit=1
198-
fi
194+
# Do not treat run-test non-zero alone as failure: skip-only cases
195+
# often exit non-zero while the resolved log classifies them as skip.
196+
{{ openstack_test_executable }} run-test --output=json "$test" \
197+
>> {{ openstack_test_log_path }} 2>&1 || true
199198
done < {{ tests_to_run_path }}
200199
python3 "{{ openstack_test_ote_resolve_script }}" junit \
201200
"{{ openstack_test_log_path }}" "{{ openstack_test_junit_path }}" || py_exit=$?
202-
if [ "$run_exit" -ne 0 ] || [ "$py_exit" -ne 0 ]; then
201+
failed_count=$(python3 "{{ openstack_test_ote_resolve_script }}" count \
202+
"{{ openstack_test_log_path }}" failed || echo 0)
203+
if [ "$py_exit" -ne 0 ] || [ "${failed_count:-0}" -gt 0 ]; then
203204
exit 1
204205
fi
205206
exit 0

‎jobs_definitions/osp_verification_4.21_nightly.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
openshift_release: "4.21"
1414
openshift_build_name: "" # Empty resolves to latest nightly via 4.21.0-0.nightly/latest
1515
installation_type: ipi
16+
lb_tests_guest_cleanup: true
1617
stages:
1718
- prepare
1819
- install

‎jobs_definitions/osp_verification_4.22_nightly.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
openshift_release: "4.22"
1414
openshift_build_name: "" # Empty resolves to latest nightly via 4.22.0-0.nightly/latest
1515
installation_type: ipi
16+
lb_tests_guest_cleanup: true
1617
stages:
1718
- prepare
1819
- install

‎jobs_definitions/osp_verification_4.23_nightly.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
openshift_release: "4.23"
1414
openshift_build_name: "" # Empty resolves to latest nightly via 4.23.0-0.nightly/latest
1515
installation_type: ipi
16+
lb_tests_guest_cleanup: true
1617
stages:
1718
- prepare
1819
- install

‎jobs_definitions/osp_verification_5.0_nightly.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
openshift_release: "5.0"
1717
openshift_build_name: "" # Empty resolves to latest nightly via 5.0.0-0.nightly/latest
1818
installation_type: ipi
19+
lb_tests_guest_cleanup: true
1920
stages:
2021
- prepare
2122
- install

0 commit comments

Comments
 (0)