Skip to content

Commit 828c5cd

Browse files
dlaw4608claude
andcommitted
Add Availability Zones (AZ) scenario support
Port the Nova + Cinder Availability Zones scenario from the legacy openshift-ir-plugin / rhos-infrared automation into the shiftstack-qa stages. The AZ job installs OCP IPI on OpenStack with one Nova aggregate per compute host (one AZ each), OCP masters/workers distributed evenly across the AZs, and rootVolumes placed on per-AZ Cinder backends. Post- install verification asserts the OCP nodes and their root volumes landed in the expected Nova and Cinder AZs. Stages/roles: - prepare: configure_az.yml creates host aggregates + per-AZ volume types, configure_az_ceph_crush.yml pins each per-AZ pool to its host-local OSDs. - install: discovers Nova/Cinder AZs (tools_cluster_checks/discover_az.yml) and renders zones + per-AZ rootVolume types/zones into install-config. - verification: check_azs.yml verifies Nova and Cinder AZ placement. - day2ops: scaleup_worker and cinder_registry (AZ-aware registry storage). - cleanup: remove_az.yml tears down aggregates and AZ volume types, gated by az_remove_on_cleanup. - jobs_definitions/availability_zones_4.22_nightly.yaml wires it together. All AZ behaviour is gated behind az_enable / openshift_root_volumes (default false), so non-AZ jobs are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Change-Id: I45bdf61b3bc8e270c950e2e07590a736a9cd81ce Signed-off-by: Daniel Lawton <dlawton@redhat.com>
1 parent f8bd928 commit 828c5cd

16 files changed

Lines changed: 778 additions & 2 deletions

File tree

collection/stages/roles/cleanup/defaults/main.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
# Set the OpenShift installation type to None
55
existing_ocp_installation_type: ""
66
force_cleanup: false
7+
az_remove_on_cleanup: false
8+
az_volume_type_prefix: "fastpool-"
9+
az_volume_type_count: 3

collection/stages/roles/cleanup/tasks/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@
6868
- name: Discover the OpenShift installation type
6969
ansible.builtin.include_tasks: detect_ocp_installation.yml
7070

71+
- name: Remove availability zone configuration from OpenStack
72+
ansible.builtin.include_tasks: remove_az.yml
73+
when: az_remove_on_cleanup | default(false) | bool
74+
7175
- name: Cleanup IPv6 secondary network resources if they exist
7276
ansible.builtin.include_tasks: cleanup_ipv6_secondary.yml
7377
when: ocp_deployment_topology.secondary_ip_protocol | default('') == 'ipv6'
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
- name: Discover existing host aggregates
3+
ansible.builtin.command:
4+
cmd: openstack aggregate list -c Name -f value
5+
environment:
6+
OS_CLOUD: "{{ admin_cloud }}"
7+
register: az_existing_aggregates
8+
changed_when: false
9+
10+
- name: Remove hosts from existing aggregates
11+
openstack.cloud.host_aggregate:
12+
cloud: "{{ admin_cloud }}"
13+
state: present
14+
name: "{{ item }}"
15+
hosts: []
16+
loop: "{{ az_existing_aggregates.stdout_lines }}"
17+
when: az_existing_aggregates.stdout_lines | length > 0
18+
19+
- name: Delete existing host aggregates
20+
openstack.cloud.host_aggregate:
21+
cloud: "{{ admin_cloud }}"
22+
state: absent
23+
name: "{{ item }}"
24+
loop: "{{ az_existing_aggregates.stdout_lines }}"
25+
when: az_existing_aggregates.stdout_lines | length > 0
26+
27+
- name: Show nova host AZ mapping after aggregate cleanup
28+
ansible.builtin.command:
29+
cmd: openstack host list -c Zone -c "Host Name" -f table
30+
environment:
31+
OS_CLOUD: "{{ admin_cloud }}"
32+
register: az_host_list_after_cleanup
33+
changed_when: false
34+
35+
- name: Print nova host AZ mapping after aggregate cleanup
36+
ansible.builtin.debug:
37+
var: az_host_list_after_cleanup.stdout_lines
38+
39+
- name: Delete AZ volume types
40+
ansible.builtin.command:
41+
cmd: openstack volume type delete {{ az_volume_type_prefix }}{{ item }}
42+
environment:
43+
OS_CLOUD: "{{ admin_cloud }}"
44+
loop: "{{ range(0, az_volume_type_count | int) | list }}"
45+
failed_when: false
46+
changed_when: true

collection/stages/roles/day2ops/defaults/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# defaults file for day2ops
33
day2ops_steps: []
44
day2ops_report_filename: shiftstack-qa-day2ops-results.xml
5+
registry_test_project: registry-test
56

67
# Application Credentials rotation
78
app_credential_name: "AppCreds-{{ user_cloud }}"
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
---
2+
- name: Get existing workers
3+
kubernetes.core.k8s_info:
4+
kubeconfig: "{{ kubeconfig }}"
5+
api_version: v1
6+
kind: Node
7+
label_selectors:
8+
- node-role.kubernetes.io/worker
9+
register: workers
10+
11+
- name: Get first worker name and its Cinder zone label
12+
ansible.builtin.set_fact:
13+
worker_name: "{{ workers.resources[0].metadata.labels['kubernetes.io/hostname'] }}"
14+
nova_az_for_registry: "{{ workers.resources[0].metadata.labels['topology.cinder.csi.openstack.org/zone'] }}"
15+
16+
- name: Discover Cinder AZ for the worker root volume
17+
block:
18+
- name: Query worker root volume AZ
19+
ansible.builtin.shell: |
20+
set -o pipefail
21+
openstack volume show \
22+
"$(openstack volume list -c ID -c Name -f value | grep "{{ worker_name }}" | cut -d' ' -f2)" \
23+
-c availability_zone -f value
24+
environment:
25+
OS_CLOUD: "{{ user_cloud }}"
26+
register: registry_worker_volume_az
27+
failed_when: false
28+
changed_when: false
29+
30+
- name: Set Cinder AZ for registry PVC
31+
ansible.builtin.set_fact:
32+
cinder_az_for_registry: "{{ (registry_worker_volume_az.stdout | trim == '') | ternary('nova', registry_worker_volume_az.stdout | trim) }}"
33+
34+
- name: Remove existing topology-aware storage class
35+
kubernetes.core.k8s:
36+
kubeconfig: "{{ kubeconfig }}"
37+
state: absent
38+
api_version: storage.k8s.io/v1
39+
kind: StorageClass
40+
name: topology-aware-image-registry
41+
42+
- name: Create a storage class for integrated registry
43+
kubernetes.core.k8s:
44+
kubeconfig: "{{ kubeconfig }}"
45+
state: present
46+
definition:
47+
apiVersion: storage.k8s.io/v1
48+
kind: StorageClass
49+
metadata:
50+
name: topology-aware-image-registry
51+
provisioner: cinder.csi.openstack.org
52+
parameters:
53+
availability: "{{ cinder_az_for_registry }}"
54+
volumeBindingMode: WaitForFirstConsumer
55+
56+
- name: Create PVC for integrated registry
57+
kubernetes.core.k8s:
58+
kubeconfig: "{{ kubeconfig }}"
59+
state: present
60+
definition:
61+
apiVersion: v1
62+
kind: PersistentVolumeClaim
63+
metadata:
64+
name: pvc-registry
65+
namespace: openshift-image-registry
66+
annotations:
67+
imageregistry.openshift.io: "true"
68+
spec:
69+
accessModes:
70+
- ReadWriteOnce
71+
resources:
72+
requests:
73+
storage: 1Gi
74+
storageClassName: topology-aware-image-registry
75+
76+
- name: Trigger registry reconfiguration
77+
kubernetes.core.k8s:
78+
kubeconfig: "{{ kubeconfig }}"
79+
definition:
80+
apiVersion: imageregistry.operator.openshift.io/v1
81+
kind: Config
82+
metadata:
83+
name: cluster
84+
spec:
85+
defaultRoute: true
86+
disableRedirect: false
87+
rolloutStrategy: Recreate
88+
replicas: 1
89+
storage:
90+
managementState: Managed
91+
pvc:
92+
claim: pvc-registry
93+
swift: null
94+
status: null
95+
state: present
96+
97+
- name: Wait for registry pod on Running phase
98+
kubernetes.core.k8s_info:
99+
kubeconfig: "{{ kubeconfig }}"
100+
kind: Pod
101+
namespace: openshift-image-registry
102+
label_selectors:
103+
- docker-registry=default
104+
register: registry_pod
105+
retries: 60
106+
delay: 10
107+
until:
108+
- registry_pod.resources[0].spec.volumes[0].persistentVolumeClaim is defined
109+
- registry_pod.resources[0].spec.volumes[0].persistentVolumeClaim.claimName == "pvc-registry"
110+
- registry_pod.resources[0].status is defined
111+
- registry_pod.resources[0].status.phase == "Running"
112+
113+
- name: Get integrated registry route
114+
kubernetes.core.k8s_info:
115+
kubeconfig: "{{ kubeconfig }}"
116+
api_version: route.openshift.io/v1
117+
kind: Route
118+
namespace: openshift-image-registry
119+
name: default-route
120+
register: registry_routes
121+
122+
- name: Set integrated registry URL
123+
ansible.builtin.set_fact:
124+
registry_url: "{{ registry_routes.resources[0].spec.host }}"
125+
126+
- name: Add registry domain resolution to /etc/hosts
127+
block:
128+
- name: Resolve apps ingress IP from /etc/hosts
129+
ansible.builtin.shell: |
130+
set -o pipefail
131+
awk '{if ($2 ~ "apps.{{ ocp_cluster_name }}") print $1}' /etc/hosts | uniq
132+
register: hosts_apps_ip
133+
changed_when: false
134+
135+
- name: Set apps ingress IP
136+
ansible.builtin.set_fact:
137+
apps_fip_ip: "{{ hosts_apps_ip.stdout }}"
138+
139+
- name: Add registry floating IP to /etc/hosts
140+
ansible.builtin.lineinfile:
141+
path: /etc/hosts
142+
line: "{{ apps_fip_ip }} {{ registry_url }}"
143+
become: true
144+
145+
- name: Delete the registry test project if it exists
146+
kubernetes.core.k8s:
147+
kubeconfig: "{{ kubeconfig }}"
148+
state: absent
149+
api_version: project.openshift.io/v1
150+
kind: Project
151+
name: "{{ registry_test_project }}"
152+
wait: true
153+
wait_timeout: 60
154+
155+
- name: Create the registry test project
156+
kubernetes.core.k8s:
157+
kubeconfig: "{{ kubeconfig }}"
158+
state: present
159+
api_version: project.openshift.io/v1
160+
kind: Project
161+
name: "{{ registry_test_project }}"
162+
163+
- name: Add registry permissions to admin user
164+
ansible.builtin.shell: |
165+
set -o pipefail
166+
oc project {{ registry_test_project }}
167+
oc policy add-role-to-user registry-viewer admin
168+
oc policy add-role-to-user registry-editor admin
169+
environment:
170+
KUBECONFIG: "{{ kubeconfig }}"
171+
changed_when: true
172+
173+
- name: Get builder service account token for OCP before 4.11
174+
when: openshift_release_build_name is version('4.11', '<')
175+
block:
176+
- name: Get builder token
177+
ansible.builtin.command: oc serviceaccounts get-token builder
178+
environment:
179+
KUBECONFIG: "{{ kubeconfig }}"
180+
register: registry_token_result
181+
retries: 5
182+
delay: 60
183+
until: registry_token_result is succeeded
184+
changed_when: false
185+
186+
- name: Set registry token
187+
ansible.builtin.set_fact:
188+
registry_token: "{{ registry_token_result.stdout }}"
189+
190+
- name: Get builder service account token for OCP 4.11 and later
191+
when: openshift_release_build_name is version('4.11', '>=')
192+
block:
193+
- name: Create builder token
194+
ansible.builtin.command: oc create token builder
195+
environment:
196+
KUBECONFIG: "{{ kubeconfig }}"
197+
register: registry_token_result
198+
retries: 5
199+
delay: 60
200+
until: registry_token_result is succeeded
201+
changed_when: false
202+
203+
- name: Set registry token
204+
ansible.builtin.set_fact:
205+
registry_token: "{{ registry_token_result.stdout }}"
206+
207+
- name: Push demo image to integrated registry
208+
ansible.builtin.shell: |
209+
set -o pipefail
210+
sudo podman login -u admin -p {{ registry_token }} --tls-verify=false {{ registry_url }} && \
211+
sudo podman pull quay.io/kuryr/demo && \
212+
sudo podman tag quay.io/kuryr/demo {{ registry_url }}/{{ registry_test_project }}/demo && \
213+
sudo podman push {{ registry_url }}/{{ registry_test_project }}/demo --tls-verify=false
214+
register: registry_push_result
215+
retries: 10
216+
delay: 30
217+
until: not registry_push_result.failed
218+
changed_when: true
219+
220+
- name: Create a pod using integrated registry
221+
kubernetes.core.k8s:
222+
kubeconfig: "{{ kubeconfig }}"
223+
state: present
224+
definition:
225+
apiVersion: apps/v1
226+
kind: Deployment
227+
metadata:
228+
name: demo
229+
namespace: "{{ registry_test_project }}"
230+
spec:
231+
replicas: 1
232+
selector:
233+
matchLabels:
234+
app: demo
235+
template:
236+
metadata:
237+
labels:
238+
app: demo
239+
spec:
240+
containers:
241+
- name: demo
242+
image: image-registry.openshift-image-registry.svc:5000/{{ registry_test_project }}/demo
243+
ports:
244+
- containerPort: 80
245+
246+
- name: Wait for demo pod to reach Running phase
247+
kubernetes.core.k8s_info:
248+
kubeconfig: "{{ kubeconfig }}"
249+
kind: Pod
250+
label_selectors:
251+
- app=demo
252+
register: demo_pod
253+
retries: 5
254+
delay: 10
255+
until:
256+
- demo_pod.resources[0].status is defined
257+
- demo_pod.resources[0].status.phase == "Running"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
- name: Get the worker machineset
3+
kubernetes.core.k8s_info:
4+
kubeconfig: "{{ kubeconfig }}"
5+
api_version: machine.openshift.io/v1beta1
6+
kind: MachineSet
7+
namespace: openshift-machine-api
8+
label_selectors:
9+
- machine.openshift.io/cluster-api-machine-type=worker
10+
register: ms_worker
11+
12+
- name: Set the expected workers replicas
13+
ansible.builtin.set_fact:
14+
worker_spec_replicas: "{{ ms_worker.resources[0].spec.replicas }}"
15+
16+
- name: Check that the number of worker replicas are as expected
17+
kubernetes.core.k8s_info:
18+
kubeconfig: "{{ kubeconfig }}"
19+
api_version: machine.openshift.io/v1beta1
20+
kind: MachineSet
21+
namespace: openshift-machine-api
22+
name: "{{ ms_worker.resources[0].metadata.name }}"
23+
register: workers
24+
until: workers | json_query('resources[0].status.readyReplicas') == worker_spec_replicas | int
25+
retries: 40
26+
delay: 30
27+
28+
- name: Set the number of new workers
29+
ansible.builtin.set_fact:
30+
new_num_workers: "{{ ms_worker.resources[0].spec.replicas | int + 1 }}"
31+
32+
- name: Scale workers
33+
ansible.builtin.command:
34+
cmd: >-
35+
oc scale --replicas={{ new_num_workers }}
36+
machineset/{{ ms_worker.resources[0].metadata.name }}
37+
-n openshift-machine-api
38+
environment:
39+
KUBECONFIG: "{{ kubeconfig }}"
40+
changed_when: true
41+
42+
- name: Wait for all the workers to be up
43+
kubernetes.core.k8s_info:
44+
kubeconfig: "{{ kubeconfig }}"
45+
api_version: machine.openshift.io/v1beta1
46+
kind: MachineSet
47+
namespace: openshift-machine-api
48+
name: "{{ ms_worker.resources[0].metadata.name }}"
49+
register: new_workers
50+
until: new_workers | json_query('resources[0].status.readyReplicas') | int == new_num_workers | int
51+
retries: 40
52+
delay: 30

collection/stages/roles/install/tasks/main.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
state: directory
2323
mode: u=rwx,g=rw,o=r
2424

25+
- name: Discover Nova and Cinder AZs for install-config generation
26+
ansible.builtin.include_role:
27+
name: tools_cluster_checks
28+
tasks_from: discover_az.yml
29+
when: az_enable | default(false) | bool
30+
2531
- name: Prepare for UPI installation
2632
ansible.builtin.include_role:
2733
name: tools_upi

0 commit comments

Comments
 (0)